Spade
Mini Shell
| Directory:~$ /proc/self/root/home/lmsyaran/www/joomla5/libraries/src/WebAsset/ |
| [Home] [System Details] [Kill Me] |
<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
namespace Joomla\CMS\WebAsset;
use Joomla\CMS\Event\AbstractEvent;
use Joomla\CMS\Event\WebAsset\WebAssetRegistryAssetChanged;
use Joomla\CMS\WebAsset\Exception\UnknownAssetException;
use Joomla\Event\Dispatcher as EventDispatcher;
use Joomla\Event\DispatcherAwareInterface;
use Joomla\Event\DispatcherAwareTrait;
use Joomla\Filesystem\Path;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Web Asset Registry class
*
* @since 4.0.0
*/
class WebAssetRegistry implements WebAssetRegistryInterface,
DispatcherAwareInterface
{
use DispatcherAwareTrait;
/**
* Files with Asset info. File path should be relative.
*
* @var array
* @example of registry file:
*
* {
* "title" : "Example",
* "name" : "com_example",
* "author": "Joomla! CMS",
* "assets": [
* {
* "name": "library1",
* "version": "3.5.0",
* "type": "script",
* "uri":
"com_example/library1.min.js"
* },
* {
* "name": "library2",
* "version": "3.5.0",
* "type": "script",
* "uri":
"com_example/library2.min.js",
* "dependencies": [
* "core",
* "library1"
* ],
* "attribute": {
* "attr-name": "attr value"
* "defer": true
* }
* },
* {
* "name": "library1",
* "version": "3.5.0",
* "type": "style",
* "uri":
"com_example/library1.min.css"
* "attribute": {
* "media": "all"
* }
* },
* {
* "name": "library1",
* "type": "preset",
* "dependencies": {
* "library1#style",
* "library1#script"
* }
* },
* ]
* }
*
* @since 4.0.0
*/
protected $dataFilesNew = [];
/**
* List of parsed files
*
* @var array
*
* @since 4.0.0
*/
protected $dataFilesParsed = [];
/**
* Registry of available Assets
*
* @var array
*
* @since 4.0.0
*/
protected $assets = [];
/**
* Registry constructor
*
* @since 4.0.0
*/
public function __construct()
{
// Use a dedicated dispatcher
$this->setDispatcher(new EventDispatcher());
}
/**
* Get an existing Asset from a registry, by asset name.
*
* @param string $type Asset type, script or style
* @param string $name Asset name
*
* @return WebAssetItem
*
* @throws UnknownAssetException When Asset cannot be found
*
* @since 4.0.0
*/
public function get(string $type, string $name): WebAssetItemInterface
{
// Check if any new file was added
$this->parseRegistryFiles();
if (empty($this->assets[$type][$name])) {
throw new UnknownAssetException(sprintf('There is no
"%s" asset of a "%s" type in the registry.',
$name, $type));
}
return $this->assets[$type][$name];
}
/**
* Add Asset to registry of known assets
*
* @param string $type Asset type, script or style
* @param WebAssetItemInterface $asset Asset instance
*
* @return self
*
* @since 4.0.0
*/
public function add(string $type, WebAssetItemInterface $asset):
WebAssetRegistryInterface
{
$type = strtolower($type);
if (!\array_key_exists($type, $this->assets)) {
$this->assets[$type] = [];
}
// Check if any new file was added
$this->parseRegistryFiles();
$eventChange = 'new';
$eventAsset = $asset;
// Use "old" asset for "Changed" event, a
"new" asset can be loaded by a name from the registry
if (!empty($this->assets[$type][$asset->getName()])) {
$eventChange = 'override';
$eventAsset = $this->assets[$type][$asset->getName()];
}
$this->assets[$type][$asset->getName()] = $asset;
$this->dispatchAssetChanged($type, $eventAsset, $eventChange);
return $this;
}
/**
* Remove Asset from registry.
*
* @param string $type Asset type, script or style
* @param string $name Asset name
*
* @return self
*
* @since 4.0.0
*/
public function remove(string $type, string $name):
WebAssetRegistryInterface
{
// Check if any new file was added
$this->parseRegistryFiles();
if (!empty($this->assets[$type][$name])) {
$asset = $this->assets[$type][$name];
unset($this->assets[$type][$name]);
$this->dispatchAssetChanged($type, $asset,
'remove');
}
return $this;
}
/**
* Check whether the asset exists in the registry.
*
* @param string $type Asset type, script or style
* @param string $name Asset name
*
* @return boolean
*
* @since 4.0.0
*/
public function exists(string $type, string $name): bool
{
// Check if any new file was added
$this->parseRegistryFiles();
return !empty($this->assets[$type][$name]);
}
/**
* Prepare new Asset instance.
*
* @param string $name The asset name
* @param string $uri The URI for the asset
* @param array $options Additional options for the asset
* @param array $attributes Attributes for the asset
* @param array $dependencies Asset dependencies
*
* @return WebAssetItem
*
* @since 4.0.0
*/
public function createAsset(
string $name,
string $uri = null,
array $options = [],
array $attributes = [],
array $dependencies = []
): WebAssetItem {
$nameSpace = \array_key_exists('namespace', $options) ?
$options['namespace'] : __NAMESPACE__ . '\\AssetItem';
$className = \array_key_exists('class', $options) ?
$options['class'] : null;
if ($className && class_exists($nameSpace . '\\'
. $className)) {
$className = $nameSpace . '\\' . $className;
return new $className($name, $uri, $options, $attributes,
$dependencies);
}
return new WebAssetItem($name, $uri, $options, $attributes,
$dependencies);
}
/**
* Register new file with Asset(s) info
*
* @param string $path Relative path
*
* @return self
*
* @since 4.0.0
*/
public function addRegistryFile(string $path): self
{
$path = Path::clean($path);
if (isset($this->dataFilesNew[$path]) ||
isset($this->dataFilesParsed[$path])) {
return $this;
}
if (is_file(JPATH_ROOT . '/' . $path)) {
$this->dataFilesNew[$path] = $path;
}
return $this;
}
/**
* Get a list of the registry files
*
* @return array
*
* @since 4.0.0
*/
public function getRegistryFiles(): array
{
return array_values($this->dataFilesParsed +
$this->dataFilesNew);
}
/**
* Helper method to register new file with Template Asset(s) info
*
* @param string $template The template name
* @param integer $client The application client id
*
* @return self
*
* @since 4.0.0
*/
public function addTemplateRegistryFile(string $template, int $client):
self
{
switch ($client) {
case 0:
$this->addRegistryFile('templates/' .
$template . '/joomla.asset.json');
break;
case 1:
$this->addRegistryFile('administrator/templates/' . $template
. '/joomla.asset.json');
break;
default:
break;
}
return $this;
}
/**
* Helper method to register new file with Extension Asset(s) info
*
* @param string $name A full extension name, actually a name in
the /media folder, eg: com_example, plg_system_example etc.
*
* @return self
*
* @since 4.0.0
*/
public function addExtensionRegistryFile(string $name): self
{
$this->addRegistryFile('media/' . $name .
'/joomla.asset.json');
return $this;
}
/**
* Parse registered files
*
* @return void
*
* @since 4.0.0
*/
protected function parseRegistryFiles()
{
if (!$this->dataFilesNew) {
return;
}
$paths = $this->dataFilesNew;
$this->dataFilesNew = [];
foreach ($paths as $path) {
// Parse only if the file was not parsed already
if (empty($this->dataFilesParsed[$path])) {
$this->parseRegistryFile($path);
// Mark the file as parsed
$this->dataFilesParsed[$path] = $path;
}
}
}
/**
* Parse registry file
*
* @param string $path Relative path to the data file
*
* @return void
*
* @throws \RuntimeException If file is empty or invalid
*
* @since 4.0.0
*/
protected function parseRegistryFile($path)
{
$data = file_get_contents(JPATH_ROOT . '/' . $path);
$data = $data ? json_decode($data, true) : null;
if ($data === null) {
throw new \RuntimeException(sprintf('Asset registry file
"%s" contains invalid JSON', $path));
}
// Check if asset field exists and contains data. If it
doesn't - we can just bail here.
if (empty($data['assets'])) {
return;
}
// Keep source info
$assetSource = [
'registryFile' => $path,
];
$namespace = \array_key_exists('namespace', $data) ?
$data['namespace'] : null;
// Prepare WebAssetItem instances
foreach ($data['assets'] as $i => $item) {
if (empty($item['name'])) {
throw new \RuntimeException(
sprintf('Failed parsing asset registry file
"%s". Property "name" is required for asset index
"%s"', $path, $i)
);
}
if (empty($item['type'])) {
throw new \RuntimeException(
sprintf('Failed parsing asset registry file
"%s". Property "type" is required for asset
"%s"', $path, $item['name'])
);
}
$item['type'] = strtolower($item['type']);
$name = $item['name'];
$uri = $item['uri'] ??
'';
$options = $item;
$options['assetSource'] = $assetSource;
unset($options['uri'], $options['name']);
// Inheriting the Namespace
if ($namespace &&
!\array_key_exists('namespace', $options)) {
$options['namespace'] = $namespace;
}
$assetItem = $this->createAsset($name, $uri, $options);
$this->add($item['type'], $assetItem);
}
}
/**
* Dispatch an event to notify listeners about asset changes: new,
remove, override
* Events:
* - onWebAssetRegistryChangedAssetNew When new asset added to
the registry
* - onWebAssetRegistryChangedAssetOverride When the asset overridden
* - onWebAssetRegistryChangedAssetRemove When new asset was
removed from the registry
*
* @param string $type Asset type, script or style
* @param WebAssetItemInterface $asset Asset instance
* @param string $change A type of change: new,
remove, override
*
* @return void
*
* @since 4.0.0
*/
protected function dispatchAssetChanged(string $type,
WebAssetItemInterface $asset, string $change)
{
// Trigger the event
$event = AbstractEvent::create(
'onWebAssetRegistryChangedAsset' . ucfirst($change),
[
'eventClass' =>
WebAssetRegistryAssetChanged::class,
'subject' => $this,
'assetType' => $type,
'asset' => $asset,
'change' => $change,
]
);
$this->getDispatcher()->dispatch($event->getName(),
$event);
}
}