[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/WebAsset/ -> WebAssetRegistry.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\WebAsset;
  11  
  12  use Joomla\CMS\Event\AbstractEvent;
  13  use Joomla\CMS\Filesystem\Path;
  14  use Joomla\CMS\WebAsset\Exception\UnknownAssetException;
  15  use Joomla\Event\Dispatcher as EventDispatcher;
  16  use Joomla\Event\DispatcherAwareInterface;
  17  use Joomla\Event\DispatcherAwareTrait;
  18  
  19  // phpcs:disable PSR1.Files.SideEffects
  20  \defined('JPATH_PLATFORM') or die;
  21  // phpcs:enable PSR1.Files.SideEffects
  22  
  23  /**
  24   * Web Asset Registry class
  25   *
  26   * @since  4.0.0
  27   */
  28  class WebAssetRegistry implements WebAssetRegistryInterface, DispatcherAwareInterface
  29  {
  30      use DispatcherAwareTrait;
  31  
  32      /**
  33       * Files with Asset info. File path should be relative.
  34       *
  35       * @var    array
  36       * @example of registry file:
  37       *
  38       * {
  39       *      "title" : "Example",
  40       *      "name"  : "com_example",
  41       *      "author": "Joomla! CMS",
  42       *      "assets": [
  43       *          {
  44       *              "name": "library1",
  45       *              "version": "3.5.0",
  46       *              "type":  "script",
  47       *              "uri": "com_example/library1.min.js"
  48       *          },
  49       *          {
  50       *              "name": "library2",
  51       *              "version": "3.5.0",
  52       *              "type":  "script",
  53       *              "uri": "com_example/library2.min.js",
  54       *              "dependencies": [
  55       *                  "core",
  56       *                  "library1"
  57       *              ],
  58       *              "attribute": {
  59       *                  "attr-name": "attr value"
  60       *                  "defer": true
  61       *              }
  62       *          },
  63       *          {
  64       *              "name": "library1",
  65       *              "version": "3.5.0",
  66       *              "type":  "style",
  67       *              "uri": "com_example/library1.min.css"
  68       *              "attribute": {
  69       *                  "media": "all"
  70       *              }
  71       *          },
  72       *          {
  73       *              "name": "library1",
  74       *              "type":  "preset",
  75       *              "dependencies": {
  76       *                  "library1#style",
  77       *                  "library1#script"
  78       *              }
  79       *          },
  80       *      ]
  81       *  }
  82       *
  83       * @since  4.0.0
  84       */
  85      protected $dataFilesNew = [];
  86  
  87      /**
  88       * List of parsed files
  89       *
  90       * @var array
  91       *
  92       * @since  4.0.0
  93       */
  94      protected $dataFilesParsed = [];
  95  
  96      /**
  97       * Registry of available Assets
  98       *
  99       * @var array
 100       *
 101       * @since  4.0.0
 102       */
 103      protected $assets = [];
 104  
 105      /**
 106       * Registry constructor
 107       *
 108       * @since  4.0.0
 109       */
 110      public function __construct()
 111      {
 112          // Use a dedicated dispatcher
 113          $this->setDispatcher(new EventDispatcher());
 114      }
 115  
 116      /**
 117       * Get an existing Asset from a registry, by asset name.
 118       *
 119       * @param   string  $type  Asset type, script or style
 120       * @param   string  $name  Asset name
 121       *
 122       * @return  WebAssetItem
 123       *
 124       * @throws  UnknownAssetException  When Asset cannot be found
 125       *
 126       * @since   4.0.0
 127       */
 128      public function get(string $type, string $name): WebAssetItemInterface
 129      {
 130          // Check if any new file was added
 131          $this->parseRegistryFiles();
 132  
 133          if (empty($this->assets[$type][$name])) {
 134              throw new UnknownAssetException(sprintf('There is no "%s" asset of a "%s" type in the registry.', $name, $type));
 135          }
 136  
 137          return $this->assets[$type][$name];
 138      }
 139  
 140      /**
 141       * Add Asset to registry of known assets
 142       *
 143       * @param   string                 $type   Asset type, script or style
 144       * @param   WebAssetItemInterface  $asset  Asset instance
 145       *
 146       * @return  self
 147       *
 148       * @since   4.0.0
 149       */
 150      public function add(string $type, WebAssetItemInterface $asset): WebAssetRegistryInterface
 151      {
 152          $type = strtolower($type);
 153  
 154          if (!array_key_exists($type, $this->assets)) {
 155              $this->assets[$type] = [];
 156          }
 157  
 158          $eventChange = 'new';
 159          $eventAsset  = $asset;
 160  
 161          // Use "old" asset for "Changed" event, a "new" asset can be loaded by a name from the registry
 162          if (!empty($this->assets[$type][$asset->getName()])) {
 163              $eventChange = 'override';
 164              $eventAsset  = $this->assets[$type][$asset->getName()];
 165          }
 166  
 167          $this->assets[$type][$asset->getName()] = $asset;
 168  
 169          $this->dispatchAssetChanged($type, $eventAsset, $eventChange);
 170  
 171          return $this;
 172      }
 173  
 174      /**
 175       * Remove Asset from registry.
 176       *
 177       * @param   string  $type  Asset type, script or style
 178       * @param   string  $name  Asset name
 179       *
 180       * @return  self
 181       *
 182       * @since   4.0.0
 183       */
 184      public function remove(string $type, string $name): WebAssetRegistryInterface
 185      {
 186          if (!empty($this->assets[$type][$name])) {
 187              $asset = $this->assets[$type][$name];
 188  
 189              unset($this->assets[$type][$name]);
 190  
 191              $this->dispatchAssetChanged($type, $asset, 'remove');
 192          }
 193  
 194          return $this;
 195      }
 196  
 197      /**
 198       * Check whether the asset exists in the registry.
 199       *
 200       * @param   string  $type  Asset type, script or style
 201       * @param   string  $name  Asset name
 202       *
 203       * @return  boolean
 204       *
 205       * @since   4.0.0
 206       */
 207      public function exists(string $type, string $name): bool
 208      {
 209          return !empty($this->assets[$type][$name]);
 210      }
 211  
 212      /**
 213       * Prepare new Asset instance.
 214       *
 215       * @param   string  $name          The asset name
 216       * @param   string  $uri           The URI for the asset
 217       * @param   array   $options       Additional options for the asset
 218       * @param   array   $attributes    Attributes for the asset
 219       * @param   array   $dependencies  Asset dependencies
 220       *
 221       * @return  WebAssetItem
 222       *
 223       * @since   4.0.0
 224       */
 225      public function createAsset(
 226          string $name,
 227          string $uri = null,
 228          array $options = [],
 229          array $attributes = [],
 230          array $dependencies = []
 231      ): WebAssetItem {
 232          $nameSpace = \array_key_exists('namespace', $options) ? $options['namespace'] : __NAMESPACE__ . '\\AssetItem';
 233          $className = \array_key_exists('class', $options) ? $options['class'] : null;
 234  
 235          if ($className && class_exists($nameSpace . '\\' . $className)) {
 236              $className = $nameSpace . '\\' . $className;
 237  
 238              return new $className($name, $uri, $options, $attributes, $dependencies);
 239          }
 240  
 241          return new WebAssetItem($name, $uri, $options, $attributes, $dependencies);
 242      }
 243  
 244      /**
 245       * Register new file with Asset(s) info
 246       *
 247       * @param   string  $path  Relative path
 248       *
 249       * @return  self
 250       *
 251       * @since  4.0.0
 252       */
 253      public function addRegistryFile(string $path): self
 254      {
 255          $path = Path::clean($path);
 256  
 257          if (isset($this->dataFilesNew[$path]) || isset($this->dataFilesParsed[$path])) {
 258              return $this;
 259          }
 260  
 261          if (is_file(JPATH_ROOT . '/' . $path)) {
 262              $this->dataFilesNew[$path] = $path;
 263          }
 264  
 265          return $this;
 266      }
 267  
 268      /**
 269       * Get a list of the registry files
 270       *
 271       * @return  array
 272       *
 273       * @since  4.0.0
 274       */
 275      public function getRegistryFiles(): array
 276      {
 277          return array_values($this->dataFilesParsed + $this->dataFilesNew);
 278      }
 279  
 280      /**
 281       * Helper method to register new file with Template Asset(s) info
 282       *
 283       * @param   string   $template  The template name
 284       * @param   integer  $client    The application client id
 285       *
 286       * @return  self
 287       *
 288       * @since  4.0.0
 289       */
 290      public function addTemplateRegistryFile(string $template, int $client): self
 291      {
 292          switch ($client) {
 293              case 0:
 294                  $this->addRegistryFile('templates/' . $template . '/joomla.asset.json');
 295                  break;
 296              case 1:
 297                  $this->addRegistryFile('administrator/templates/' . $template . '/joomla.asset.json');
 298                  break;
 299              default:
 300                  break;
 301          }
 302  
 303          return $this;
 304      }
 305  
 306      /**
 307       * Helper method to register new file with Extension Asset(s) info
 308       *
 309       * @param   string  $name  A full extension name, actually a name in the /media folder, eg: com_example, plg_system_example etc.
 310       *
 311       * @return  self
 312       *
 313       * @since  4.0.0
 314       */
 315      public function addExtensionRegistryFile(string $name): self
 316      {
 317          $this->addRegistryFile('media/' . $name . '/joomla.asset.json');
 318  
 319          return $this;
 320      }
 321  
 322      /**
 323       * Parse registered files
 324       *
 325       * @return  void
 326       *
 327       * @since  4.0.0
 328       */
 329      protected function parseRegistryFiles()
 330      {
 331          if (!$this->dataFilesNew) {
 332              return;
 333          }
 334  
 335          foreach ($this->dataFilesNew as $path) {
 336              // Parse only if the file was not parsed already
 337              if (empty($this->dataFilesParsed[$path])) {
 338                  $this->parseRegistryFile($path);
 339  
 340                  // Mark the file as parsed
 341                  $this->dataFilesParsed[$path] = $path;
 342              }
 343  
 344              // Remove the file from queue
 345              unset($this->dataFilesNew[$path]);
 346          }
 347      }
 348  
 349      /**
 350       * Parse registry file
 351       *
 352       * @param   string  $path  Relative path to the data file
 353       *
 354       * @return  void
 355       *
 356       * @throws  \RuntimeException If file is empty or invalid
 357       *
 358       * @since   4.0.0
 359       */
 360      protected function parseRegistryFile($path)
 361      {
 362          $data = file_get_contents(JPATH_ROOT . '/' . $path);
 363          $data = $data ? json_decode($data, true) : null;
 364  
 365          if ($data === null) {
 366              throw new \RuntimeException(sprintf('Asset registry file "%s" contains invalid JSON', $path));
 367          }
 368  
 369          // Check if asset field exists and contains data. If it doesn't - we can just bail here.
 370          if (empty($data['assets'])) {
 371              return;
 372          }
 373  
 374          // Keep source info
 375          $assetSource = [
 376              'registryFile' => $path,
 377          ];
 378  
 379          $namespace = \array_key_exists('namespace', $data) ? $data['namespace'] : null;
 380  
 381          // Prepare WebAssetItem instances
 382          foreach ($data['assets'] as $i => $item) {
 383              if (empty($item['name'])) {
 384                  throw new \RuntimeException(
 385                      sprintf('Failed parsing asset registry file "%s". Property "name" is required for asset index "%s"', $path, $i)
 386                  );
 387              }
 388  
 389              if (empty($item['type'])) {
 390                  throw new \RuntimeException(
 391                      sprintf('Failed parsing asset registry file "%s". Property "type" is required for asset "%s"', $path, $item['name'])
 392                  );
 393              }
 394  
 395              $item['type'] = strtolower($item['type']);
 396  
 397              $name    = $item['name'];
 398              $uri     = $item['uri'] ?? '';
 399              $options = $item;
 400              $options['assetSource'] = $assetSource;
 401  
 402              unset($options['uri'], $options['name']);
 403  
 404              // Inheriting the Namespace
 405              if ($namespace && !\array_key_exists('namespace', $options)) {
 406                  $options['namespace'] = $namespace;
 407              }
 408  
 409              $assetItem = $this->createAsset($name, $uri, $options);
 410              $this->add($item['type'], $assetItem);
 411          }
 412      }
 413  
 414      /**
 415       * Dispatch an event to notify listeners about asset changes: new, remove, override
 416       * Events:
 417       *  - onWebAssetRegistryChangedAssetNew       When new asset added to the registry
 418       *  - onWebAssetRegistryChangedAssetOverride  When the asset overridden
 419       *  - onWebAssetRegistryChangedAssetRemove    When new asset was removed from the registry
 420       *
 421       * @param   string                 $type    Asset type, script or style
 422       * @param   WebAssetItemInterface  $asset   Asset instance
 423       * @param   string                 $change  A type of change: new, remove, override
 424       *
 425       * @return  void
 426       *
 427       * @since  4.0.0
 428       */
 429      protected function dispatchAssetChanged(string $type, WebAssetItemInterface $asset, string $change)
 430      {
 431          // Trigger the event
 432          $event = AbstractEvent::create(
 433              'onWebAssetRegistryChangedAsset' . ucfirst($change),
 434              [
 435                  'eventClass' => 'Joomla\\CMS\\Event\\WebAsset\\WebAssetRegistryAssetChanged',
 436                  'subject'    => $this,
 437                  'assetType'  => $type,
 438                  'asset'      => $asset,
 439                  'change'     => $change,
 440              ]
 441          );
 442  
 443          $this->getDispatcher()->dispatch($event->getName(), $event);
 444      }
 445  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer