function ThemeHookCollectorPass::collectThemeHookImplementations
Collects procedural and Attribute hook implementations.
Parameters
string $dir: The directory in which the theme resides.
string $theme: The name of the theme.
string $currentThemePreg: A regular expression matching only the theme being scanned.
string $allThemesPreg: A regular expression matching every theme, longer theme names are matched first.
bool $shouldSkipProceduralScan: Skip the procedural check for the current theme.
File
-
core/
lib/ Drupal/ Core/ Hook/ ThemeHookCollectorPass.php, line 206
Class
- ThemeHookCollectorPass
- Collects and registers hook implementations.
Namespace
Drupal\Core\HookCode
protected function collectThemeHookImplementations($dir, $theme, $currentThemePreg, $allThemesPreg, bool $shouldSkipProceduralScan) : void {
$hookFileCache = FileCacheFactory::get('theme_hook_implementations');
$proceduralHookFileCache = FileCacheFactory::get('theme_procedural_hook_implementations:' . $allThemesPreg);
$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);
$fileExtension = $fileinfo->getExtension();
$filename = $fileinfo->getPathname();
if ($fileExtension === 'php') {
$cached = $hookFileCache->get($filename);
if ($cached) {
$class = $cached['class'];
$attributes = $cached['attributes'];
}
else {
$namespace = preg_replace('#^src/#', "Drupal/{$theme}/", $iterator->getSubPath());
$class = $namespace . '/' . $fileinfo->getBasename('.php');
$class = str_replace('/', '\\', $class);
$attributes = [];
if (class_exists($class)) {
$reflectionClass = new \ReflectionClass($class);
$attributes = self::getAttributeInstances($reflectionClass);
$hookFileCache->set($filename, [
'class' => $class,
'attributes' => $attributes,
]);
}
}
foreach ($attributes as $method => $methodAttributes) {
foreach ($methodAttributes as $attribute) {
if ($attribute instanceof Hook) {
self::checkInvalidHookParametersInThemes($attribute, $class);
$this->oopImplementations[$attribute->hook][$class . '::' . ($attribute->method ?: $method)] = $theme;
}
elseif ($attribute instanceof RemoveHook) {
throw new \LogicException("The #[RemoveHook] attribute is not allowed in themes. Found in {$class}.");
}
elseif ($attribute instanceof ReorderHook) {
throw new \LogicException("The #[ReorderHook] attribute is not allowed in themes. Found in {$class}.");
}
}
}
}
elseif (!$shouldSkipProceduralScan) {
$implementations = $proceduralHookFileCache->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($currentThemePreg, $function, $matches) || preg_match($allThemesPreg, $function, $matches))) {
assert($function === $matches['theme'] . '_' . $matches['hook']);
$implementations[] = [
'theme' => $matches['theme'],
'hook' => $matches['hook'],
];
}
}
$proceduralHookFileCache->set($filename, $implementations);
}
foreach ($implementations as $implementation) {
$this->proceduralImplementations[$implementation['hook']][] = $implementation['theme'];
}
}
}
}
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.