From f6f705f8a29f08d7dc21e36ee7b3bb0ed26d1c01 Mon Sep 17 00:00:00 2001 From: Dave Long <dave@longwaveconsulting.com> Date: Fri, 15 Nov 2024 13:03:22 +0000 Subject: [PATCH] Issue #3478621 by catch, longwave, nicxvan: Add filecache to OOP hook attribute parsing (cherry picked from commit 6aca2acfd19c043f688e505b94b72cb1f8369eb0) --- .../Drupal/Core/Hook/HookCollectorPass.php | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 3d96ca99486f..a1d38544c22f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -6,9 +6,11 @@ use Drupal\Component\Annotation\Doctrine\StaticReflectionParser; use Drupal\Component\Annotation\Reflection\MockFileFinder; +use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\LegacyHook; +use Drupal\Core\Site\Settings; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -156,6 +158,11 @@ public static function collectAllHookImplementations(array $module_filenames): s * @return void */ protected function collectModuleHookImplementations($dir, $module, $module_preg): void { + // Add the deployment identifier to the cache namespace so that changes in + // the implementation of attribute parsing will not result in a stale + // cache. + $file_cache = FileCacheFactory::get('hook_implementations' . ':' . Settings::get('deployment_identifier')); + $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::FOLLOW_SYMLINKS); $iterator = new \RecursiveCallbackFilterIterator($iterator, static::filterIterator(...)); $iterator = new \RecursiveIteratorIterator($iterator); @@ -163,32 +170,53 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg) foreach ($iterator as $fileinfo) { assert($fileinfo instanceof \SplFileInfo); $extension = $fileinfo->getExtension(); + $filename = $fileinfo->getPathname(); + $cached = $file_cache->get($filename); + if ($extension === 'module' && !$iterator->getDepth()) { // There is an expectation for all modules to be loaded. However, // .module files are not supposed to be in subdirectories. - include_once $fileinfo->getPathname(); + include_once $filename; } if ($extension === 'php') { - $namespace = preg_replace('#^src/#', "Drupal/$module/", $iterator->getSubPath()); - $class = $namespace . '/' . $fileinfo->getBasename('.php'); - $class = str_replace('/', '\\', $class); - foreach (static::getHookAttributesInClass($class) as $attribute) { + 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 = static::getHookAttributesInClass($class); + $file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); + } + foreach ($attributes as $attribute) { $this->addFromAttribute($attribute, $class, $module); } } else { - $finder = MockFileFinder::create($fileinfo->getPathName()); - $parser = new StaticReflectionParser('', $finder); - foreach ($parser->getMethodAttributes() as $function => $attributes) { - if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches)) { - $this->addProceduralImplementation($fileinfo, $matches['hook'], $matches['module'], $matches['function']); + if ($cached) { + $implementations = $cached; + } + else { + $finder = MockFileFinder::create($filename); + $parser = new StaticReflectionParser('', $finder); + $implementations = []; + foreach ($parser->getMethodAttributes() as $function => $attributes) { + if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches)) { + $implementations[] = ['function' => $function, 'module' => $matches['module'], 'hook' => $matches['hook']]; + } } + $file_cache->set($filename, $implementations); + } + foreach ($implementations as $implementation) { + $this->addProceduralImplementation($fileinfo, $implementation['hook'], $implementation['module'], $implementation['function']); } } if ($extension === 'inc') { $parts = explode('.', $fileinfo->getFilename()); if (count($parts) === 3 && $parts[0] === $module) { - $this->groupIncludes[$parts[1]][] = $fileinfo->getPathname(); + $this->groupIncludes[$parts[1]][] = $filename; } } } -- GitLab