class ComponentDiscovery in Decoupled Blocks 8
Discovery service for front-end components provided by modules and themes.
Components (anything whose info file 'type' is 'pdb') are treated as Drupal extensions unto themselves.
Hierarchy
- class \Drupal\Core\Extension\ExtensionDiscovery
- class \Drupal\pdb\ComponentDiscovery implements ComponentDiscoveryInterface
Expanded class hierarchy of ComponentDiscovery
1 string reference to 'ComponentDiscovery'
1 service uses ComponentDiscovery
File
- src/
ComponentDiscovery.php, line 21
Namespace
Drupal\pdbView source
class ComponentDiscovery extends ExtensionDiscovery implements ComponentDiscoveryInterface {
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The info parser.
*
* @var \Drupal\Core\Extension\InfoParserInterface
*/
protected $infoParser;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Flag to indicate if the discovery is global or fixed to given dirs.
*
* @var bool
*/
protected $globalDiscovery = FALSE;
/**
* ComponentDiscovery constructor.
*
* @param string $root
* The root directory of the Drupal installation.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
* @param \Drupal\Core\Extension\InfoParserInterface $info_parser
* The info parser.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct($root, EventDispatcherInterface $event_dispatcher, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler) {
parent::__construct($root);
$this->eventDispatcher = $event_dispatcher;
$this->infoParser = $info_parser;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public function getComponents() {
// Find components.
$components = $this
->scan('pdb');
// Set defaults for module info.
$defaults = [
'dependencies' => [],
'description' => '',
'version' => NULL,
];
// Read info files for each module.
foreach ($components as $key => $component) {
// Look for the info file.
$component->info = $this->infoParser
->parse($component
->getPathname());
// Merge in defaults and save.
$components[$key]->info = $component->info + $defaults;
}
$this->moduleHandler
->alter('component_info', $components);
return $components;
}
/**
* {@inheritdoc}
*
* Extends to provide user defined paths to look for components.
*/
public function scan($type, $include_tests = NULL) {
// Try to get search dirs from settings.php.
$search_dirs = Settings::get('pdb_search_dirs', []);
if (is_string($search_dirs)) {
$search_dirs = [
$search_dirs,
];
}
// Try to get search dirs from subscribers.
$event = new PdbDiscoveryEvent($search_dirs);
$this->eventDispatcher
->dispatch(PdbDiscoveryEvent::SEARCH_DIRS, $event);
// Get the updated dicovery path from subscribers.
$search_dirs = $event
->getDirs();
// If user is not defining any custom path, then do a global discovery
// by following parent's approach.
if (empty($search_dirs)) {
$this->globalDiscovery = TRUE;
return parent::scan($type, $include_tests);
}
// Based on parent::scan().
$files = [];
foreach ($search_dirs as $dir) {
// Discover all extensions in the directory, unless we did already.
if (!isset(static::$files[$this->root][$dir][$include_tests])) {
static::$files[$this->root][$dir][$include_tests] = $this
->scanDirectory($dir, $include_tests);
}
// Only return extensions of the requested type.
if (isset(static::$files[$this->root][$dir][$include_tests][$type])) {
$files += static::$files[$this->root][$dir][$include_tests][$type];
}
}
// If applicable, filter out extensions that do not belong to the current
// installation profiles.
$files = $this
->filterByProfileDirectories($files);
// Sort the discovered extensions by their originating directories.
$origin_weights = array_flip($search_dirs);
$files = $this
->sort($files, $origin_weights);
// Process and return the list of extensions keyed by extension name.
return $this
->process($files);
}
/**
* {@inheritdoc}
*
* This is mostly extended to be able to use a different
* RecursiveExtensionFilterIterator class when searching custom user dirs.
*/
protected function scanDirectory($dir, $include_tests) {
// If it is a global discovery, then follow parent's approach.
if ($this->globalDiscovery) {
return parent::scanDirectory($dir, $include_tests);
}
// Based on parent::scanDirectory().
$files = [];
// In order to scan top-level directories, absolute directory paths have to
// be used (which also improves performance, since any configured PHP
// include_paths will not be consulted). Retain the relative originating
// directory being scanned, so relative paths can be reconstructed below
// (all paths are expected to be relative to $this->root).
$dir_prefix = $dir == '' ? '' : "{$dir}/";
$absolute_dir = $dir == '' ? $this->root : $this->root . "/{$dir}";
if (!is_dir($absolute_dir)) {
return $files;
}
// Use Unix paths regardless of platform, skip dot directories, follow
// symlinks (to allow extensions to be linked from elsewhere), and return
// the RecursiveDirectoryIterator instance to have access to getSubPath(),
// since SplFileInfo does not support relative paths.
$flags = \FilesystemIterator::UNIX_PATHS;
$flags |= \FilesystemIterator::SKIP_DOTS;
$flags |= \FilesystemIterator::FOLLOW_SYMLINKS;
$flags |= \FilesystemIterator::CURRENT_AS_SELF;
$directory_iterator = new \RecursiveDirectoryIterator($absolute_dir, $flags);
// Allow directories specified in settings.php to be ignored. You can use
// this to not check for files in common special-purpose directories. For
// example, node_modules and bower_components. Ignoring irrelevant
// directories is a performance boost.
$ignore_directories = Settings::get('file_scan_ignore_directories', []);
// Filter the recursive scan to discover extensions only.
// Important: Without a RecursiveFilterIterator, RecursiveDirectoryIterator
// would recurse into the entire filesystem directory tree without any kind
// of limitations.
$filter = new PdbRecursiveExtensionFilterIterator($directory_iterator, $ignore_directories);
$filter
->acceptTests($include_tests);
// The actual recursive filesystem scan is only invoked by instantiating the
// RecursiveIteratorIterator.
$iterator = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::LEAVES_ONLY, \RecursiveIteratorIterator::CATCH_GET_CHILD);
foreach ($iterator as $key => $fileinfo) {
// All extension names in Drupal have to be valid PHP function names due
// to the module hook architecture.
if (!preg_match(static::PHP_FUNCTION_PATTERN, $fileinfo
->getBasename('.info.yml'))) {
continue;
}
$extension_arguments = $this->fileCache ? $this->fileCache
->get($fileinfo
->getPathName()) : FALSE;
// Ensure $extension_arguments is an array. Previously, the Extension
// object was cached and now needs to be replaced with the array.
if (empty($extension_arguments) || !is_array($extension_arguments)) {
// Determine extension type from info file.
$type = FALSE;
$file = $fileinfo
->openFile('r');
while (!$type && !$file
->eof()) {
preg_match('@^type:\\s*(\'|")?(\\w+)\\1?\\s*$@', $file
->fgets(), $matches);
if (isset($matches[2])) {
$type = $matches[2];
}
}
if (empty($type)) {
continue;
}
$name = $fileinfo
->getBasename('.info.yml');
$pathname = $dir_prefix . $fileinfo
->getSubPathname();
// Determine whether the extension has a main extension file.
// For theme engines, the file extension is .engine.
if ($type == 'theme_engine') {
$filename = $name . '.engine';
}
else {
$filename = $name . '.' . $type;
}
if (!file_exists($this->root . '/' . dirname($pathname) . '/' . $filename)) {
$filename = NULL;
}
$extension_arguments = [
'type' => $type,
'pathname' => $pathname,
'filename' => $filename,
'subpath' => $fileinfo
->getSubPath(),
];
if ($this->fileCache) {
$this->fileCache
->set($fileinfo
->getPathName(), $extension_arguments);
}
}
$extension = new Extension($this->root, $extension_arguments['type'], $extension_arguments['pathname'], $extension_arguments['filename']);
// Track the originating directory for sorting purposes.
$extension->subpath = $extension_arguments['subpath'];
$extension->origin = $dir;
$files[$extension_arguments['type']][$key] = $extension;
}
return $files;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ComponentDiscovery:: |
protected | property | The event dispatcher. | |
ComponentDiscovery:: |
protected | property | Flag to indicate if the discovery is global or fixed to given dirs. | |
ComponentDiscovery:: |
protected | property | The info parser. | |
ComponentDiscovery:: |
protected | property | The module handler. | |
ComponentDiscovery:: |
public | function |
Find all available front-end components. Overrides ComponentDiscoveryInterface:: |
|
ComponentDiscovery:: |
public | function |
Extends to provide user defined paths to look for components. Overrides ExtensionDiscovery:: |
|
ComponentDiscovery:: |
protected | function |
This is mostly extended to be able to use a different
RecursiveExtensionFilterIterator class when searching custom user dirs. Overrides ExtensionDiscovery:: |
|
ComponentDiscovery:: |
public | function |
ComponentDiscovery constructor. Overrides ExtensionDiscovery:: |
|
ExtensionDiscovery:: |
protected | property | The file cache object. | |
ExtensionDiscovery:: |
protected static | property | Previously discovered files keyed by origin directory and extension type. | |
ExtensionDiscovery:: |
protected | property | List of installation profile directories to additionally scan. | |
ExtensionDiscovery:: |
protected | property | The app root for the current operation. | |
ExtensionDiscovery:: |
protected | property | The site path. | |
ExtensionDiscovery:: |
protected | function | Filters out extensions not belonging to the scanned installation profiles. | |
ExtensionDiscovery:: |
public | function | Gets the installation profile directories to be scanned. | |
ExtensionDiscovery:: |
constant | Origin directory weight: Core. | ||
ExtensionDiscovery:: |
constant | Origin directory weight: Parent site directory of a test site environment. | ||
ExtensionDiscovery:: |
constant | Origin directory weight: Installation profile. | ||
ExtensionDiscovery:: |
constant | Origin directory weight: Site-wide directory. | ||
ExtensionDiscovery:: |
constant | Origin directory weight: Site-specific directory. | ||
ExtensionDiscovery:: |
constant | Origin directory weight: sites/all. | ||
ExtensionDiscovery:: |
constant | Regular expression to match PHP function names. | ||
ExtensionDiscovery:: |
protected | function | Processes the filtered and sorted list of extensions. | |
ExtensionDiscovery:: |
public | function | Sets explicit profile directories to scan. | |
ExtensionDiscovery:: |
public | function | Sets installation profile directories based on current site settings. | |
ExtensionDiscovery:: |
protected | function | Sorts the discovered extensions. |