function HookCollectorPass::collectModuleHookImplementations
Collects procedural and Attribute hook implementations.
Parameters
string $dir: The directory in which the module resides.
string $module: The name of the module.
string $current_module_preg: A regular expression matching only the module being scanned.
string $all_modules_preg: A regular expression matching every module, longer module names are matched first.
bool $skip_procedural: Skip the procedural check for the current module.
File
-
core/
lib/ Drupal/ Core/ Hook/ HookCollectorPass.php, line 411
Class
- HookCollectorPass
- Collects and registers hook implementations.
Namespace
Drupal\Core\HookCode
protected function collectModuleHookImplementations($dir, $module, $current_module_preg, $all_modules_preg, bool $skip_procedural) : void {
$hook_file_cache = FileCacheFactory::get('hook_implementations');
$procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $all_modules_preg);
$iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::FOLLOW_SYMLINKS);
$iterator = new \RecursiveCallbackFilterIterator($iterator, static::filterIterator(...));
$iterator = new \RecursiveIteratorIterator($iterator);
/** @var \RecursiveDirectoryIterator | \RecursiveIteratorIterator $iterator*/
foreach ($iterator as $fileinfo) {
assert($fileinfo instanceof \SplFileInfo);
$extension = $fileinfo->getExtension();
$filename = $fileinfo->getPathname();
if ($extension === 'php') {
$cached = $hook_file_cache->get($filename);
if ($cached) {
$class = $cached['class'];
$attributes = $cached['attributes'];
}
else {
$namespace = preg_replace('#^src/#', "Drupal/{$module}/", $iterator->getSubPath());
$class = $namespace . '/' . $fileinfo->getBasename('.php');
$class = str_replace('/', '\\', $class);
$attributes = [];
if (class_exists($class)) {
$reflectionClass = new \ReflectionClass($class);
$attributes = self::getAttributeInstances($reflectionClass);
$hook_file_cache->set($filename, [
'class' => $class,
'attributes' => $attributes,
]);
}
}
foreach ($attributes as $method => $methodAttributes) {
foreach ($methodAttributes as $attribute) {
if ($attribute instanceof Hook) {
self::checkForProceduralOnlyHooks($attribute, $class);
$this->oopImplementations[$attribute->hook][$class . '::' . ($attribute->method ?: $method)] = $attribute->module ?? $module;
if ($attribute->order !== NULL) {
// Use a lower weight for order operations that are declared
// together with the hook listener they apply to.
$this->orderOperations[$attribute->hook][0][] = $attribute->order
->getOperation("{$class}::{$method}");
}
}
elseif ($attribute instanceof ReorderHook) {
// Use a higher weight for order operations that target other hook
// listeners.
$this->orderOperations[$attribute->hook][1][] = $attribute->order
->getOperation($attribute->class . '::' . $attribute->method);
}
elseif ($attribute instanceof RemoveHook) {
$this->removeHookIdentifiers[$attribute->hook][] = $attribute->class . '::' . $attribute->method;
}
}
}
}
elseif (!$skip_procedural) {
$implementations = $procedural_hook_file_cache->get($filename);
if ($implementations === NULL) {
$finder = MockFileFinder::create($filename);
$parser = new StaticReflectionParser('', $finder);
$implementations = [];
foreach ($parser->getMethodAttributes() as $function => $attributes) {
if (StaticReflectionParser::hasAttribute($attributes, ProceduralHookScanStop::class)) {
break;
}
if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && (preg_match($current_module_preg, $function, $matches) || preg_match($all_modules_preg, $function, $matches)) && !StaticReflectionParser::hasAttribute($attributes, LegacyModuleImplementsAlter::class)) {
// Skip hooks that are not supported by the new hook system, they
// do not need to be added to the BC layer. Note that is different
// from static::checkForProceduralOnlyHooks(). hook_requirements
// is allowed and only update hooks are considered as dynamic
// exclusion, since post updates are not expected to be parsed.
// Also, update hooks are checked as ends with, since the regular
// expression sometimes attributes them to the wrong module,
// resulting in a prefix.
$staticDenyHooks = [
'install',
'install_tasks',
'install_tasks_alter',
'schema',
'uninstall',
'update_last_removed',
'update_dependencies',
];
if (in_array($matches['hook'], $staticDenyHooks) || preg_match('/update_\\d+$/', $function)) {
continue;
}
assert($function === $matches['module'] . '_' . $matches['hook']);
$implementations[] = [
'module' => $matches['module'],
'hook' => $matches['hook'],
];
}
}
$procedural_hook_file_cache->set($filename, $implementations);
}
foreach ($implementations as $implementation) {
$this->addProceduralImplementation($fileinfo, $implementation['hook'], $implementation['module']);
}
}
if ($extension === 'inc') {
$parts = explode('.', $fileinfo->getFilename());
if (count($parts) === 3 && $parts[0] === $module) {
$this->groupIncludes[$parts[1]][] = $filename;
}
}
}
}
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.