diff --git a/libraries.services.yml b/libraries.services.yml index a5925eb345690b60ae053337bca790acb06dc24d..de969d2e02577b037e5a95d9b535150d4e59849a 100644 --- a/libraries.services.yml +++ b/libraries.services.yml @@ -3,12 +3,16 @@ services: class: Drupal\libraries\ExternalLibrary\ExternalLibraryManager arguments: - '@libraries.registry' + - '@plugin.manager.libraries.locator' - '@libraries.extension_handler' - '@libraries.php_file_loader' libraries.registry: class: Drupal\libraries\ExternalLibrary\Registry\ExternalLibraryRegistry # @todo Reconsider the decision to go with JSON instead of YAML. arguments: ['@serialization.json'] + plugin.manager.libraries.locator: + class: Drupal\libraries\ExternalLibrary\Local\LocatorManager + parent: default_plugin_manager libraries.extension_handler: class: Drupal\libraries\Extension\ExtensionHandler arguments: ['%app.root', '@module_handler', '@theme_handler'] diff --git a/src/Annotation/Locator.php b/src/Annotation/Locator.php new file mode 100644 index 0000000000000000000000000000000000000000..4cada913a586116bb467deafa1c82a16da53222c --- /dev/null +++ b/src/Annotation/Locator.php @@ -0,0 +1,19 @@ +<?php + +/** + * @file + * Contains \Drupal\libraries\Annotation\Locator. + */ + +namespace Drupal\libraries\Annotation; + +use Drupal\Component\Annotation\PluginID; + +/** + * Provides an annotation class for locator plugins. + * + * @Annotation + */ +class Locator extends PluginID { + +} diff --git a/src/ExternalLibrary/Exception/LibraryNotInstalledException.php b/src/ExternalLibrary/Exception/LibraryNotInstalledException.php index 7fbefb63e9c25af923bf06a99cac0253b237d8b1..ea4486427a9a61ddb2c6f331d77a3c5ab3be659d 100644 --- a/src/ExternalLibrary/Exception/LibraryNotInstalledException.php +++ b/src/ExternalLibrary/Exception/LibraryNotInstalledException.php @@ -7,7 +7,7 @@ namespace Drupal\libraries\ExternalLibrary\Exception; -use Drupal\libraries\ExternalLibrary\LocalLibraryInterface; +use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface; use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorTrait; use Exception; @@ -21,7 +21,7 @@ class LibraryNotInstalledException extends \RuntimeException { /** * Constructs a library exception. * - * @param \Drupal\libraries\ExternalLibrary\LocalLibraryInterface $library + * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library * The library that is not installed. * @param string $message * (optional) The exception message. diff --git a/src/ExternalLibrary/ExternalLibraryManager.php b/src/ExternalLibrary/ExternalLibraryManager.php index 75f1c17b24c3200f73b95ae480f8941e840ed825..98663c00345005a6326d128df5257d2fbb15600d 100644 --- a/src/ExternalLibrary/ExternalLibraryManager.php +++ b/src/ExternalLibrary/ExternalLibraryManager.php @@ -24,6 +24,13 @@ class ExternalLibraryManager implements ExternalLibraryManagerInterface { */ protected $registry; + /** + * The library locator factory. + * + * @var \Drupal\Component\Plugin\Factory\FactoryInterface + */ + protected $locatorFactory; + /** * The extension handler. * @@ -43,6 +50,8 @@ class ExternalLibraryManager implements ExternalLibraryManagerInterface { * * @param \Drupal\libraries\ExternalLibrary\Registry\ExternalLibraryRegistryInterface $registry * The library registry. + * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory + * The library locator factory. * @param \Drupal\libraries\Extension\ExtensionHandlerInterface $extension_handler * The extension handler. * @param \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLoaderInterface $php_file_loader @@ -50,10 +59,12 @@ class ExternalLibraryManager implements ExternalLibraryManagerInterface { */ public function __construct( ExternalLibraryRegistryInterface $registry, + FactoryInterface $locator_factory, ExtensionHandlerInterface $extension_handler, PhpFileLoaderInterface $php_file_loader ) { $this->registry = $registry; + $this->locatorFactory = $locator_factory; $this->extensionHandler = $extension_handler; $this->phpFileLoader = $php_file_loader; } @@ -62,14 +73,16 @@ class ExternalLibraryManager implements ExternalLibraryManagerInterface { * {@inheritdoc} */ public function getRequiredLibraries() { + $libraries = []; foreach ($this->extensionHandler->getExtensions() as $extension) { foreach ($extension->getLibraryDependencies() as $library_id) { // Do not bother instantiating a library multiple times. if (!isset($libraries[$library_id])) { - yield $this->registry->getLibrary($library_id); + $libraries[$library_id] = $this->registry->getLibrary($library_id); } } } + return $libraries; } /** @@ -80,9 +93,11 @@ class ExternalLibraryManager implements ExternalLibraryManagerInterface { $library = $this->registry->getLibrary($id); // @todo Throw an exception instead of silently failing. if ($library instanceof PhpFileLibraryInterface) { - $path = $library->getLibraryPath(); + // @todo Somehow make it possible to not repeat this in case the library + // has already been located. + $library->getLocator($this->locatorFactory)->locate($library); foreach ($library->getPhpFiles() as $file) { - $this->phpFileLoader->load($path . '/' . $file); + $this->phpFileLoader->load($file); } } } diff --git a/src/ExternalLibrary/Local/LocalLibraryInterface.php b/src/ExternalLibrary/Local/LocalLibraryInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..3d4107f5c7c783b42c4d9b7bb888cf4c0411b26a --- /dev/null +++ b/src/ExternalLibrary/Local/LocalLibraryInterface.php @@ -0,0 +1,86 @@ +<?php + +/** + * @file + * Contains \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface. + */ + +namespace Drupal\libraries\ExternalLibrary\Local; + +use Drupal\Component\Plugin\Factory\FactoryInterface; +use Drupal\libraries\ExternalLibrary\ExternalLibraryInterface; + +/** + * Provides an interface for local libraries. + * + * Local libraries are libraries that can be found on the filesystem. If the + * library files can be found in the filesystem a library is considered + * installed and its library path can be retrieved. + * + * A library not being installed can have a different meaning depending on the + * library type. A PHP file library. for example, can not be loaded if it is not + * installed whereas an asset library can be loaded from a remote source if one + * is provided and that is permitted by the configuration. + */ +interface LocalLibraryInterface extends ExternalLibraryInterface { + + /** + * Checks whether the library is installed. + * + * @return bool + * TRUE if the library is installed; FALSE otherwise; + */ + public function isInstalled(); + + /** + * Marks the library as uninstalled. + * + * A corresponding method to mark the library as installed is not provided as + * an installed library should have a library path, so that + * LocalLibraryInterface::setLibraryPath() can be used instead. + * + * @return $this + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::isInstalled() + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::setLibraryPath() + */ + public function setUninstalled(); + + /** + * Gets the path to the library. + * + * @return string + * The absolute path to the library on the filesystem. + * + * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::setLibraryPath() + */ + public function getLibraryPath(); + + /** + * Sets the library path of the library. + * + * @param string $path + * The path to the library. + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLibraryPath() + */ + public function setLibraryPath($path); + + /** + * Gets the locator of this library using the locator factory. + * + * Because determining the installation status and library path of a library + * is not specific to any library or even any library type, this logic is + * offloaded to separate locator objects. + * + * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory + * + * @return \Drupal\libraries\ExternalLibrary\Local\LocatorInterface + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface + */ + public function getLocator(FactoryInterface $locator_factory); + +} diff --git a/src/ExternalLibrary/Local/LocalLibraryTrait.php b/src/ExternalLibrary/Local/LocalLibraryTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..5a6de590ec04a6ef6850c1a07ad5cc1899d88f6b --- /dev/null +++ b/src/ExternalLibrary/Local/LocalLibraryTrait.php @@ -0,0 +1,103 @@ +<?php + +/** + * @file + * Contains \Drupal\libraries\ExternalLibrary\Local\LocalLibraryTrait. + */ + +namespace Drupal\libraries\ExternalLibrary\Local; +use Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException; + +/** + * Provides a trait for local libraries utilizing a stream wrapper. + * + * It assumes that the library files can be accessed using a specified stream + * wrapper and that the first component of the file URIs are the library IDs. + * Thus, file URIs are of the form: + * stream-wrapper-scheme://library-id/path/to/file/within/the/library/filename + * + * This trait should only be used by classes implementing LocalLibraryInterface. + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface + */ +trait LocalLibraryTrait { + + /** + * Whether or not the library is installed. + * + * A library being installed means its files can be found on the filesystem. + * + * @var bool + */ + protected $installed = FALSE; + + /** + * The absolute path to the library on the filesystem. + * + * @var string + */ + protected $libraryPath; + + /** + * Checks whether the library is installed. + * + * @return bool + * TRUE if the library is installed; FALSE otherwise; + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::isInstalled() + */ + public function isInstalled() { + return $this->installed; + } + + /** + * Marks the library as uninstalled. + * + * A corresponding method to mark the library as installed is not provided as + * an installed library should have a library path, so that + * LocalLibraryInterface::setLibraryPath() can be used instead. + * + * @return $this + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::setUninstalled() + */ + public function setUninstalled() { + $this->installed = FALSE; + return $this; + } + + /** + * Gets the path to the library. + * + * @return string + * The absolute path to the library on the filesystem. + * + * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLibraryPath() + */ + public function getLibraryPath() { + if (!$this->isInstalled()) { + /** @var \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $this */ + throw new LibraryNotInstalledException($this); + } + + return $this->libraryPath; + } + + /** + * Sets the library path of the library. + * + * @param string $path + * The path to the library. + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLibraryPath() + */ + public function setLibraryPath($path) { + $this->installed = TRUE; + $this->libraryPath = (string) $path; + + assert('$this->libraryPath !== ""'); + } + +} diff --git a/src/ExternalLibrary/Local/LocatorInterface.php b/src/ExternalLibrary/Local/LocatorInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..dbaad0cd898aa956625bee0c0b9413b9d80f0de8 --- /dev/null +++ b/src/ExternalLibrary/Local/LocatorInterface.php @@ -0,0 +1,29 @@ +<?php + +/** + * @file + * Contains \Drupal\libraries\ExternalLibrary\LocalInterface. + */ + +namespace Drupal\libraries\ExternalLibrary\Local; + +use Drupal\libraries\ExternalLibrary\ExternalLibraryInterface; + +/** + * Provides an interface for library locators. + * + * Because determining the installation status and library path of a library + * is not specific to any library or even any library type, this logic can be + * implemented generically in form of a locator. + */ +interface LocatorInterface { + + /** + * Locates a library. + * + * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library + * The library to locate. + */ + public function locate(LocalLibraryInterface $library); + +} diff --git a/src/ExternalLibrary/Local/LocatorManager.php b/src/ExternalLibrary/Local/LocatorManager.php new file mode 100644 index 0000000000000000000000000000000000000000..4979f319ba535de849f0f0750e91cc245e01307b --- /dev/null +++ b/src/ExternalLibrary/Local/LocatorManager.php @@ -0,0 +1,38 @@ +<?php + +/** + * @file + * Contains \Drupal\libraries\ExternalLibrary\Local\LocatorManager. + */ + +namespace Drupal\libraries\ExternalLibrary\Local; + +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Plugin\DefaultPluginManager; + +/** + * Provides a plugin manager for library locator plugins. + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface + */ +class LocatorManager extends DefaultPluginManager { + + /** + * Constructs a locator manager. + * + * @param \Traversable $namespaces + * An object that implements \Traversable which contains the root paths + * keyed by the corresponding namespace to look for plugin implementations. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler to invoke the alter hook with. + */ + public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { + parent::__construct('Plugin/libraries/Locator', $namespaces, $module_handler, 'Drupal\libraries\ExternalLibrary\Local\LocatorInterface', 'Drupal\libraries\Annotation\Locator'); + $this->alterInfo('libraries_locator_info'); + $this->setCacheBackend($cache_backend, 'libraries_locator_info'); + } + +} diff --git a/src/ExternalLibrary/LocalLibraryInterface.php b/src/ExternalLibrary/LocalLibraryInterface.php deleted file mode 100644 index fc4c997e81dcd5740411e629dd2ba0511a73e561..0000000000000000000000000000000000000000 --- a/src/ExternalLibrary/LocalLibraryInterface.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\libraries\ExternalLibrary\LocalLibraryInterface. - */ - -namespace Drupal\libraries\ExternalLibrary; - - -/** - * Provides an interface for local libraries. - * - * @todo Explain - */ -interface LocalLibraryInterface extends ExternalLibraryInterface { - - /** - * Checks whether the library is installed. - * - * @return bool - * TRUE if the library is installed; FALSE otherwise; - */ - public function isInstalled(); - - /** - * Gets the path to the library. - * - * @return string - * The path to the library. - * - * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException - */ - public function getLibraryPath(); - -} diff --git a/src/ExternalLibrary/LocalLibraryTrait.php b/src/ExternalLibrary/LocalLibraryTrait.php deleted file mode 100644 index 7e6249c511570aad3ba468bc2c36fa16a515b616..0000000000000000000000000000000000000000 --- a/src/ExternalLibrary/LocalLibraryTrait.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\libraries\ExternalLibrary\LocalLibraryTrait. - */ - -namespace Drupal\libraries\ExternalLibrary; - -use Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException; - -/** - * Provides a trait for local libraries utilizing a stream wrapper. - * - * It assumes that the library files can be accessed using a specified stream - * wrapper and that the first component of the file URIs are the library IDs. - * Thus, file URIs are of the form: - * stream-wrapper-scheme://library-id/path/to/file/within/the/library/filename - * - * This trait should only be used by classes implementing LocalLibraryInterface. - * - * @see \Drupal\libraries\ExternalLibrary\LocalLibraryInterface - */ -trait LocalLibraryTrait { - - /** - * The file system helper. - * - * @var \Drupal\Core\File\FileSystemInterface - */ - protected $fileSystemHelper; - - /** - * Checks whether the library is installed. - * - * @return bool - * TRUE if the library is installed; FALSE otherwise; - */ - public function isInstalled() { - return is_dir($this->getLibraryPath()) && is_readable($this->getLibraryPath()); - } - - /** - * Gets the path to the library. - * - * @return string - * The path to the library. - * - * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException - */ - public function getLibraryPath() { - // @todo Validate that the library is installed without causing infinite - // recursion. - - return $this->fileSystemHelper->realpath($this->getUri()); - } - - /** - * Gets the URI of the library. - * - * @return string - * The URI of the library. - */ - protected function getUri() { - /** @var \Drupal\libraries\ExternalLibrary\LocalLibraryInterface|static $this */ - return $this->getScheme() . '://' . $this->getId(); - } - - /** - * Gets the URI scheme that is used for this type of library. - * - * @return string - * The URI scheme for this library. - */ - abstract protected function getScheme(); - -} diff --git a/src/ExternalLibrary/PhpFile/PhpFileLibrary.php b/src/ExternalLibrary/PhpFile/PhpFileLibrary.php index 56842b75584bf1e2c528e908532e122955718c3b..48391cc7f0ac904cd3394d95719d14ba5d1490b8 100644 --- a/src/ExternalLibrary/PhpFile/PhpFileLibrary.php +++ b/src/ExternalLibrary/PhpFile/PhpFileLibrary.php @@ -6,8 +6,11 @@ */ namespace Drupal\libraries\ExternalLibrary\PhpFile; + +use Drupal\Component\Plugin\Factory\FactoryInterface; +use Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException; use Drupal\libraries\ExternalLibrary\ExternalLibraryTrait; -use Drupal\libraries\ExternalLibrary\LocalLibraryTrait; +use Drupal\libraries\ExternalLibrary\Local\LocalLibraryTrait; /** * Provides a base PHP file library implementation. @@ -17,22 +20,39 @@ class PhpFileLibrary implements PhpFileLibraryInterface { use ExternalLibraryTrait; use LocalLibraryTrait; + /** + * An array of PHP files for this library. + * + * @var array + */ + protected $files = []; + /** * Constructs a PHP file library. * * @param string $id * The library ID. + * @param array $files + * An array of PHP files for this library. + */ + public function __construct($id, array $files) { + $this->id = (string) $id; + $this->files = $files; + } + + /** + * Creates an instance of the library from its definition. + * + * @param string $id + * The library ID. * @param array $definition * The library definition array parsed from the definition JSON file. * - * @todo Dependency injection + * @return static */ - public function __construct($id, array $definition) { - $this->id = (string) $id; - // @todo Split this into proper properties. - $this->definition = $definition; - - $this->fileSystemHelper = \Drupal::service('file_system'); + public static function create($id, array $definition) { + $definition += ['files' => []]; + return new static($id, $definition['files']); } /** @@ -46,8 +66,20 @@ class PhpFileLibrary implements PhpFileLibraryInterface { * {@inheritdoc} */ public function getPhpFiles() { - // @todo - return $this->definition['files']; + if (!$this->isInstalled()) { + throw new LibraryNotInstalledException($this); + } + + foreach ($this->files as $file) { + yield $this->getLibraryPath() . DIRECTORY_SEPARATOR . $file; + } + } + + /** + * {@inheritdoc} + */ + public function getLocator(FactoryInterface $locator_factory) { + return $locator_factory->createInstance('stream', ['scheme' => 'php-file']); } } diff --git a/src/ExternalLibrary/PhpFile/PhpFileLibraryInterface.php b/src/ExternalLibrary/PhpFile/PhpFileLibraryInterface.php index 8d29fe4839a66507b53dcaf98e91f8be0ab13940..f149a9ff3aca525cac48756df171b90f8bd94484 100644 --- a/src/ExternalLibrary/PhpFile/PhpFileLibraryInterface.php +++ b/src/ExternalLibrary/PhpFile/PhpFileLibraryInterface.php @@ -7,7 +7,7 @@ namespace Drupal\libraries\ExternalLibrary\PhpFile; -use Drupal\libraries\ExternalLibrary\LocalLibraryInterface; +use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface; /** * Provides an interface for libraries which can be loaded. @@ -19,7 +19,7 @@ interface PhpFileLibraryInterface extends LocalLibraryInterface { /** * Returns the PHP files of this library. * - * @return string[] + * @return string[]|\Generator * An array of absolute file paths of PHP files. */ public function getPhpFiles(); diff --git a/src/Plugin/MissingPluginConfigurationException.php b/src/Plugin/MissingPluginConfigurationException.php new file mode 100644 index 0000000000000000000000000000000000000000..3c0f19bb1f849b4592dcabb1024a98d57172bb93 --- /dev/null +++ b/src/Plugin/MissingPluginConfigurationException.php @@ -0,0 +1,58 @@ +<?php + +/** + * @file + * Contains \Drupal\libraries\Exception\MissingPluginConfigurationException. + */ + +namespace Drupal\libraries\Plugin; +use Drupal\Component\Plugin\Exception\PluginException; +use Exception; + + +/** + * Provides an exception class for missing plugin configuration. + * + * The plugin system allows passing arbitrary data to plugins in form of the + * $configuration array. Some plugins, however, may depend on certain keys to + * be present in $configuration. This exception class can be used if such keys + * are missing. + * + * @todo Provide accessors for the passed-in information. + */ +class MissingPluginConfigurationException extends PluginException { + + /** + * Constructs an exception for a missing plugin configuration value. + * + * @param string $plugin_id + * The plugin ID. + * @param $plugin_definition + * The plugin definition + * @param array $configuration + * The plugin configuration. + * @param $missing_key + * The missing key in the configuration. + * @param string $message + * (optional) The exception message. + * @param int $code + * (optional) The error code. + * @param \Exception $previous + * (optional) The previous exception. + */ + public function __construct( + $plugin_id, + $plugin_definition, + array $configuration, + $missing_key, + $message = '', + $code = 0, + \Exception $previous = NULL + ) { + $message = $message ?: "The '{$missing_key}' key is missing in the configuration of the '{$plugin_id}' plugin."; + parent::__construct($message, $code, $previous); + } + + +} + diff --git a/src/Plugin/libraries/Locator/StreamLocator.php b/src/Plugin/libraries/Locator/StreamLocator.php new file mode 100644 index 0000000000000000000000000000000000000000..e6a628f60cf90c2f87cc37b76a31ecdbb15dbb25 --- /dev/null +++ b/src/Plugin/libraries/Locator/StreamLocator.php @@ -0,0 +1,103 @@ +<?php + +/** + * @file + * Contains \Drupal\libraries\Plugin\libraries\Locator\StreamLocatorTrait. + */ + +namespace Drupal\libraries\Plugin\libraries\Locator; + +use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Core\File\FileSystemInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface; +use Drupal\libraries\ExternalLibrary\Local\LocatorInterface; +use Drupal\libraries\Plugin\MissingPluginConfigurationException; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Provides a locator utilizing a stream wrapper. + * + * It makes the following assumptions: + * - The library files can be accessed using a specified stream. + * - The stream wrapper is local (i.e. it returns a proper value in its + * realpath() method). + * - The first component of the file URIs are the library IDs (i.e. file URIs + * are of the form: scheme://library-id/path/to/file/filename). + * + * @Locator("stream") + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface + */ +class StreamLocator implements LocatorInterface, ContainerFactoryPluginInterface { + + /** + * The file system helper. + * + * @var \Drupal\Core\File\FileSystemInterface + */ + protected $fileSystemHelper; + + /** + * The scheme of the stream wrapper. + * + * @var string + */ + protected $scheme; + + /** + * Constructs a stream locator. + * + * @param \Drupal\Core\File\FileSystemInterface $file_system_helper + * The file system helper. + * @param string $scheme + * The scheme of the stream wrapper. + */ + public function __construct(FileSystemInterface $file_system_helper, $scheme) { + $this->fileSystemHelper = $file_system_helper; + $this->scheme = (string) $scheme; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + if (!isset($configuration['scheme'])) { + throw new MissingPluginConfigurationException($plugin_id, $plugin_definition, $configuration, 'scheme'); + } + return new static($container->get('file_system'), $configuration['scheme']); + } + + + /** + * Locates a library. + * + * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library + * The library to locate. + * + * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface::locate() + */ + public function locate(LocalLibraryInterface $library) { + $path = $this->fileSystemHelper->realpath($this->getUri($library)); + if (is_dir($path) && is_readable($path)) { + $library->setLibraryPath($path); + } + else { + $library->setUninstalled(); + } + } + + /** + * Gets the URI of a library. + * + * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library + * The library. + * + * @return string + * The URI of the library. + */ + protected function getUri(LocalLibraryInterface $library) { + return $this->scheme . '://' . $library->getId(); + } + +}