From 4e7f94a2b481d546ce3a8e6c15f26059169b3db1 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 9 Dec 2024 12:23:22 -0500 Subject: [PATCH 001/268] New attributes for ordering --- .../Drupal/Core/Hook/Attribute/HookAfter.php | 36 +++++++++++++++++++ .../Drupal/Core/Hook/Attribute/HookBefore.php | 36 +++++++++++++++++++ .../Drupal/Core/Hook/Attribute/HookFirst.php | 28 +++++++++++++++ .../Drupal/Core/Hook/Attribute/HookLast.php | 28 +++++++++++++++ .../Core/Hook/Attribute/HookOrderGroup.php | 36 +++++++++++++++++++ 5 files changed, 164 insertions(+) create mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookAfter.php create mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookBefore.php create mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookFirst.php create mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookLast.php create mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php new file mode 100644 index 000000000000..dcd282b5b30b --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +/** + * Attribute for marking that a hook should be changed. + * + * This allows you to ensure the hook is executed after + * a specific hook in another module. + * + * @section sec_backwards_compatibility Backwards-compatibility + * + * To allow hook implementations to work on older versions of Drupal as well, + * keep the hook_module_implements_alter implementation and add the appropriate + * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and + * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to + * the hook_module_implements_alter() implementation. + * + * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] +class HookAfter { + + /** + * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. + * + * @param string $module + * The module this implementation should run before. + */ + public function __construct( + public string $module, + ) {} + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php new file mode 100644 index 000000000000..ba09d8b8d3fb --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +/** + * Attribute for marking that a hook should be changed. + * + * This allows you to ensure the hook is executed before + * a specific hook in another module. + * + * @section sec_backwards_compatibility Backwards-compatibility + * + * To allow hook implementations to work on older versions of Drupal as well, + * keep the hook_module_implements_alter implementation and add the appropriate + * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and + * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to + * the hook_module_implements_alter() implementation. + * + * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] +class HookBefore { + + /** + * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookBefore.php attribute object. + * + * @param string $module + * The module this implementation should run before. + */ + public function __construct( + public string $module, + ) {} + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php new file mode 100644 index 000000000000..21c33c4cdd6a --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +/** + * Attribute for marking that a hook should be executed first. + * + * @section sec_backwards_compatibility Backwards-compatibility + * + * To allow hook implementations to work on older versions of Drupal as well, + * keep the hook_module_implements_alter implementation and add the appropriate + * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and + * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to + * the hook_module_implements_alter() implementation. + * + * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] +class HookFirst { + + /** + * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookFirst.php attribute object. + */ + public function __construct() {} + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php new file mode 100644 index 000000000000..4269b67c2291 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +/** + * Attribute for marking that a hook should be executed last. + * + * @section sec_backwards_compatibility Backwards-compatibility + * + * To allow hook implementations to work on older versions of Drupal as well, + * keep the hook_module_implements_alter implementation and add the appropriate + * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and + * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to + * the hook_module_implements_alter() implementation. + * + * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] +class HookLast { + + /** + * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookLast.php attribute object. + */ + public function __construct() {} + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php new file mode 100644 index 000000000000..0fb0c3bfa183 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +/** + * Attribute for marking which specific implementations to group. + * + * This allows hook ordering to handle extra types such as ordering form_alter + * relative to hook_form_FORM_ID_alter. + * + * @section sec_backwards_compatibility Backwards-compatibility + * + * To allow hook implementations to work on older versions of Drupal as well, + * keep the hook_module_implements_alter implementation and add the appropriate + * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and + * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to + * the hook_module_implements_alter() implementation. + * + * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + */ +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] +class HookOrderGroup { + + /** + * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. + * + * @param array + * The group of implementations to change. + */ + public function __construct( + public string $group, + ) {} + +} -- GitLab From fb1364e2eb9df5a0c2b6e032e35495df6391d3d2 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 9 Dec 2024 13:00:54 -0500 Subject: [PATCH 002/268] Add change priority and grouping WIP --- .../Drupal/Core/Hook/HookCollectorPass.php | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 3809e24af21d..bb00f6b44b8f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -71,6 +71,11 @@ class HookCollectorPass implements CompilerPassInterface { */ private array $groupIncludes = []; + /** + * A list of implementations to reprioritize. + */ + protected array $orderGroup = []; + /** * {@inheritdoc} */ @@ -311,6 +316,14 @@ protected function addFromAttribute(Hook $hook, $class, $module): void { } $this->moduleImplements[$hook->hook][$module] = ''; $this->implementations[$hook->hook][$module][$class][] = $hook->method; + + if ($hook->orderGroup) { + if (!isset($this->orderGroup[$hook->orderGroup])) { + $this->orderGroup[$hook->orderGroup] = ['drupal_hook' . $hook->orderGroup]; + } + $this->orderGroup[$hook->orderGroup][$hook->hook] = ['drupal_hook' . $hook->hook]; + + } } /** @@ -384,4 +397,111 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v } } + /** + * Change the priority of a hook implementation. + * + * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container + * The container builder. + * @param string $hook + * The name of the hook. + * @param string $class_and_method + * Class and method separated by :: containing the hook implementation which + * should be changed. + * @param bool $should_be_larger + * TRUE for before/first, FALSE for after/last. Larger priority listeners + * fire first. + * @param array|null $others + * Other hook implementations to compare to, if any. The array is keyed by + * string containing a class and method separated by ::, the value is not + * used. + * + * @return void + */ + protected function changePriority(ContainerBuilder $container, string $hook, string $class_and_method, bool $should_be_larger, ?array $others = NULL): void { + $events = $this->orderGroup[$hook] ?? ["drupal_hook.$hook"]; + foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) { + foreach ($attributes as $key => $tag) { + if (in_array($tag['event'], $events)) { + $index = "$id.$key"; + $priority = $tag['priority']; + // Symfony documents event listener priorities to be integers, + // HookCollectorPass sets them to be integers, ::setPriority() only + // accepts integers. + assert(is_int($priority)); + $priorities[$index] = $priority; + $specifier = "$id::" . $tag['method']; + if ($class_and_method === $specifier) { + $index_this = $index; + } + // If $others is specified by ::before() / ::after() then for + // comparison only the priority of those matter. + // For ::first() / ::last() the priority of every other hook + // matters. + elseif (!isset($others) || isset($others[$specifier])) { + $priorities_other[] = $priority; + } + } + } + } + if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { + return; + } + // The priority of the hook being changed. + $priority_this = $priorities[$index_this]; + // The priority of the hook being compared to. + $priority_other = $should_be_larger ? max($priorities_other) : min($priorities_other); + // If the order is correct there is nothing to do. If the two priorities + // are the same then the order is undefined and so it can't be correct. + // If they are not the same and $priority_this is already larger exactly + // when $should_be_larger says then it's the correct order. + if ($priority_this !== $priority_other && ($should_be_larger === ($priority_this > $priority_other))) { + return; + } + $priority_new = $priority_other + ($should_be_larger ? 1 : -1); + // For ::first() / ::last() this new priority is already larger/smaller + // than all existing priorities but for ::before() / ::after() it might + // belong to an already existing hook. In this case set the new priority + // temporarily to be halfway between $priority_other and $priority_new + // then give all hook implementations new, integer priorities keeping this + // new order. This ensures the hook implementation being changed is in the + // right order relative to both $priority_other and the hook whose + // priority was $priority_new. + if (in_array($priority_new, $priorities)) { + $priorities[$index_this] = $priority_other + ($should_be_larger ? 0.5 : -0.5); + asort($priorities); + $changed_indexes = array_keys($priorities); + $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); + } + else { + $priorities[$index_this] = $priority_new; + $changed_indexes = [$index_this]; + } + foreach ($changed_indexes as $index) { + [$id, $key] = explode('.', $index); + self::setPriority($container, $id, (int) $key, $priorities[$index]); + } + } + + /** + * Set the priority of a listener. + * + * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container + * The container. + * @param string $class + * The name of the class, this is the same as the service id. + * @param int $key + * The key within the tags array of the 'kernel.event_listener' tag for the + * hook implementation to be changed. + * @param int $priority + * The new priority. + * + * @return void + */ + public static function setPriority(ContainerBuilder $container, string $class, int $key, int $priority): void { + $definition = $container->getDefinition($class); + $tags = $definition->getTags(); + $tags['kernel.event_listener'][$key]['priority'] = $priority; + $definition->setTags($tags); + } + } -- GitLab From 5a1307112c51b39baba4981b8500458a6612ff55 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 9 Dec 2024 16:31:53 -0500 Subject: [PATCH 003/268] Hook Order --- .../Drupal/Core/Hook/Attribute/HookAfter.php | 4 +- .../Drupal/Core/Hook/Attribute/HookBefore.php | 4 +- .../Core/Hook/Attribute/HookOrderGroup.php | 4 +- .../Drupal/Core/Hook/HookCollectorPass.php | 143 +++++++++++------- 4 files changed, 94 insertions(+), 61 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index dcd282b5b30b..9763e1cb1fd5 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -26,11 +26,11 @@ class HookAfter { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. * - * @param string $module + * @param array $modules * The module this implementation should run before. */ public function __construct( - public string $module, + public array $modules, ) {} } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index ba09d8b8d3fb..73bcf62c3e9e 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -26,11 +26,11 @@ class HookBefore { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookBefore.php attribute object. * - * @param string $module + * @param array $modules * The module this implementation should run before. */ public function __construct( - public string $module, + public array $modules, ) {} } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php index 0fb0c3bfa183..d011996e6803 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php @@ -26,11 +26,11 @@ class HookOrderGroup { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. * - * @param array + * @param array $group * The group of implementations to change. */ public function __construct( - public string $group, + public array $group, ) {} } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index bb00f6b44b8f..89b5d88622e7 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -9,6 +9,11 @@ use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\HookBefore; +use Drupal\Core\Hook\Attribute\HookFirst; +use Drupal\Core\Hook\Attribute\HookLast; +use Drupal\Core\Hook\Attribute\HookOrderGroup; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -74,7 +79,12 @@ class HookCollectorPass implements CompilerPassInterface { /** * A list of implementations to reprioritize. */ - protected array $orderGroup = []; + protected array $moduleAttributes = []; + + /** + * An organized list of hooks to reorder. + */ + protected array $orderMap = []; /** * {@inheritdoc} @@ -94,6 +104,42 @@ public function process(ContainerBuilder $container): void { } $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); + foreach ($this->moduleAttributes as $module => $classAttributes) { + foreach ($classAttributes as $class => $methodAttributes) { + foreach ($methodAttributes as $method => $attributes) { + foreach ($attributes as $attribute) { + $attribute = $attribute->newInstance(); + switch (get_class($attribute)) { + case Hook::class: + self::checkForProceduralOnlyHooks($attribute->hook, $class); + $this->addFromAttribute($attribute, $class, $module); + break; + + case HookAfter::class: + $this->orderMap[$hook][$class][$method]['after'] = $attribute->modules; + break; + + case HookBefore::class: + $this->orderMap[$hook][$class][$method]['before'] = $attribute->modules; + break; + + case HookFirst::class: + $this->orderMap[$hook][$class][$method]['first'] = 9999; + break; + + case HookLast::class: + $this->orderMap[$hook][$class][$method]['last'] = -9999; + break; + + case HookOrderGroup::class: + $this->orderMap[$hook][$class][$method]['sort'] = $attribute->group; + break; + } + } + } + } + } + foreach ($collector->moduleImplements as $hook => $moduleImplements) { foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); @@ -114,13 +160,41 @@ public function process(ContainerBuilder $container): void { $definition->addTag('kernel.event_listener', [ 'event' => "drupal_hook.$hook", 'method' => $method, - 'priority' => $priority--, + 'priority' => $priority, ]); } } } } $container->setParameter('hook_implementations_map', $map); + + foreach ($this->orderMap as $hook => $classes) { + foreach ($classes as $class => $methods) { + foreach ($methods as $method => $actions) { + foreach ($actions as $action => $others) { + switch ($action) { + case 'first': + $this->changePriority($container, $hook, "$class::$method", TRUE); + break; + + case 'before': + // @todo $others likely needs to be updated. + $this->changePriority($container, $hook, "$class::$method", TRUE, $others); + break; + + case 'after': + // @todo $others likely needs to be updated. + $this->changePriority($container, $hook, "$class::$method", FALSE, $others); + break; + + case 'last': + $this->changePriority($container, $hook, "$class::$method", FALSE); + break; + } + } + } + } + } } /** @@ -174,6 +248,7 @@ public static function collectAllHookImplementations(array $module_filenames, ?C protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural): void { $hook_file_cache = FileCacheFactory::get('hook_implementations'); $procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $module_preg); + $this->moduleAttributes[$module] = []; $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::FOLLOW_SYMLINKS); $iterator = new \RecursiveCallbackFilterIterator($iterator, static::filterIterator(...)); @@ -192,6 +267,8 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, } if ($extension === 'php') { $cached = $hook_file_cache->get($filename); + // @todo remove this comment. + // $cached = FALSE; if ($cached) { $class = $cached['class']; $attributes = $cached['attributes']; @@ -201,16 +278,19 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $class = $namespace . '/' . $fileinfo->getBasename('.php'); $class = str_replace('/', '\\', $class); if (class_exists($class)) { - $attributes = static::getHookAttributesInClass($class); + $reflectionClass = new \ReflectionClass($class); + $reflectionClass = new \ReflectionClass($class); + $attributes['__invoke'] = $reflectionClass->getAttributes(); + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodName => $methodReflection) { + $attributes[$methodName] = $methodReflection->getAttributes(); + } $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } else { $attributes = []; } } - foreach ($attributes as $attribute) { - $this->addFromAttribute($attribute, $class, $module); - } + $this->moduleAttributes[$module][$class] = array_merge($this->moduleAttributes[$module][$class] ?? [], $attributes); } elseif (!$skip_procedural) { $implementations = $procedural_hook_file_cache->get($filename); @@ -260,46 +340,6 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv return in_array($extension, ['inc', 'module', 'profile', 'install']); } - /** - * An array of Hook attributes on this class with $method set. - * - * @param string $class - * The class. - * - * @return \Drupal\Core\Hook\Attribute\Hook[] - * An array of Hook attributes on this class. The $method property is - * guaranteed to be set. - */ - protected static function getHookAttributesInClass(string $class): array { - $reflection_class = new \ReflectionClass($class); - $class_implementations = []; - // Check for #[Hook] on the class itself. - foreach ($reflection_class->getAttributes(Hook::class, \ReflectionAttribute::IS_INSTANCEOF) as $reflection_attribute) { - $hook = $reflection_attribute->newInstance(); - assert($hook instanceof Hook); - self::checkForProceduralOnlyHooks($hook, $class); - if (!$hook->method) { - if (method_exists($class, '__invoke')) { - $hook->setMethod('__invoke'); - } - else { - throw new \LogicException("The Hook attribute for hook $hook->hook on class $class must specify a method."); - } - } - $class_implementations[] = $hook; - } - // Check for #[Hook] on methods. - foreach ($reflection_class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method_reflection) { - foreach ($method_reflection->getAttributes(Hook::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute_reflection) { - $hook = $attribute_reflection->newInstance(); - assert($hook instanceof Hook); - self::checkForProceduralOnlyHooks($hook, $class); - $class_implementations[] = $hook->setMethod($method_reflection->getName()); - } - } - return $class_implementations; - } - /** * Adds a Hook attribute implementation. * @@ -316,14 +356,6 @@ protected function addFromAttribute(Hook $hook, $class, $module): void { } $this->moduleImplements[$hook->hook][$module] = ''; $this->implementations[$hook->hook][$module][$class][] = $hook->method; - - if ($hook->orderGroup) { - if (!isset($this->orderGroup[$hook->orderGroup])) { - $this->orderGroup[$hook->orderGroup] = ['drupal_hook' . $hook->orderGroup]; - } - $this->orderGroup[$hook->orderGroup][$hook->hook] = ['drupal_hook' . $hook->hook]; - - } } /** @@ -344,6 +376,7 @@ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $h $this->hookInfo[] = $function; } if ($hook === 'module_implements_alter') { + // @todo confirm this is skipped when #[LegacyHook] should be. $this->moduleImplementsAlters[] = $function; } if ($fileinfo->getExtension() !== 'module') { @@ -397,7 +430,7 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v } } - /** + /** * Change the priority of a hook implementation. * * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container -- GitLab From 32f8d0eda367173c7572728cd2a271f8a97af95a Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 9 Dec 2024 16:33:41 -0500 Subject: [PATCH 004/268] Fix priority --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 89b5d88622e7..792717eb58de 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -160,7 +160,7 @@ public function process(ContainerBuilder $container): void { $definition->addTag('kernel.event_listener', [ 'event' => "drupal_hook.$hook", 'method' => $method, - 'priority' => $priority, + 'priority' => $priority--, ]); } } -- GitLab From bcf87750c28e6828ef221959c71e851eeb9f0477 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 9 Dec 2024 16:39:00 -0500 Subject: [PATCH 005/268] change priority --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 792717eb58de..b3523627dad2 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -451,7 +451,9 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v * @return void */ protected function changePriority(ContainerBuilder $container, string $hook, string $class_and_method, bool $should_be_larger, ?array $others = NULL): void { - $events = $this->orderGroup[$hook] ?? ["drupal_hook.$hook"]; + // @todo clean this up. + // $events = $this->orderGroup[$hook] ?? ["drupal_hook.$hook"]; + $events = ["drupal_hook.$hook"]; foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) { foreach ($attributes as $key => $tag) { if (in_array($tag['event'], $events)) { -- GitLab From 8bd68a236e44d61ed018669312dea8a047a2eec1 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 9 Dec 2024 16:50:02 -0500 Subject: [PATCH 006/268] Spelling and stan --- .../Drupal/Core/Hook/HookCollectorPass.php | 16 +++++----- .../Tests/Core/Hook/HookCollectorPassTest.php | 31 ------------------- 2 files changed, 9 insertions(+), 38 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index b3523627dad2..043eb86e1f6c 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -77,7 +77,7 @@ class HookCollectorPass implements CompilerPassInterface { private array $groupIncludes = []; /** - * A list of implementations to reprioritize. + * A list of attributes in modules for Hooks. */ protected array $moduleAttributes = []; @@ -113,26 +113,27 @@ public function process(ContainerBuilder $container): void { case Hook::class: self::checkForProceduralOnlyHooks($attribute->hook, $class); $this->addFromAttribute($attribute, $class, $module); + $this->orderMap[$module][$class][$method]['hook'] = $attribute->hook; break; case HookAfter::class: - $this->orderMap[$hook][$class][$method]['after'] = $attribute->modules; + $this->orderMap[$module][$class][$method]['after'] = $attribute->modules; break; case HookBefore::class: - $this->orderMap[$hook][$class][$method]['before'] = $attribute->modules; + $this->orderMap[$module][$class][$method]['before'] = $attribute->modules; break; case HookFirst::class: - $this->orderMap[$hook][$class][$method]['first'] = 9999; + $this->orderMap[$module][$class][$method]['first'] = 9999; break; case HookLast::class: - $this->orderMap[$hook][$class][$method]['last'] = -9999; + $this->orderMap[$module][$class][$method]['last'] = -9999; break; case HookOrderGroup::class: - $this->orderMap[$hook][$class][$method]['sort'] = $attribute->group; + $this->orderMap[$module][$class][$method]['sort'] = $attribute->group; break; } } @@ -168,9 +169,10 @@ public function process(ContainerBuilder $container): void { } $container->setParameter('hook_implementations_map', $map); - foreach ($this->orderMap as $hook => $classes) { + foreach ($this->orderMap as $module => $classes) { foreach ($classes as $class => $methods) { foreach ($methods as $method => $actions) { + $hook = $this->orderMap[$module][$class][$method]['hook']; foreach ($actions as $action => $others) { switch ($action) { case 'first': diff --git a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php index 4b92b8d6d25f..f7ccd2e17be4 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php @@ -85,35 +85,4 @@ public function testGroupIncludes(): void { $this->assertSame(self::GROUP_INCLUDES, $argument); } - /** - * @covers ::getHookAttributesInClass - */ - public function testGetHookAttributesInClass(): void { - // @phpstan-ignore-next-line - $getHookAttributesInClass = fn ($class) => $this->getHookAttributesInClass($class); - $p = new HookCollectorPass(); - $getHookAttributesInClass = $getHookAttributesInClass->bindTo($p, $p); - - $x = new class { - - #[Hook('foo')] - function foo(): void {} - - }; - $hooks = $getHookAttributesInClass(get_class($x)); - $hook = reset($hooks); - $this->assertInstanceOf(Hook::class, $hook); - $this->assertSame('foo', $hook->hook); - - $x = new class { - - #[Hook('install')] - function foo(): void {} - - }; - $this->expectException(\LogicException::class); - // This will throw exception, and stop code execution. - $getHookAttributesInClass(get_class($x)); - } - } -- GitLab From 63b2e7e6984462a72e8325d206d0ffa422a904dc Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 9 Dec 2024 16:51:47 -0500 Subject: [PATCH 007/268] PHPCS --- core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php index f7ccd2e17be4..2d6fba3b451f 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php @@ -6,7 +6,6 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\ProceduralCall; -use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\HookCollectorPass; use Drupal\Tests\UnitTestCase; use Drupal\Tests\Core\GroupIncludesTestTrait; -- GitLab From 0f037a9e7c4a44b37d0578aecade06536d67ba45 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 9 Dec 2024 20:50:12 -0500 Subject: [PATCH 008/268] Fix cache --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 043eb86e1f6c..b691de0c456c 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -281,10 +281,9 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $class = str_replace('/', '\\', $class); if (class_exists($class)) { $reflectionClass = new \ReflectionClass($class); - $reflectionClass = new \ReflectionClass($class); - $attributes['__invoke'] = $reflectionClass->getAttributes(); + $attributes['__invoke'] = array_map(fn ($x) => $x->newInstance(), $reflectionClass->getAttributes()); foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodName => $methodReflection) { - $attributes[$methodName] = $methodReflection->getAttributes(); + $attributes[$methodName] = array_map(fn ($x) => $x->newInstance(), $methodReflection->getAttributes()); } $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } -- GitLab From 7939725dcd45c2ee0dfccfb37f883316b46d7a64 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 9 Dec 2024 22:45:14 -0500 Subject: [PATCH 009/268] Mapping attributes --- .../Drupal/Core/Hook/HookCollectorPass.php | 94 +++++++++++-------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index b691de0c456c..fa9044bac5a3 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -104,37 +104,47 @@ public function process(ContainerBuilder $container): void { } $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); - foreach ($this->moduleAttributes as $module => $classAttributes) { - foreach ($classAttributes as $class => $methodAttributes) { - foreach ($methodAttributes as $method => $attributes) { + foreach ($collector->moduleAttributes as $module => $classes) { + foreach ($classes as $class => $methods) { + foreach ($methods as $method => $attributes) { foreach ($attributes as $attribute) { - $attribute = $attribute->newInstance(); - switch (get_class($attribute)) { - case Hook::class: - self::checkForProceduralOnlyHooks($attribute->hook, $class); - $this->addFromAttribute($attribute, $class, $module); - $this->orderMap[$module][$class][$method]['hook'] = $attribute->hook; - break; - - case HookAfter::class: - $this->orderMap[$module][$class][$method]['after'] = $attribute->modules; - break; - - case HookBefore::class: - $this->orderMap[$module][$class][$method]['before'] = $attribute->modules; - break; - - case HookFirst::class: - $this->orderMap[$module][$class][$method]['first'] = 9999; - break; - - case HookLast::class: - $this->orderMap[$module][$class][$method]['last'] = -9999; - break; - - case HookOrderGroup::class: - $this->orderMap[$module][$class][$method]['sort'] = $attribute->group; - break; + if ($attribute) { + switch (get_class($attribute)) { + case Hook::class: + $hook = $attribute->hook; + self::checkForProceduralOnlyHooks($attribute, $class); + if ($on_behalf_module = $attribute->module) { + $collector->moduleImplements[$hook][$on_behalf_module] = ''; + $collector->implementations[$hook][$on_behalf_module][$class][] = $method; + $collector->orderMap[$on_behalf_module][$class][$method]['hook'] = $hook; + } + else { + $collector->moduleImplements[$hook][$module] = ''; + $collector->implementations[$hook][$module][$class][] = $method; + $collector->orderMap[$module][$class][$method]['hook'] = $hook; + } + break; + + case HookAfter::class: + $collector->orderMap[$module][$class][$method]['after'] = $attribute->modules; + break; + + case HookBefore::class: + $collector->orderMap[$module][$class][$method]['before'] = $attribute->modules; + break; + + case HookFirst::class: + $collector->orderMap[$module][$class][$method]['first'] = 9999; + break; + + case HookLast::class: + $collector->orderMap[$module][$class][$method]['last'] = -9999; + break; + + case HookOrderGroup::class: + $collector->orderMap[$module][$class][$method]['sort'] = $attribute->group; + break; + } } } } @@ -147,7 +157,7 @@ public function process(ContainerBuilder $container): void { } $priority = 0; foreach ($moduleImplements as $module => $v) { - foreach ($collector->implementations[$hook][$module] as $class => $method_hooks) { + foreach ($collector->[$hook][$module] as $class => $method_hooks) { if ($container->has($class)) { $definition = $container->findDefinition($class); } @@ -169,28 +179,28 @@ public function process(ContainerBuilder $container): void { } $container->setParameter('hook_implementations_map', $map); - foreach ($this->orderMap as $module => $classes) { + foreach ($collector->orderMap as $module => $classes) { foreach ($classes as $class => $methods) { foreach ($methods as $method => $actions) { - $hook = $this->orderMap[$module][$class][$method]['hook']; + $hook = $collector->orderMap[$module][$class][$method]['hook']; foreach ($actions as $action => $others) { switch ($action) { case 'first': - $this->changePriority($container, $hook, "$class::$method", TRUE); + $collector->changePriority($container, $hook, "$class::$method", TRUE); break; case 'before': // @todo $others likely needs to be updated. - $this->changePriority($container, $hook, "$class::$method", TRUE, $others); + $collector->changePriority($container, $hook, "$class::$method", TRUE, $others); break; case 'after': // @todo $others likely needs to be updated. - $this->changePriority($container, $hook, "$class::$method", FALSE, $others); + $collector->changePriority($container, $hook, "$class::$method", FALSE, $others); break; case 'last': - $this->changePriority($container, $hook, "$class::$method", FALSE); + $collector->changePriority($container, $hook, "$class::$method", FALSE); break; } } @@ -281,9 +291,13 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $class = str_replace('/', '\\', $class); if (class_exists($class)) { $reflectionClass = new \ReflectionClass($class); - $attributes['__invoke'] = array_map(fn ($x) => $x->newInstance(), $reflectionClass->getAttributes()); + if ($class_attributes = $reflectionClass->getAttributes()) { + $attributes['__invoke'] = array_map(fn ($x) => $x->newInstance(), $class_attributes); + } foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodName => $methodReflection) { - $attributes[$methodName] = array_map(fn ($x) => $x->newInstance(), $methodReflection->getAttributes()); + if ($method_attributes = $methodReflection->getAttributes()) { + $attributes[$methodReflection->getName()] = array_map(fn ($x) => $x->newInstance(), $method_attributes); + } } $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } @@ -291,7 +305,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $attributes = []; } } - $this->moduleAttributes[$module][$class] = array_merge($this->moduleAttributes[$module][$class] ?? [], $attributes); + $this->moduleAttributes[$module][$class] = $attributes; } elseif (!$skip_procedural) { $implementations = $procedural_hook_file_cache->get($filename); -- GitLab From 45ab6a10ddb1749e396a14ae19c96e4217f8ad24 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 9 Dec 2024 22:47:28 -0500 Subject: [PATCH 010/268] missed implementations --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index fa9044bac5a3..67ac0f43fc0d 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -157,7 +157,7 @@ public function process(ContainerBuilder $container): void { } $priority = 0; foreach ($moduleImplements as $module => $v) { - foreach ($collector->[$hook][$module] as $class => $method_hooks) { + foreach ($collector->implementations[$hook][$module] as $class => $method_hooks) { if ($container->has($class)) { $definition = $container->findDefinition($class); } -- GitLab From 4968e3860fb80138c83dd48158fd87f9f1794996 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 9 Dec 2024 22:48:34 -0500 Subject: [PATCH 011/268] CS --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 67ac0f43fc0d..dbe40fd31e2c 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -294,7 +294,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, if ($class_attributes = $reflectionClass->getAttributes()) { $attributes['__invoke'] = array_map(fn ($x) => $x->newInstance(), $class_attributes); } - foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodName => $methodReflection) { + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodReflection) { if ($method_attributes = $methodReflection->getAttributes()) { $attributes[$methodReflection->getName()] = array_map(fn ($x) => $x->newInstance(), $method_attributes); } -- GitLab From 94f27b18897ff528a805d04bf2fd0d561cde444e Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 09:33:04 -0500 Subject: [PATCH 012/268] HookOrderPriority --- .../Drupal/Core/Hook/Attribute/HookAfter.php | 8 +- .../Drupal/Core/Hook/Attribute/HookBefore.php | 8 +- .../Drupal/Core/Hook/Attribute/HookFirst.php | 8 +- .../Drupal/Core/Hook/Attribute/HookLast.php | 8 +- .../Core/Hook/Attribute/HookOrderGroup.php | 7 +- .../Drupal/Core/Hook/HookCollectorPass.php | 213 +++--------------- core/lib/Drupal/Core/Hook/HookPriority.php | 124 ++++++++++ core/modules/ckeditor5/ckeditor5.module | 25 -- .../ckeditor5/src/Hook/Ckeditor5Hooks.php | 13 ++ 9 files changed, 202 insertions(+), 212 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/HookPriority.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index 9763e1cb1fd5..3f630808c0e0 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -4,6 +4,8 @@ namespace Drupal\Core\Hook\Attribute; +use Drupal\Core\Hook\HookPriority; + /** * Attribute for marking that a hook should be changed. * @@ -21,7 +23,7 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookAfter { +class HookAfter implements HookOrderInterface { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. @@ -33,4 +35,8 @@ public function __construct( public array $modules, ) {} + public function getOrderAction(string $hook, string $class, string $method): \Closure { + return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", FALSE, $this->modules); + } + } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index 73bcf62c3e9e..229598f63173 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -4,6 +4,8 @@ namespace Drupal\Core\Hook\Attribute; +use Drupal\Core\Hook\HookPriority; + /** * Attribute for marking that a hook should be changed. * @@ -21,7 +23,7 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookBefore { +class HookBefore implements HookOrderInterface { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookBefore.php attribute object. @@ -33,4 +35,8 @@ public function __construct( public array $modules, ) {} + public function getOrderAction(string $hook, string $class, string $method): \Closure { + return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", TRUE, $this->modules); + } + } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php index 21c33c4cdd6a..a7957339fb07 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php @@ -4,6 +4,8 @@ namespace Drupal\Core\Hook\Attribute; +use Drupal\Core\Hook\HookPriority; + /** * Attribute for marking that a hook should be executed first. * @@ -18,11 +20,15 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookFirst { +class HookFirst implements HookOrderInterface { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookFirst.php attribute object. */ public function __construct() {} + public function getOrderAction(string $hook, string $class, string $method): \Closure { + return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", TRUE); + } + } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php index 4269b67c2291..70b21fc530b0 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php @@ -4,6 +4,8 @@ namespace Drupal\Core\Hook\Attribute; +use Drupal\Core\Hook\HookPriority; + /** * Attribute for marking that a hook should be executed last. * @@ -18,11 +20,15 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookLast { +class HookLast implements HookOrderInterface { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookLast.php attribute object. */ public function __construct() {} + public function getOrderAction(string $hook, string $class, string $method): \Closure { + return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", FALSE); + } + } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php index d011996e6803..346a78ee7e67 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php @@ -24,10 +24,13 @@ class HookOrderGroup { /** - * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. + * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php attribute object. * * @param array $group - * The group of implementations to change. + * A list of hooks to sort together. For example, if a method implementing + * form_BASE_FORM_ID_alter wants to sort itself relative to some + * implementations of form_FORM_ID_alter then this would contain those. + * See Ckeditor5::formFilterFormatFormAlter() for example. */ public function __construct( public array $group, diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index dbe40fd31e2c..82fbbb7b8200 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -9,11 +9,8 @@ use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookAfter; -use Drupal\Core\Hook\Attribute\HookBefore; -use Drupal\Core\Hook\Attribute\HookFirst; -use Drupal\Core\Hook\Attribute\HookLast; use Drupal\Core\Hook\Attribute\HookOrderGroup; +use Drupal\Core\Hook\Attribute\HookOrderInterface; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -81,11 +78,6 @@ class HookCollectorPass implements CompilerPassInterface { */ protected array $moduleAttributes = []; - /** - * An organized list of hooks to reorder. - */ - protected array $orderMap = []; - /** * {@inheritdoc} */ @@ -104,47 +96,39 @@ public function process(ContainerBuilder $container): void { } $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); + $orderGroups = []; + /** @var \Closure[] $orderActions */ + $orderActions = []; foreach ($collector->moduleAttributes as $module => $classes) { foreach ($classes as $class => $methods) { foreach ($methods as $method => $attributes) { + $orderAttributes = []; + $orderGroup = FALSE; + $hook = FALSE; foreach ($attributes as $attribute) { - if ($attribute) { - switch (get_class($attribute)) { - case Hook::class: - $hook = $attribute->hook; - self::checkForProceduralOnlyHooks($attribute, $class); - if ($on_behalf_module = $attribute->module) { - $collector->moduleImplements[$hook][$on_behalf_module] = ''; - $collector->implementations[$hook][$on_behalf_module][$class][] = $method; - $collector->orderMap[$on_behalf_module][$class][$method]['hook'] = $hook; - } - else { - $collector->moduleImplements[$hook][$module] = ''; - $collector->implementations[$hook][$module][$class][] = $method; - $collector->orderMap[$module][$class][$method]['hook'] = $hook; - } - break; - - case HookAfter::class: - $collector->orderMap[$module][$class][$method]['after'] = $attribute->modules; - break; - - case HookBefore::class: - $collector->orderMap[$module][$class][$method]['before'] = $attribute->modules; - break; - - case HookFirst::class: - $collector->orderMap[$module][$class][$method]['first'] = 9999; - break; - - case HookLast::class: - $collector->orderMap[$module][$class][$method]['last'] = -9999; - break; - - case HookOrderGroup::class: - $collector->orderMap[$module][$class][$method]['sort'] = $attribute->group; - break; + if ($attribute instanceof Hook) { + self::checkForProceduralOnlyHooks($attribute, $class); + $hook = $attribute->hook; + $hookModule = $attribute->module ?: $module; + if ($attribute->method) { + $method = $attribute->method; } + $collector->moduleImplements[$hook][$hookModule] = ''; + $collector->implementations[$hook][$hookModule][$class][] = $method; + } + if ($attribute instanceof HookOrderInterface) { + $orderAttributes[] = $attribute; + } + if ($attribute instanceof HookOrderGroup) { + $orderGroup = $attribute->group; + } + } + if ($hook) { + foreach ($orderAttributes as $orderAttribute) { + $orderActions[] = $orderAttribute->getOrderAction($hook, $class, $method); + } + if ($orderGroup) { + $orderGroups[] = array_merge($orderGroup, [$hook]); } } } @@ -179,33 +163,9 @@ public function process(ContainerBuilder $container): void { } $container->setParameter('hook_implementations_map', $map); - foreach ($collector->orderMap as $module => $classes) { - foreach ($classes as $class => $methods) { - foreach ($methods as $method => $actions) { - $hook = $collector->orderMap[$module][$class][$method]['hook']; - foreach ($actions as $action => $others) { - switch ($action) { - case 'first': - $collector->changePriority($container, $hook, "$class::$method", TRUE); - break; - - case 'before': - // @todo $others likely needs to be updated. - $collector->changePriority($container, $hook, "$class::$method", TRUE, $others); - break; - - case 'after': - // @todo $others likely needs to be updated. - $collector->changePriority($container, $hook, "$class::$method", FALSE, $others); - break; - - case 'last': - $collector->changePriority($container, $hook, "$class::$method", FALSE); - break; - } - } - } - } + $hookPriority = new HookPriority($container, $orderGroups); + foreach ($orderActions as $orderAction) { + $orderAction($hookPriority); } } @@ -445,113 +405,4 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v } } - /** - * Change the priority of a hook implementation. - * - * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container - * The container builder. - * @param string $hook - * The name of the hook. - * @param string $class_and_method - * Class and method separated by :: containing the hook implementation which - * should be changed. - * @param bool $should_be_larger - * TRUE for before/first, FALSE for after/last. Larger priority listeners - * fire first. - * @param array|null $others - * Other hook implementations to compare to, if any. The array is keyed by - * string containing a class and method separated by ::, the value is not - * used. - * - * @return void - */ - protected function changePriority(ContainerBuilder $container, string $hook, string $class_and_method, bool $should_be_larger, ?array $others = NULL): void { - // @todo clean this up. - // $events = $this->orderGroup[$hook] ?? ["drupal_hook.$hook"]; - $events = ["drupal_hook.$hook"]; - foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) { - foreach ($attributes as $key => $tag) { - if (in_array($tag['event'], $events)) { - $index = "$id.$key"; - $priority = $tag['priority']; - // Symfony documents event listener priorities to be integers, - // HookCollectorPass sets them to be integers, ::setPriority() only - // accepts integers. - assert(is_int($priority)); - $priorities[$index] = $priority; - $specifier = "$id::" . $tag['method']; - if ($class_and_method === $specifier) { - $index_this = $index; - } - // If $others is specified by ::before() / ::after() then for - // comparison only the priority of those matter. - // For ::first() / ::last() the priority of every other hook - // matters. - elseif (!isset($others) || isset($others[$specifier])) { - $priorities_other[] = $priority; - } - } - } - } - if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { - return; - } - // The priority of the hook being changed. - $priority_this = $priorities[$index_this]; - // The priority of the hook being compared to. - $priority_other = $should_be_larger ? max($priorities_other) : min($priorities_other); - // If the order is correct there is nothing to do. If the two priorities - // are the same then the order is undefined and so it can't be correct. - // If they are not the same and $priority_this is already larger exactly - // when $should_be_larger says then it's the correct order. - if ($priority_this !== $priority_other && ($should_be_larger === ($priority_this > $priority_other))) { - return; - } - $priority_new = $priority_other + ($should_be_larger ? 1 : -1); - // For ::first() / ::last() this new priority is already larger/smaller - // than all existing priorities but for ::before() / ::after() it might - // belong to an already existing hook. In this case set the new priority - // temporarily to be halfway between $priority_other and $priority_new - // then give all hook implementations new, integer priorities keeping this - // new order. This ensures the hook implementation being changed is in the - // right order relative to both $priority_other and the hook whose - // priority was $priority_new. - if (in_array($priority_new, $priorities)) { - $priorities[$index_this] = $priority_other + ($should_be_larger ? 0.5 : -0.5); - asort($priorities); - $changed_indexes = array_keys($priorities); - $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); - } - else { - $priorities[$index_this] = $priority_new; - $changed_indexes = [$index_this]; - } - foreach ($changed_indexes as $index) { - [$id, $key] = explode('.', $index); - self::setPriority($container, $id, (int) $key, $priorities[$index]); - } - } - - /** - * Set the priority of a listener. - * - * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container - * The container. - * @param string $class - * The name of the class, this is the same as the service id. - * @param int $key - * The key within the tags array of the 'kernel.event_listener' tag for the - * hook implementation to be changed. - * @param int $priority - * The new priority. - * - * @return void - */ - public static function setPriority(ContainerBuilder $container, string $class, int $key, int $priority): void { - $definition = $container->getDefinition($class); - $tags = $definition->getTags(); - $tags['kernel.event_listener'][$key]['priority'] = $priority; - $definition->setTags($tags); - } - } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php new file mode 100644 index 000000000000..f34da2273b5e --- /dev/null +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -0,0 +1,124 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class HookPriority { + + public function __construct(protected ContainerBuilder $container, protected array $orderGroups) {} + + /** + * Change the priority of a hook implementation. + * + * @param string $hook + * The name of the hook. + * @param string $class_and_method + * Class and method separated by :: containing the hook implementation which + * should be changed. + * @param bool $should_be_larger + * TRUE for before/first, FALSE for after/last. Larger priority listeners + * fire first. + * @param array|null $others + * Other hook implementations to compare to, if any. The array is a list of + * strings containing a class and method separated by ::. + * + * @return void + */ + public function change(string $hook, string $class_and_method, bool $should_be_larger, ?array $others = NULL): void { + $events = ["drupal_hook.$hook"]; + foreach ($this->orderGroups as $group) { + if (in_array($hook, $group)) { + foreach ($group as $alsoHook) { + $events[] = "drupal_hook.$alsoHook"; + } + } + } + $events = array_unique($events); + foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) { + foreach ($attributes as $key => $tag) { + if (in_array($tag['event'], $events)) { + $index = "$id.$key"; + $priority = $tag['priority']; + // Symfony documents event listener priorities to be integers, + // HookCollectorPass sets them to be integers, ::setPriority() only + // accepts integers. + assert(is_int($priority)); + $priorities[$index] = $priority; + $specifier = "$id::" . $tag['method']; + if ($class_and_method === $specifier) { + $index_this = $index; + } + // $others is specified for before and after, for these compare only + // the priority of those. For first and last the priority of every + // other hook matters. + elseif (!isset($others) || in_array($specifier, $others)) { + $priorities_other[] = $priority; + } + } + } + } + if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { + return; + } + // The priority of the hook being changed. + $priority_this = $priorities[$index_this]; + // The priority of the hook being compared to. + $priority_other = $should_be_larger ? max($priorities_other) : min($priorities_other); + // If the order is correct there is nothing to do. If the two priorities + // are the same then the order is undefined and so it can't be correct. + // If they are not the same and $priority_this is already larger exactly + // when $should_be_larger says then it's the correct order. + if ($priority_this !== $priority_other && ($should_be_larger === ($priority_this > $priority_other))) { + return; + } + $priority_new = $priority_other + ($should_be_larger ? 1 : -1); + // For first and last this new priority is already larger/smaller + // than all existing priorities but for before / after it might belong to + // an already existing hook. In this case set the new priority temporarily + // to be halfway between $priority_other and $priority_new then give all + // hook implementations new, integer priorities keeping this new order. + // This ensures the hook implementation being changed is in the right order + // relative to both $priority_other and the hook whose priority was + // $priority_new. + if (in_array($priority_new, $priorities)) { + $priorities[$index_this] = $priority_other + ($should_be_larger ? 0.5 : -0.5); + asort($priorities); + $changed_indexes = array_keys($priorities); + $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); + } + else { + $priorities[$index_this] = $priority_new; + $changed_indexes = [$index_this]; + } + foreach ($changed_indexes as $index) { + [$id, $key] = explode('.', $index); + self::set($this->container, $id, (int) $key, $priorities[$index]); + } + } + + /** + * Set the priority of a listener. + * + * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container + * The container. + * @param string $class + * The name of the class, this is the same as the service id. + * @param int $key + * The key within the tags array of the 'kernel.event_listener' tag for the + * hook implementation to be changed. + * @param int $priority + * The new priority. + * + * @return void + */ + public static function set(ContainerBuilder $container, string $class, int $key, int $priority): void { + $definition = $container->getDefinition($class); + $tags = $definition->getTags(); + $tags['kernel.event_listener'][$key]['priority'] = $priority; + $definition->setTags($tags); + } + +} diff --git a/core/modules/ckeditor5/ckeditor5.module b/core/modules/ckeditor5/ckeditor5.module index 759900cda00c..86be6f62b247 100644 --- a/core/modules/ckeditor5/ckeditor5.module +++ b/core/modules/ckeditor5/ckeditor5.module @@ -17,31 +17,6 @@ use Drupal\Core\Ajax\RemoveCommand; use Drupal\Core\Form\FormStateInterface; -/** - * Implements hook_module_implements_alter(). - */ -function ckeditor5_module_implements_alter(&$implementations, $hook): void { - // This module's implementation of form_filter_format_form_alter() must happen - // after the editor module's implementation, as that implementation adds the - // active editor to $form_state. It must also happen after the media module's - // implementation so media_filter_format_edit_form_validate can be removed - // from the validation chain, as that validator is not needed with CKEditor 5 - // and will trigger a false error. - if ($hook === 'form_alter' && isset($implementations['ckeditor5']) && isset($implementations['editor'])) { - $group = $implementations['ckeditor5']; - unset($implementations['ckeditor5']); - - $offset = array_search('editor', array_keys($implementations)) + 1; - if (array_key_exists('media', $implementations)) { - $media_offset = array_search('media', array_keys($implementations)) + 1; - $offset = max([$offset, $media_offset]); - } - $implementations = array_slice($implementations, 0, $offset, TRUE) + - ['ckeditor5' => $group] + - array_slice($implementations, $offset, NULL, TRUE); - } -} - /** * Form submission handler for filter format forms. */ diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index 85ead6ae51dd..519e23396240 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -2,6 +2,8 @@ namespace Drupal\ckeditor5\Hook; +use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\HookOrderGroup; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Render\Element; @@ -102,6 +104,17 @@ public function theme() : array { * Implements hook_form_FORM_ID_alter(). */ #[Hook('form_filter_format_form_alter')] + + /** + * This module's implementation of form_filter_format_form_alter() must happen + *after the editor module's implementation, as that implementation adds the + *active editor to $form_state. It must also happen after the media module's + *implementation so media_filter_format_edit_form_validate can be removed + *from the validation chain, as that validator is not needed with CKEditor 5 + *and will trigger a false error. + */ + #[HookOrderGroup(['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'])] + #[HookAfter(['editor', 'media'])] public function formFilterFormatFormAlter(array &$form, FormStateInterface $form_state, $form_id) : void { $editor = $form_state->get('editor'); // CKEditor 5 plugin config determines the available HTML tags. If an HTML -- GitLab From 624fc7a179b85b06e30f5652f69ba1d486e723e0 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 09:46:38 -0500 Subject: [PATCH 013/268] CS and stan --- .../Core/Hook/Attribute/HookOrderInterface.php | 11 +++++++++++ .../ckeditor5/src/Hook/Ckeditor5Hooks.php | 16 +++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php new file mode 100644 index 000000000000..2fc1b7219115 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -0,0 +1,11 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +interface HookOrderInterface { + + public function getOrderAction(string $hook, string $class, string $method): \Closure; + +} diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index 519e23396240..fd6c2dc3cb16 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -102,17 +102,15 @@ public function theme() : array { /** * Implements hook_form_FORM_ID_alter(). + * + * This module's implementation of form_filter_format_form_alter() must + * happen after the editor module's implementation, as that implementation + * adds the active editor to $form_state. It must also happen after the media + * module's implementation so media_filter_format_edit_form_validate can be + * removed from the validation chain, as that validator is not needed with + * CKEditor 5 and will trigger a false error. */ #[Hook('form_filter_format_form_alter')] - - /** - * This module's implementation of form_filter_format_form_alter() must happen - *after the editor module's implementation, as that implementation adds the - *active editor to $form_state. It must also happen after the media module's - *implementation so media_filter_format_edit_form_validate can be removed - *from the validation chain, as that validator is not needed with CKEditor 5 - *and will trigger a false error. - */ #[HookOrderGroup(['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'])] #[HookAfter(['editor', 'media'])] public function formFilterFormatFormAlter(array &$form, FormStateInterface $form_state, $form_id) : void { -- GitLab From b6240cff2306a225067f85f8aa9a08999a929504 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 09:52:37 -0500 Subject: [PATCH 014/268] Stan --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 82fbbb7b8200..7403541e4f98 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -239,8 +239,6 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, } if ($extension === 'php') { $cached = $hook_file_cache->get($filename); - // @todo remove this comment. - // $cached = FALSE; if ($cached) { $class = $cached['class']; $attributes = $cached['attributes']; @@ -249,6 +247,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $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); if ($class_attributes = $reflectionClass->getAttributes()) { @@ -261,9 +260,6 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, } $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } - else { - $attributes = []; - } } $this->moduleAttributes[$module][$class] = $attributes; } @@ -351,7 +347,6 @@ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $h $this->hookInfo[] = $function; } if ($hook === 'module_implements_alter') { - // @todo confirm this is skipped when #[LegacyHook] should be. $this->moduleImplementsAlters[] = $function; } if ($fileinfo->getExtension() !== 'module') { -- GitLab From efd11a6f6689cd8b5229b7e5c385f233abc2e25e Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 11:13:37 -0500 Subject: [PATCH 015/268] Procedural hooks --- .../Drupal/Core/Hook/HookCollectorPass.php | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 7403541e4f98..2fd07bed0d8a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -38,15 +38,6 @@ class HookCollectorPass implements CompilerPassInterface { */ protected array $implementations = []; - /** - * An associative array of hook implementations. - * - * Keys are hook, module and an empty string value. - * - * @see hook_module_implements_alter() - */ - protected array $moduleImplements = []; - /** * A list of include files. * @@ -99,21 +90,23 @@ public function process(ContainerBuilder $container): void { $orderGroups = []; /** @var \Closure[] $orderActions */ $orderActions = []; - foreach ($collector->moduleAttributes as $module => $classes) { - foreach ($classes as $class => $methods) { + foreach (array_keys($container->getParameter('container.modules')) as $module) { + foreach ($collector->moduleAttributes[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $attributes) { $orderAttributes = []; $orderGroup = FALSE; $hook = FALSE; foreach ($attributes as $attribute) { if ($attribute instanceof Hook) { - self::checkForProceduralOnlyHooks($attribute, $class); + if ($class !== ProceduralCall::class) { + self::checkForProceduralOnlyHooks($attribute, $class); + } $hook = $attribute->hook; $hookModule = $attribute->module ?: $module; if ($attribute->method) { $method = $attribute->method; } - $collector->moduleImplements[$hook][$hookModule] = ''; + $moduleImplements[$hook][$hookModule] = ''; $collector->implementations[$hook][$hookModule][$class][] = $method; } if ($attribute instanceof HookOrderInterface) { @@ -135,7 +128,7 @@ public function process(ContainerBuilder $container): void { } } - foreach ($collector->moduleImplements as $hook => $moduleImplements) { + foreach ($moduleImplements ?? [] as $hook => $moduleImplements) { foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } @@ -311,24 +304,6 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv return in_array($extension, ['inc', 'module', 'profile', 'install']); } - /** - * Adds a Hook attribute implementation. - * - * @param \Drupal\Core\Hook\Attribute\Hook $hook - * A hook attribute. - * @param string $class - * The class in which said attribute resides in. - * @param string $module - * The module in which the class resides in. - */ - protected function addFromAttribute(Hook $hook, $class, $module): void { - if ($hook->module) { - $module = $hook->module; - } - $this->moduleImplements[$hook->hook][$module] = ''; - $this->implementations[$hook->hook][$module][$class][] = $hook->method; - } - /** * Adds a procedural hook implementation. * @@ -342,7 +317,7 @@ protected function addFromAttribute(Hook $hook, $class, $module): void { * The name of function implementing the hook. (Wow!) */ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { - $this->addFromAttribute(new Hook($hook, $module . '_' . $hook), ProceduralCall::class, $module); + $this->moduleAttributes[$module][ProceduralCall::class][$function] = [new Hook($hook, $module . '_' . $hook)]; if ($hook === 'hook_info') { $this->hookInfo[] = $function; } -- GitLab From 6c90e5bf14f08852bae342e92031813f2a87e1aa Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 14:06:04 -0500 Subject: [PATCH 016/268] Fix implements on behalf --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 2fd07bed0d8a..035693858f7f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -213,7 +213,6 @@ public static function collectAllHookImplementations(array $module_filenames, ?C protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural): void { $hook_file_cache = FileCacheFactory::get('hook_implementations'); $procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $module_preg); - $this->moduleAttributes[$module] = []; $iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::FOLLOW_SYMLINKS); $iterator = new \RecursiveCallbackFilterIterator($iterator, static::filterIterator(...)); @@ -308,16 +307,17 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv * Adds a procedural hook implementation. * * @param \SplFileInfo $fileinfo - * The file this procedural implementation is in. (You don't say) + * The file this procedural implementation is in. * @param string $hook - * The name of the hook. (Huh, right?) - * @param string $module - * The name of the module. (Truly shocking!) + * The name of the hook. + * @param string $hookModule + * The name of the module this hook implementation belongs to. It can be + * different to the file where $function is in. * @param string $function - * The name of function implementing the hook. (Wow!) + * The name of function implementing the hook. */ - protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { - $this->moduleAttributes[$module][ProceduralCall::class][$function] = [new Hook($hook, $module . '_' . $hook)]; + protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $hookModule, string $function): void { + $this->moduleAttributes[$hookModule][ProceduralCall::class][$function] = [new Hook($hook, $hookModule . '_' . $hook)]; if ($hook === 'hook_info') { $this->hookInfo[] = $function; } -- GitLab From 85e6895597c7003c55e6cfe5ba102592fcda0746 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 14:33:26 -0500 Subject: [PATCH 017/268] Clean up service registration and process --- .../Drupal/Core/Hook/HookCollectorPass.php | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 035693858f7f..556203d0eeb0 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -74,19 +74,6 @@ class HookCollectorPass implements CompilerPassInterface { */ public function process(ContainerBuilder $container): void { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); - $map = []; - $container->register(ProceduralCall::class, ProceduralCall::class) - ->addArgument($collector->includes); - $groupIncludes = []; - foreach ($collector->hookInfo as $function) { - foreach ($function() as $hook => $info) { - if (isset($collector->groupIncludes[$info['group']])) { - $groupIncludes[$hook] = $collector->groupIncludes[$info['group']]; - } - } - } - $definition = $container->getDefinition('module_handler'); - $definition->setArgument('$groupIncludes', $groupIncludes); $orderGroups = []; /** @var \Closure[] $orderActions */ $orderActions = []; @@ -128,7 +115,35 @@ public function process(ContainerBuilder $container): void { } } - foreach ($moduleImplements ?? [] as $hook => $moduleImplements) { + $this->registerServices($container, $collector, $moduleImplements ?? []); + + $hookPriority = new HookPriority($container, $orderGroups); + foreach ($orderActions as $orderAction) { + $orderAction($hookPriority); + } + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Drupal\Core\Hook\HookCollectorPass $collector + * @param array $allModuleImplements + * + * @return void + */ + protected function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $allModuleImplements): void { + $container->register(ProceduralCall::class, ProceduralCall::class) + ->addArgument($collector->includes); + $groupIncludes = []; + foreach ($collector->hookInfo as $function) { + foreach ($function() as $hook => $info) { + if (isset($collector->groupIncludes[$info['group']])) { + $groupIncludes[$hook] = $collector->groupIncludes[$info['group']]; + } + } + } + $definition = $container->getDefinition('module_handler'); + $definition->setArgument('$groupIncludes', $groupIncludes); + foreach ($allModuleImplements as $hook => $moduleImplements) { foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } @@ -154,12 +169,7 @@ public function process(ContainerBuilder $container): void { } } } - $container->setParameter('hook_implementations_map', $map); - - $hookPriority = new HookPriority($container, $orderGroups); - foreach ($orderActions as $orderAction) { - $orderAction($hookPriority); - } + $container->setParameter('hook_implementations_map', $map ?? []); } /** -- GitLab From 13fd65cca6fb74e1cb62c880f441b4862a968191 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 15:19:44 -0500 Subject: [PATCH 018/268] Refactor hook ordering --- .../Drupal/Core/Hook/Attribute/HookAfter.php | 10 ++-- .../Drupal/Core/Hook/Attribute/HookBefore.php | 10 ++-- .../Drupal/Core/Hook/Attribute/HookFirst.php | 9 ++- .../Drupal/Core/Hook/Attribute/HookLast.php | 8 +-- .../Core/Hook/Attribute/HookOrderBase.php | 34 +++++++++++ .../Hook/Attribute/HookOrderInterface.php | 4 +- .../Drupal/Core/Hook/HookCollectorPass.php | 57 +++++++++++++++---- core/lib/Drupal/Core/Hook/HookPriority.php | 22 ++----- 8 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index 3f630808c0e0..161bdd51c1ca 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -23,7 +23,7 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookAfter implements HookOrderInterface { +class HookAfter extends HookOrderBase { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. @@ -32,11 +32,9 @@ class HookAfter implements HookOrderInterface { * The module this implementation should run before. */ public function __construct( - public array $modules, - ) {} - - public function getOrderAction(string $hook, string $class, string $method): \Closure { - return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", FALSE, $this->modules); + public readonly array $modules, + ) { + parent::__construct(FALSE); } } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index 229598f63173..1fb10ba31258 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -23,7 +23,7 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookBefore implements HookOrderInterface { +class HookBefore extends HookOrderBase { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookBefore.php attribute object. @@ -32,11 +32,9 @@ class HookBefore implements HookOrderInterface { * The module this implementation should run before. */ public function __construct( - public array $modules, - ) {} - - public function getOrderAction(string $hook, string $class, string $method): \Closure { - return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", TRUE, $this->modules); + public readonly array $modules, + ) { + parent::__construct(TRUE); } } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php index a7957339fb07..f6597196e793 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php @@ -20,15 +20,14 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookFirst implements HookOrderInterface { +class HookFirst extends HookOrderBase { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookFirst.php attribute object. */ - public function __construct() {} - - public function getOrderAction(string $hook, string $class, string $method): \Closure { - return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", TRUE); + public function __construct() { + parent::__construct(TRUE); } + } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php index 70b21fc530b0..eeca6e3e8c00 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php @@ -20,15 +20,13 @@ * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookLast implements HookOrderInterface { +class HookLast extends HookOrderBase { /** * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookLast.php attribute object. */ - public function __construct() {} - - public function getOrderAction(string $hook, string $class, string $method): \Closure { - return fn(HookPriority $hookPriority) => $hookPriority->change($hook, "$class::$method", FALSE); + public function __construct() { + parent::__construct(FALSE); } } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php new file mode 100644 index 000000000000..d17417dfcbf1 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +class HookOrderBase implements HookOrderInterface { + + public readonly string $hook; + + public readonly string $class; + + public readonly string $method; + + public function __construct(public readonly bool $shouldBeLarger) { + + } + + public function setHook(string $hook): static { + $this->hook = $hook; + return $this; + } + + public function setClass(string $class): static { + $this->class = $class; + return $this; + } + + public function setMethod(string $method): static { + $this->method = $method; + return $this; + } + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php index 2fc1b7219115..5080620d959c 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -6,6 +6,8 @@ interface HookOrderInterface { - public function getOrderAction(string $hook, string $class, string $method): \Closure; + public function setHook(string $hook): static; + public function setClass(string $class): static; + public function setMethod(string $method): static; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 556203d0eeb0..591618310116 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -75,8 +75,8 @@ class HookCollectorPass implements CompilerPassInterface { public function process(ContainerBuilder $container): void { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); $orderGroups = []; - /** @var \Closure[] $orderActions */ - $orderActions = []; + /** @var \Drupal\Core\Hook\Attribute\HookOrderBase[] $allOrderAttributes */ + $allOrderAttributes = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleAttributes[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $attributes) { @@ -105,7 +105,10 @@ public function process(ContainerBuilder $container): void { } if ($hook) { foreach ($orderAttributes as $orderAttribute) { - $orderActions[] = $orderAttribute->getOrderAction($hook, $class, $method); + $allOrderAttributes[] = $orderAttribute + ->setHook($hook) + ->setClass($class) + ->setMethod($method); } if ($orderGroup) { $orderGroups[] = array_merge($orderGroup, [$hook]); @@ -115,12 +118,8 @@ public function process(ContainerBuilder $container): void { } } - $this->registerServices($container, $collector, $moduleImplements ?? []); - - $hookPriority = new HookPriority($container, $orderGroups); - foreach ($orderActions as $orderAction) { - $orderAction($hookPriority); - } + static::registerServices($container, $collector, $moduleImplements ?? []); + static::reOrderServices($container, $allOrderAttributes, $orderGroups, $collector->implementations); } /** @@ -130,7 +129,7 @@ public function process(ContainerBuilder $container): void { * * @return void */ - protected function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $allModuleImplements): void { + protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $allModuleImplements): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); $groupIncludes = []; @@ -172,6 +171,44 @@ protected function registerServices(ContainerBuilder $container, HookCollectorPa $container->setParameter('hook_implementations_map', $map ?? []); } + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param array $allOrderAttributes + * @param array $orderGroups + * @param array $implementations + * + * @return void + */ + protected static function reOrderServices(ContainerBuilder $container, array $allOrderAttributes, array $orderGroups, array $implementations): void { + $hookPriority = new HookPriority($container); + foreach ($allOrderAttributes as $orderAttribute) { + $hooks = [$orderAttribute->hook]; + foreach ($orderGroups as $group) { + if (in_array($orderAttribute->hook, $group)) { + $hooks = array_merge($hooks, $group); + } + } + $hooks = array_unique($hooks); + if (isset($orderAttribute->modules)) { + $others = []; + foreach ($orderAttribute->modules as $module) { + foreach ($hooks as $hook) { + foreach ($implementations[$hook][$module] as $class => $methods) { + foreach ($methods as $method) { + $others[] = "$class::$method"; + } + } + } + } + } + else { + $others = NULL; + } + $hookPriority->change($hooks, "$orderAttribute->class::$orderAttribute->method", $orderAttribute->shouldBeLarger, $others); + } + } + + /** * Collects all hook implementations. * diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index f34da2273b5e..82033db888d8 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -8,7 +8,7 @@ class HookPriority { - public function __construct(protected ContainerBuilder $container, protected array $orderGroups) {} + public function __construct(protected ContainerBuilder $container) {} /** * Change the priority of a hook implementation. @@ -27,16 +27,8 @@ public function __construct(protected ContainerBuilder $container, protected arr * * @return void */ - public function change(string $hook, string $class_and_method, bool $should_be_larger, ?array $others = NULL): void { - $events = ["drupal_hook.$hook"]; - foreach ($this->orderGroups as $group) { - if (in_array($hook, $group)) { - foreach ($group as $alsoHook) { - $events[] = "drupal_hook.$alsoHook"; - } - } - } - $events = array_unique($events); + public function change(array $hooks, string $class_and_method, bool $should_be_larger, ?array $others = NULL): void { + $events = array_map(fn ($hook) => "drupal_hook.$hook", $hooks); foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) { foreach ($attributes as $key => $tag) { if (in_array($tag['event'], $events)) { @@ -95,15 +87,13 @@ public function change(string $hook, string $class_and_method, bool $should_be_l } foreach ($changed_indexes as $index) { [$id, $key] = explode('.', $index); - self::set($this->container, $id, (int) $key, $priorities[$index]); + $this->set($id, (int) $key, $priorities[$index]); } } /** * Set the priority of a listener. * - * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container - * The container. * @param string $class * The name of the class, this is the same as the service id. * @param int $key @@ -114,8 +104,8 @@ public function change(string $hook, string $class_and_method, bool $should_be_l * * @return void */ - public static function set(ContainerBuilder $container, string $class, int $key, int $priority): void { - $definition = $container->getDefinition($class); + public function set(string $class, int $key, int $priority): void { + $definition = $this->container->getDefinition($class); $tags = $definition->getTags(); $tags['kernel.event_listener'][$key]['priority'] = $priority; $definition->setTags($tags); -- GitLab From d6acaeb6c7729fc03dc3f78ec12a462dd69db405 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 15:31:24 -0500 Subject: [PATCH 019/268] Fix cs --- core/lib/Drupal/Core/Hook/Attribute/HookAfter.php | 4 ++-- core/lib/Drupal/Core/Hook/Attribute/HookBefore.php | 4 ++-- core/lib/Drupal/Core/Hook/Attribute/HookFirst.php | 5 ++--- core/lib/Drupal/Core/Hook/Attribute/HookLast.php | 4 ++-- core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php | 2 ++ core/lib/Drupal/Core/Hook/HookCollectorPass.php | 1 - 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index 161bdd51c1ca..86af0eb1806d 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookPriority; +use Drupal\Core\Hook\Attribute\HookOrderBase; /** * Attribute for marking that a hook should be changed. @@ -26,7 +26,7 @@ class HookAfter extends HookOrderBase { /** - * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookAfter.php attribute object. + * Constructs a HookAfter attribute. * * @param array $modules * The module this implementation should run before. diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index 1fb10ba31258..5fbde2e1356d 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookPriority; +use Drupal\Core\Hook\Attribute\HookOrderBase; /** * Attribute for marking that a hook should be changed. @@ -26,7 +26,7 @@ class HookBefore extends HookOrderBase { /** - * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookBefore.php attribute object. + * Constructs a HookBefore attribute. * * @param array $modules * The module this implementation should run before. diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php index f6597196e793..36b8d21edd41 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookPriority; +use Drupal\Core\Hook\Attribute\HookOrderBase; /** * Attribute for marking that a hook should be executed first. @@ -23,11 +23,10 @@ class HookFirst extends HookOrderBase { /** - * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookFirst.php attribute object. + * Constructs a HookFirst attribute. */ public function __construct() { parent::__construct(TRUE); } - } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php index eeca6e3e8c00..ae509b890b94 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookPriority; +use Drupal\Core\Hook\Attribute\HookOrderBase; /** * Attribute for marking that a hook should be executed last. @@ -23,7 +23,7 @@ class HookLast extends HookOrderBase { /** - * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookLast.php attribute object. + * Constructs a HookLast attribute. */ public function __construct() { parent::__construct(FALSE); diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php index 5080620d959c..c5406a63801e 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -7,7 +7,9 @@ interface HookOrderInterface { public function setHook(string $hook): static; + public function setClass(string $class): static; + public function setMethod(string $method): static; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 591618310116..1b3e52035ffb 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -208,7 +208,6 @@ protected static function reOrderServices(ContainerBuilder $container, array $al } } - /** * Collects all hook implementations. * -- GitLab From 19208bf674286e6bd5cc280edb9ff2e77ae2e04e Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 15:33:06 -0500 Subject: [PATCH 020/268] Fix comments --- core/lib/Drupal/Core/Hook/Attribute/HookBefore.php | 2 +- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index 5fbde2e1356d..8d244c11d2f3 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -26,7 +26,7 @@ class HookBefore extends HookOrderBase { /** - * Constructs a HookBefore attribute. + * Constructs a HookBefore lib/Drupal/Core/Hook/Attribute/attribute. * * @param array $modules * The module this implementation should run before. diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 1b3e52035ffb..0e0f2b1d8074 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -124,8 +124,11 @@ public function process(ContainerBuilder $container): void { /** * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * The container. * @param \Drupal\Core\Hook\HookCollectorPass $collector + * The collector. * @param array $allModuleImplements + * Modules that implement hooks. * * @return void */ -- GitLab From 82d02e50ed47b92e21411454c5721accf0a0f4bc Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 15:56:53 -0500 Subject: [PATCH 021/268] Internal --- .../Core/Hook/Attribute/HookOrderBase.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php index d17417dfcbf1..752fcea02c71 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php @@ -6,11 +6,20 @@ class HookOrderBase implements HookOrderInterface { - public readonly string $hook; - - public readonly string $class; - - public readonly string $method; + /** + * @internal + */ + public string $hook; + + /** + * @internal + */ + public string $class; + + /** + * @internal + */ + public string $method; public function __construct(public readonly bool $shouldBeLarger) { -- GitLab From 5602c2ac1d6509c60e8d5417ab774d23c953755f Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 16:18:54 -0500 Subject: [PATCH 022/268] Ordering tests --- .../hook_order_first_alphabetically1.info.yml | 6 +++ .../src/Hook/FirstAlphabeticallyHooks1.php | 35 ++++++++++++++ .../hook_order_first_alphabetically2.info.yml | 6 +++ .../src/Hook/FirstAlphabeticallyHooks2.php | 36 +++++++++++++++ .../hook_order_last_alphabetically1.info.yml | 6 +++ .../src/Hook/LastAlphabeticallyHooks1.php | 46 +++++++++++++++++++ .../hook_order_last_alphabetically2.info.yml | 6 +++ .../src/Hook/LastAlphabeticallyHooks2.php | 38 +++++++++++++++ .../Core/Hook/HookCollectorPassTest.php | 38 +++++++++++++++ 9 files changed, 217 insertions(+) create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml new file mode 100644 index 000000000000..d61a2ddc1212 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml @@ -0,0 +1,6 @@ +name: first alphabetically +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php new file mode 100644 index 000000000000..142c377d686c --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically1\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookLast; + +/** + * Hook implementations for hook_order_first_alphabetically. + */ +class FirstAlphabeticallyHooks1 { + + /** + * After LastAlphabeticallyHooks1::cacheFlush1 + */ + #[Hook('cache_flush')] + public static function cacheFlush1(): void { + if(!isset($GLOBALS['HookFirst'])) { + $GLOBALS['HookOutOfOrderTestingFirst'] = 'HookOutOfOrderTestingFirst'; + } + $GLOBALS['HookRanTestingFirst'] = 'HookRanTestingFirst'; + } + + /** + * After LastAlphabeticallyHooks1::cacheFlush2 + */ + #[HookLast] + #[Hook('cache_flush')] + public static function cacheFlush2(): void { + $GLOBALS['HookLast'] = 'HookLast'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml new file mode 100644 index 000000000000..d61a2ddc1212 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml @@ -0,0 +1,6 @@ +name: first alphabetically +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php new file mode 100644 index 000000000000..14ff7cf0c961 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically2\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\HookOrderGroup; + +/** + * Hook implementations for hook_order_first_alphabetically. + */ +class FirstAlphabeticallyHooks2 { + /** + * After LastAlphabeticallyHooks2::cacheFlush1 + */ + #[HookAfter(['hook_order_last_alphabetically2'])] + #[HookOrderGroup(['cacheFlush1'])] + #[Hook('cache_flush')] + public static function cacheFlush1(): void { + $GLOBALS['HookAfter'] = 'HookAfter'; + } + + /** + * After LastAlphabeticallyHooks2::cacheFlush2 + */ + #[Hook('cache_flush')] + public static function cacheFlush2(): void { + if(!isset($GLOBALS['HookBefore'])) { + $GLOBALS['HookOutOfOrderTestingBefore'] = 'HookOutOfOrderTestingBefore'; + } + $GLOBALS['HookRanTestingBefore'] = 'HookRanTestingBefore'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml new file mode 100644 index 000000000000..59334b38d2fc --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml @@ -0,0 +1,6 @@ +name: Hook ordering last +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php new file mode 100644 index 000000000000..bd19a4b7e1b1 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically1\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookFirst; + +/** + * Hook implementations for hook_order_last_alphabetically. + */ +class LastAlphabeticallyHooks1 { + + /** + * Before FirstAlphabeticallyHooks1::cacheFlush1 + */ + #[HookFirst] + #[Hook('cache_flush')] + public static function cacheFlush1(): void { + $GLOBALS['HookFirst'] = 'HookFirst'; + } + + /** + * Before FirstAlphabeticallyHooks1::cacheFlush2 + */ + #[Hook('cache_flush')] + public static function cacheFlush2(): void { + if(isset($GLOBALS['HookLast'])) { + $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; + } + $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; + } + + /** + * Before FirstAlphabeticallyHooks::cacheFlush3 + */ + #[Hook('cache_flush')] + public static function cacheFlush3(): void { + if(isset($GLOBALS['HookLast'])) { + $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; + } + $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml new file mode 100644 index 000000000000..59334b38d2fc --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml @@ -0,0 +1,6 @@ +name: Hook ordering last +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php new file mode 100644 index 000000000000..1cc7ac3768a6 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php @@ -0,0 +1,38 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically2\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookBefore; +use Drupal\Core\Hook\Attribute\HookOrderGroup; + +/** + * Hook implementations for hook_order_last_alphabetically. + */ +class LastAlphabeticallyHooks2 { + + /** + * Before FirstAlphabeticallyHooks2::cacheFlush1 + */ + #[Hook('cache_flush')] + public static function cacheFlush1(): void { + // This should be run before so HookAfter should not be set. + if(isset($GLOBALS['HookAfter'])) { + $GLOBALS['HookOutOfOrderTestingAfter'] = 'HookOutOfOrderTestingAfter'; + } + $GLOBALS['HookRanTestingAfter'] = 'HookRanTestingAfter'; + } + + /** + * Before FirstAlphabeticallyHooks2::cacheFlush2 + */ + #[HookBefore(['hook_order_last_alphabetically2'])] + #[HookOrderGroup(['cacheFlush2'])] + #[Hook('cache_flush')] + public static function cacheFlush2(): void { + $GLOBALS['HookBefore'] = 'HookBefore'; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 4156481d3b92..2f0832a8f83d 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -121,7 +121,45 @@ public function testProceduralHooksSkippedWhenConfigured(): void { $this->assertFalse(isset($GLOBALS['procedural_attribute_skip_after_attribute'])); $this->assertTrue(isset($GLOBALS['procedural_attribute_skip_find'])); $this->assertTrue(isset($GLOBALS['skipped_procedural_oop_cache_flush'])); + } + /** + * Tests HookFirst. + */ + public function testHookFirst(): void { + $module_installer = $this->container->get('module_installer'); + $module_handler = $this->container->get('module_handler'); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically1'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically1'])); + $this->assertFalse(isset($GLOBALS['HookFirst'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingFirst'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingFirst'])); + $this->assertFalse(isset($GLOBALS['HookLast'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingLast'])); + drupal_flush_all_caches(); + $this->assertTrue(isset($GLOBALS['HookFirst'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingFirst'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingFirst'])); + $this->assertTrue(isset($GLOBALS['HookLast'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingLast'])); + + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically2'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically2'])); + $this->assertFalse(isset($GLOBALS['HookAfter'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingAfter'])); + $this->assertFalse(isset($GLOBALS['HookBefore'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingBefore'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingBefore'])); + drupal_flush_all_caches(); + $this->assertTrue(isset($GLOBALS['HookAfter'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingAfter'])); + $this->assertTrue(isset($GLOBALS['HookBefore'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingBefore'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingBefore'])); } /** -- GitLab From c7e3aeb64d118ccde074313a152896cbcaa690ba Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 16:23:02 -0500 Subject: [PATCH 023/268] CS and fix group --- core/lib/Drupal/Core/Hook/Attribute/HookAfter.php | 2 -- core/lib/Drupal/Core/Hook/Attribute/HookBefore.php | 2 -- core/lib/Drupal/Core/Hook/Attribute/HookFirst.php | 2 -- core/lib/Drupal/Core/Hook/Attribute/HookLast.php | 2 -- .../src/Hook/FirstAlphabeticallyHooks1.php | 6 +++--- .../src/Hook/FirstAlphabeticallyHooks2.php | 9 +++++---- .../src/Hook/LastAlphabeticallyHooks1.php | 10 +++++----- .../src/Hook/LastAlphabeticallyHooks2.php | 8 ++++---- 8 files changed, 17 insertions(+), 24 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index 86af0eb1806d..c1ee29d48ad9 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -4,8 +4,6 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\Attribute\HookOrderBase; - /** * Attribute for marking that a hook should be changed. * diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index 8d244c11d2f3..cdbef7f0e983 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -4,8 +4,6 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\Attribute\HookOrderBase; - /** * Attribute for marking that a hook should be changed. * diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php index 36b8d21edd41..774d74f0bb29 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php @@ -4,8 +4,6 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\Attribute\HookOrderBase; - /** * Attribute for marking that a hook should be executed first. * diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php index ae509b890b94..2f23ef11c055 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php @@ -4,8 +4,6 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\Attribute\HookOrderBase; - /** * Attribute for marking that a hook should be executed last. * diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php index 142c377d686c..d9e190560719 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php @@ -13,18 +13,18 @@ class FirstAlphabeticallyHooks1 { /** - * After LastAlphabeticallyHooks1::cacheFlush1 + * After LastAlphabeticallyHooks1::cacheFlush1. */ #[Hook('cache_flush')] public static function cacheFlush1(): void { - if(!isset($GLOBALS['HookFirst'])) { + if (!isset($GLOBALS['HookFirst'])) { $GLOBALS['HookOutOfOrderTestingFirst'] = 'HookOutOfOrderTestingFirst'; } $GLOBALS['HookRanTestingFirst'] = 'HookRanTestingFirst'; } /** - * After LastAlphabeticallyHooks1::cacheFlush2 + * After LastAlphabeticallyHooks1::cacheFlush2. */ #[HookLast] #[Hook('cache_flush')] diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php index 14ff7cf0c961..97e6124acae7 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php @@ -12,22 +12,23 @@ * Hook implementations for hook_order_first_alphabetically. */ class FirstAlphabeticallyHooks2 { + /** - * After LastAlphabeticallyHooks2::cacheFlush1 + * After LastAlphabeticallyHooks2::cacheFlush1. */ #[HookAfter(['hook_order_last_alphabetically2'])] - #[HookOrderGroup(['cacheFlush1'])] + #[HookOrderGroup(['cache_flush'])] #[Hook('cache_flush')] public static function cacheFlush1(): void { $GLOBALS['HookAfter'] = 'HookAfter'; } /** - * After LastAlphabeticallyHooks2::cacheFlush2 + * After LastAlphabeticallyHooks2::cacheFlush2. */ #[Hook('cache_flush')] public static function cacheFlush2(): void { - if(!isset($GLOBALS['HookBefore'])) { + if (!isset($GLOBALS['HookBefore'])) { $GLOBALS['HookOutOfOrderTestingBefore'] = 'HookOutOfOrderTestingBefore'; } $GLOBALS['HookRanTestingBefore'] = 'HookRanTestingBefore'; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php index bd19a4b7e1b1..c9b15dc39f21 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php @@ -13,7 +13,7 @@ class LastAlphabeticallyHooks1 { /** - * Before FirstAlphabeticallyHooks1::cacheFlush1 + * Before FirstAlphabeticallyHooks1::cacheFlush1. */ #[HookFirst] #[Hook('cache_flush')] @@ -22,22 +22,22 @@ public static function cacheFlush1(): void { } /** - * Before FirstAlphabeticallyHooks1::cacheFlush2 + * Before FirstAlphabeticallyHooks1::cacheFlush2. */ #[Hook('cache_flush')] public static function cacheFlush2(): void { - if(isset($GLOBALS['HookLast'])) { + if (isset($GLOBALS['HookLast'])) { $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; } $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; } /** - * Before FirstAlphabeticallyHooks::cacheFlush3 + * Before FirstAlphabeticallyHooks::cacheFlush3. */ #[Hook('cache_flush')] public static function cacheFlush3(): void { - if(isset($GLOBALS['HookLast'])) { + if (isset($GLOBALS['HookLast'])) { $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; } $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php index 1cc7ac3768a6..c0161c2a239d 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php @@ -14,22 +14,22 @@ class LastAlphabeticallyHooks2 { /** - * Before FirstAlphabeticallyHooks2::cacheFlush1 + * Before FirstAlphabeticallyHooks2::cacheFlush1. */ #[Hook('cache_flush')] public static function cacheFlush1(): void { // This should be run before so HookAfter should not be set. - if(isset($GLOBALS['HookAfter'])) { + if (isset($GLOBALS['HookAfter'])) { $GLOBALS['HookOutOfOrderTestingAfter'] = 'HookOutOfOrderTestingAfter'; } $GLOBALS['HookRanTestingAfter'] = 'HookRanTestingAfter'; } /** - * Before FirstAlphabeticallyHooks2::cacheFlush2 + * Before FirstAlphabeticallyHooks2::cacheFlush2. */ #[HookBefore(['hook_order_last_alphabetically2'])] - #[HookOrderGroup(['cacheFlush2'])] + #[HookOrderGroup(['cache_flush'])] #[Hook('cache_flush')] public static function cacheFlush2(): void { $GLOBALS['HookBefore'] = 'HookBefore'; -- GitLab From 68e075765317aa4aba8f1fd1f94991c482c60d62 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 16:28:27 -0500 Subject: [PATCH 024/268] CS --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 ++++ core/lib/Drupal/Core/Hook/HookPriority.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 0e0f2b1d8074..09456a4f8fed 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -176,9 +176,13 @@ protected static function registerServices(ContainerBuilder $container, HookColl /** * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * The container. * @param array $allOrderAttributes + * All attributes related to ordering. * @param array $orderGroups + * Groups to order by. * @param array $implementations + * Hook implementations. * * @return void */ diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 82033db888d8..d0b909b3b60a 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -13,7 +13,7 @@ public function __construct(protected ContainerBuilder $container) {} /** * Change the priority of a hook implementation. * - * @param string $hook + * @param array $hooks * The name of the hook. * @param string $class_and_method * Class and method separated by :: containing the hook implementation which -- GitLab From a9477fbd6c7e9c7874057132300b840a1980d1b7 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 10 Dec 2024 16:55:20 -0500 Subject: [PATCH 025/268] Fix missing array key --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 09456a4f8fed..940f518b6699 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -200,7 +200,7 @@ protected static function reOrderServices(ContainerBuilder $container, array $al $others = []; foreach ($orderAttribute->modules as $module) { foreach ($hooks as $hook) { - foreach ($implementations[$hook][$module] as $class => $methods) { + foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { $others[] = "$class::$method"; } -- GitLab From f2472b13ff3c9e8f35ba2020440be4474606ecb8 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 10 Dec 2024 21:00:09 -0500 Subject: [PATCH 026/268] Split before and after into two modules --- .../src/Hook/FirstAlphabeticallyHooks2.php | 11 -------- .../hook_order_first_alphabetically3.info.yml | 6 +++++ .../src/Hook/FirstAlphabeticallyHooks3.php | 27 +++++++++++++++++++ .../src/Hook/LastAlphabeticallyHooks1.php | 11 -------- .../src/Hook/LastAlphabeticallyHooks2.php | 10 ------- .../hook_order_last_alphabetically3.info.yml | 6 +++++ .../src/Hook/LastAlphabeticallyHooks3.php | 26 ++++++++++++++++++ .../Core/Hook/HookCollectorPassTest.php | 11 +++++--- 8 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php index 97e6124acae7..920a685d5f67 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php @@ -23,15 +23,4 @@ public static function cacheFlush1(): void { $GLOBALS['HookAfter'] = 'HookAfter'; } - /** - * After LastAlphabeticallyHooks2::cacheFlush2. - */ - #[Hook('cache_flush')] - public static function cacheFlush2(): void { - if (!isset($GLOBALS['HookBefore'])) { - $GLOBALS['HookOutOfOrderTestingBefore'] = 'HookOutOfOrderTestingBefore'; - } - $GLOBALS['HookRanTestingBefore'] = 'HookRanTestingBefore'; - } - } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml new file mode 100644 index 000000000000..d61a2ddc1212 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml @@ -0,0 +1,6 @@ +name: first alphabetically +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php new file mode 100644 index 000000000000..24355e8a3362 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically3\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\HookOrderGroup; + +/** + * Hook implementations for hook_order_first_alphabetically. + */ +class FirstAlphabeticallyHooks3 { + + /** + * After LastAlphabeticallyHooks3::cacheFlush. + */ + #[Hook('cache_flush')] + public static function cacheFlush(): void { + if (!isset($GLOBALS['HookBefore'])) { + $GLOBALS['HookOutOfOrderTestingBefore'] = 'HookOutOfOrderTestingBefore'; + } + $GLOBALS['HookRanTestingBefore'] = 'HookRanTestingBefore'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php index c9b15dc39f21..a90d8e6b67fb 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php @@ -32,15 +32,4 @@ public static function cacheFlush2(): void { $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; } - /** - * Before FirstAlphabeticallyHooks::cacheFlush3. - */ - #[Hook('cache_flush')] - public static function cacheFlush3(): void { - if (isset($GLOBALS['HookLast'])) { - $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; - } - $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; - } - } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php index c0161c2a239d..73375ea550c8 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php @@ -25,14 +25,4 @@ public static function cacheFlush1(): void { $GLOBALS['HookRanTestingAfter'] = 'HookRanTestingAfter'; } - /** - * Before FirstAlphabeticallyHooks2::cacheFlush2. - */ - #[HookBefore(['hook_order_last_alphabetically2'])] - #[HookOrderGroup(['cache_flush'])] - #[Hook('cache_flush')] - public static function cacheFlush2(): void { - $GLOBALS['HookBefore'] = 'HookBefore'; - } - } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml new file mode 100644 index 000000000000..59334b38d2fc --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml @@ -0,0 +1,6 @@ +name: Hook ordering last +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php new file mode 100644 index 000000000000..16d1177ee0f9 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php @@ -0,0 +1,26 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically3\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookBefore; +use Drupal\Core\Hook\Attribute\HookOrderGroup; + +/** + * Hook implementations for hook_order_last_alphabetically3. + */ +class LastAlphabeticallyHooks3 { + + /** + * Before FirstAlphabeticallyHooks3::cacheFlush. + */ + #[HookBefore(['hook_order_last_alphabetically3'])] + #[HookOrderGroup(['cache_flush'])] + #[Hook('cache_flush')] + public static function cacheFlush(): void { + $GLOBALS['HookBefore'] = 'HookBefore'; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 2f0832a8f83d..0f8450cff86b 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -150,13 +150,18 @@ public function testHookFirst(): void { $this->assertFalse(isset($GLOBALS['HookAfter'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); $this->assertFalse(isset($GLOBALS['HookRanTestingAfter'])); - $this->assertFalse(isset($GLOBALS['HookBefore'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingBefore'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingBefore'])); + drupal_flush_all_caches(); $this->assertTrue(isset($GLOBALS['HookAfter'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); $this->assertTrue(isset($GLOBALS['HookRanTestingAfter'])); + + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically3'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically3'])); + $this->assertFalse(isset($GLOBALS['HookBefore'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingBefore'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingBefore'])); + drupal_flush_all_caches(); $this->assertTrue(isset($GLOBALS['HookBefore'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingBefore'])); $this->assertTrue(isset($GLOBALS['HookRanTestingBefore'])); -- GitLab From ed9b022e6a96cf7cbfea3212d79d8b5985120975 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 10 Dec 2024 21:22:42 -0500 Subject: [PATCH 027/268] Fix cs and the test --- .../src/Hook/FirstAlphabeticallyHooks3.php | 2 -- .../src/Hook/LastAlphabeticallyHooks2.php | 2 -- .../src/Hook/LastAlphabeticallyHooks3.php | 2 +- .../Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php | 1 - 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php index 24355e8a3362..dc84d9c8fef2 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php @@ -5,8 +5,6 @@ namespace Drupal\hook_order_first_alphabetically3\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookAfter; -use Drupal\Core\Hook\Attribute\HookOrderGroup; /** * Hook implementations for hook_order_first_alphabetically. diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php index 73375ea550c8..b95ceb3a8531 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php @@ -5,8 +5,6 @@ namespace Drupal\hook_order_last_alphabetically2\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookBefore; -use Drupal\Core\Hook\Attribute\HookOrderGroup; /** * Hook implementations for hook_order_last_alphabetically. diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php index 16d1177ee0f9..3f985067ba9b 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php @@ -16,7 +16,7 @@ class LastAlphabeticallyHooks3 { /** * Before FirstAlphabeticallyHooks3::cacheFlush. */ - #[HookBefore(['hook_order_last_alphabetically3'])] + #[HookBefore(['hook_order_first_alphabetically3'])] #[HookOrderGroup(['cache_flush'])] #[Hook('cache_flush')] public static function cacheFlush(): void { diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 0f8450cff86b..28b38812974c 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -150,7 +150,6 @@ public function testHookFirst(): void { $this->assertFalse(isset($GLOBALS['HookAfter'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); $this->assertFalse(isset($GLOBALS['HookRanTestingAfter'])); - drupal_flush_all_caches(); $this->assertTrue(isset($GLOBALS['HookAfter'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); -- GitLab From 48f22e90e5bad663550a34011ab6ee5d1d60d963 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 11 Dec 2024 09:24:16 -0500 Subject: [PATCH 028/268] Patience --- .../tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js b/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js index 2bf3247862c7..5dd7d76aa87e 100644 --- a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js +++ b/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js @@ -43,6 +43,7 @@ module.exports = { // Wait for new source editing vertical tab to be present before continuing. .waitForElementVisible( '[href*=edit-editor-settings-plugins-ckeditor5-sourceediting]', + 9000 ) .click('.ckeditor5-toolbar-item-codeBlock') // Select the Code Block button. // Hit the down arrow key to move it to the toolbar. -- GitLab From 30f45d9e73a1b474a62e429ca82546fe98073b5c Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 11 Dec 2024 09:32:10 -0500 Subject: [PATCH 029/268] eslint --- .../tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js b/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js index 5dd7d76aa87e..42cc0a335f90 100644 --- a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js +++ b/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js @@ -43,7 +43,7 @@ module.exports = { // Wait for new source editing vertical tab to be present before continuing. .waitForElementVisible( '[href*=edit-editor-settings-plugins-ckeditor5-sourceediting]', - 9000 + 9000, ) .click('.ckeditor5-toolbar-item-codeBlock') // Select the Code Block button. // Hit the down arrow key to move it to the toolbar. -- GitLab From 4ccaab9b40d3d6de11e7c044d26709d0779db989 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 11 Dec 2024 09:40:37 -0500 Subject: [PATCH 030/268] Fix modulehandler add --- .../Drupal/Core/Extension/ModuleHandler.php | 5 ++-- .../Drupal/Core/Hook/HookCollectorPass.php | 26 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 1f9ff1cb0f63..5722b7730d25 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -198,7 +198,8 @@ protected function add($type, $name, $path) { $filename = file_exists($php_file_path) ? "$name.$type" : NULL; $this->moduleList[$name] = new Extension($this->root, $type, $pathname, $filename); $this->resetImplementations(); - $hook_collector = HookCollectorPass::collectAllHookImplementations([$name => ['pathname' => $pathname]]); + $paths = [$name => ['pathname' => $pathname]]; + $hook_collector = HookCollectorPass::collectAllHookImplementations($paths); // A module freshly added will not be registered on the container yet. // ProceduralCall service does not yet know about it. // Note in HookCollectorPass: @@ -208,7 +209,7 @@ protected function add($type, $name, $path) { // includes. $hook_collector->loadAllIncludes(); // Register procedural implementations. - foreach ($hook_collector->getImplementations() as $hook => $moduleImplements) { + foreach ($hook_collector->getImplementations($paths) as $hook => $moduleImplements) { foreach ($moduleImplements as $module => $classImplements) { foreach ($classImplements[ProceduralCall::class] ?? [] as $method) { $this->invokeMap[$hook][$module][] = $method; diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 940f518b6699..8ee3f08f8ee5 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -31,13 +31,6 @@ */ class HookCollectorPass implements CompilerPassInterface { - /** - * An associative array of hook implementations. - * - * Keys are hook, module, class. Values are a list of methods. - */ - protected array $implementations = []; - /** * A list of include files. * @@ -72,8 +65,9 @@ class HookCollectorPass implements CompilerPassInterface { /** * {@inheritdoc} */ - public function process(ContainerBuilder $container): void { + public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); + $implementations = []; $orderGroups = []; /** @var \Drupal\Core\Hook\Attribute\HookOrderBase[] $allOrderAttributes */ $allOrderAttributes = []; @@ -94,7 +88,7 @@ public function process(ContainerBuilder $container): void { $method = $attribute->method; } $moduleImplements[$hook][$hookModule] = ''; - $collector->implementations[$hook][$hookModule][$class][] = $method; + $implementations[$hook][$hookModule][$class][] = $method; } if ($attribute instanceof HookOrderInterface) { $orderAttributes[] = $attribute; @@ -118,8 +112,12 @@ public function process(ContainerBuilder $container): void { } } - static::registerServices($container, $collector, $moduleImplements ?? []); - static::reOrderServices($container, $allOrderAttributes, $orderGroups, $collector->implementations); + // This can be removed when ModuleHandler::add() is removed. + if ($container->hasDefinition('module_handler')) { + static::registerServices($container, $collector, $moduleImplements ?? []); + static::reOrderServices($container, $allOrderAttributes, $orderGroups, $collector->implementations); + } + return $implementations; } /** @@ -398,8 +396,10 @@ public function loadAllIncludes(): void { * * @internal */ - public function getImplementations(): array { - return $this->implementations; + public function getImplementations($paths): array { + $container = new ContainerBuilder(); + $container->setParameter('container.modules', $paths); + return $this->process($container); } /** -- GitLab From 7899c0d74fc2ac3546fcea5d7db27200d2a5cbbc Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 11 Dec 2024 09:56:34 -0500 Subject: [PATCH 031/268] Add implementations back in --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 8ee3f08f8ee5..f33fe4a4e601 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -114,8 +114,8 @@ public function process(ContainerBuilder $container): array { // This can be removed when ModuleHandler::add() is removed. if ($container->hasDefinition('module_handler')) { - static::registerServices($container, $collector, $moduleImplements ?? []); - static::reOrderServices($container, $allOrderAttributes, $orderGroups, $collector->implementations); + static::registerServices($container, $collector, $implementations, $moduleImplements ?? []); + static::reOrderServices($container, $allOrderAttributes, $orderGroups, $implementations); } return $implementations; } @@ -125,12 +125,14 @@ public function process(ContainerBuilder $container): array { * The container. * @param \Drupal\Core\Hook\HookCollectorPass $collector * The collector. + * @param array $implementations + * All implementations. * @param array $allModuleImplements * Modules that implement hooks. * * @return void */ - protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $allModuleImplements): void { + protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $allModuleImplements): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); $groupIncludes = []; @@ -149,7 +151,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl } $priority = 0; foreach ($moduleImplements as $module => $v) { - foreach ($collector->implementations[$hook][$module] as $class => $method_hooks) { + foreach ($implementations[$hook][$module] as $class => $method_hooks) { if ($container->has($class)) { $definition = $container->findDefinition($class); } -- GitLab From 916444988df6bcde7989a5c5a2df149f62d6d6d7 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 11 Dec 2024 13:01:47 -0500 Subject: [PATCH 032/268] Fix ckeditor5 --- .../Drupal/Core/Extension/ModuleHandler.php | 31 ++----------------- .../Drupal/Core/Hook/HookCollectorPass.php | 2 +- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 5722b7730d25..ebb10025fbfb 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -455,9 +455,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // the primary hook, we need to add them to the $modules array in their // appropriate order. $modules = array_keys($hook_listeners); - if (isset($extra_modules)) { - $modules = $this->reOrderModulesForAlter($modules, $hook); - } + foreach ($modules as $module) { foreach ($hook_listeners[$module] ?? [] as $listener) { $this->alterEventListeners[$cid][] = $listener; @@ -469,32 +467,6 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { } } - /** - * Reorder modules for alters. - * - * @param array $modules - * A list of modules. - * @param string $hook - * The hook being worked on, for example form_alter. - * - * @return array - * The list, potentially reordered and changed by - * hook_module_implements_alter(). - */ - protected function reOrderModulesForAlter(array $modules, string $hook): array { - // Order by module order first. - $modules = array_intersect(array_keys($this->moduleList), $modules); - // Alter expects the module list to be in the keys. - $implementations = array_fill_keys($modules, FALSE); - // Let modules adjust the order solely based on the primary hook. This - // ensures the same module order regardless of whether this block - // runs. Calling $this->alter() recursively in this way does not - // result in an infinite loop, because this call is for a single - // $type, so we won't end up in this method again. - $this->alter('module_implements', $implementations, $hook); - return array_keys($implementations); - } - /** * {@inheritdoc} */ @@ -581,6 +553,7 @@ protected function getHookListeners(string $hook): array { } } } + return $this->invokeMap[$hook] ?? []; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index f33fe4a4e601..00b7eddab3d0 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -113,7 +113,7 @@ public function process(ContainerBuilder $container): array { } // This can be removed when ModuleHandler::add() is removed. - if ($container->hasDefinition('module_handler')) { + if (count($container->getDefinitions()) > 1) { static::registerServices($container, $collector, $implementations, $moduleImplements ?? []); static::reOrderServices($container, $allOrderAttributes, $orderGroups, $implementations); } -- GitLab From 88ce810e0219d94c97edc216123f0f0dc28d9db4 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 11 Dec 2024 13:04:18 -0500 Subject: [PATCH 033/268] Unused module --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index ebb10025fbfb..03c1b7b5251d 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -446,7 +446,6 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { } else { $hook_listeners[$module] = $listeners; - $extra_modules = TRUE; } } } -- GitLab From 18d0f5c058c2b944c97bf623fdd2f50623537d99 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 11 Dec 2024 13:33:44 -0500 Subject: [PATCH 034/268] Need runtime alters --- .../Drupal/Core/Extension/ModuleHandler.php | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 03c1b7b5251d..7c8a599090ae 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -446,6 +446,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { } else { $hook_listeners[$module] = $listeners; + $extra_modules = TRUE; } } } @@ -454,7 +455,9 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // the primary hook, we need to add them to the $modules array in their // appropriate order. $modules = array_keys($hook_listeners); - + if (isset($extra_modules)) { + $modules = $this->reOrderModulesForAlter($modules, $hook); + } foreach ($modules as $module) { foreach ($hook_listeners[$module] ?? [] as $listener) { $this->alterEventListeners[$cid][] = $listener; @@ -466,6 +469,32 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { } } + /** + * Reorder modules for alters. + * + * @param array $modules + * A list of modules. + * @param string $hook + * The hook being worked on, for example form_alter. + * + * @return array + * The list, potentially reordered and changed by + * hook_module_implements_alter(). + */ + protected function reOrderModulesForAlter(array $modules, string $hook): array { + // Order by module order first. + $modules = array_intersect(array_keys($this->moduleList), $modules); + // Alter expects the module list to be in the keys. + $implementations = array_fill_keys($modules, FALSE); + // Let modules adjust the order solely based on the primary hook. This + // ensures the same module order regardless of whether this block + // runs. Calling $this->alter() recursively in this way does not + // result in an infinite loop, because this call is for a single + // $type, so we won't end up in this method again. + $this->alter('module_implements', $implementations, $hook); + return array_keys($implementations); + } + /** * {@inheritdoc} */ -- GitLab From b06752ba31d001fa3e003ac1273ee865bd68c33f Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 18:08:59 -0500 Subject: [PATCH 035/268] BC layer for hmia --- .../Drupal/Core/Extension/ModuleHandler.php | 13 ++++- .../Drupal/Core/Hook/HookCollectorPass.php | 52 ++++++++++++++----- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 7c8a599090ae..923687709a3b 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -80,11 +80,13 @@ class ModuleHandler implements ModuleHandlerInterface { * An array keyed by hook, classname, method and the value is the module. * @param array $groupIncludes * An array of .inc files to get helpers from. + * @param array $hooksOrderedByAttributes + * An array of hooks that have been ordered by attributes. * * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider */ - public function __construct($root, array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = []) { + public function __construct($root, array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], protected array $hooksOrderedByAttributes = []) { $this->root = $root; $this->moduleList = []; foreach ($module_list as $name => $module) { @@ -456,7 +458,14 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // appropriate order. $modules = array_keys($hook_listeners); if (isset($extra_modules)) { - $modules = $this->reOrderModulesForAlter($modules, $hook); + $orderingDone = FALSE; + if (count(array_intersect($extra_types, $this->hooksOrderedByAttributes)) === count($extra_types)) { + $orderingDone = TRUE; + } + + if (!$orderingDone) { + $modules = $this->reOrderModulesForAlter($modules, $hook); + } } foreach ($modules as $module) { foreach ($hook_listeners[$module] ?? [] as $listener) { diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 00b7eddab3d0..4861b7d539e4 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -114,12 +114,30 @@ public function process(ContainerBuilder $container): array { // This can be removed when ModuleHandler::add() is removed. if (count($container->getDefinitions()) > 1) { - static::registerServices($container, $collector, $implementations, $moduleImplements ?? []); + static::registerServices($container, $collector, $implementations, $moduleImplements ?? [], $orderGroups); static::reOrderServices($container, $allOrderAttributes, $orderGroups, $implementations); } return $implementations; } + /** + * @param mixed $hook + * The hook to get from orderGroups. + * @param array $orderGroups + * The hooks that have HookOrderGroup attributes. + * + * @return array + */ + protected static function getHooks(string $hook, array $orderGroups): array { + $hooks = [$hook]; + foreach ($orderGroups as $group) { + if (in_array($hook, $group)) { + $hooks = array_merge($hooks, $group); + } + } + return array_unique($hooks); + } + /** * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. @@ -127,12 +145,14 @@ public function process(ContainerBuilder $container): array { * The collector. * @param array $implementations * All implementations. - * @param array $allModuleImplements + * @param array $legacyImplementations * Modules that implement hooks. + * @param array $reorderGroups + * Groups of hooks to reorder. * * @return void */ - protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $allModuleImplements): void { + protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementations, array $reorderGroups): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); $groupIncludes = []; @@ -143,9 +163,17 @@ protected static function registerServices(ContainerBuilder $container, HookColl } } } - $definition = $container->getDefinition('module_handler'); - $definition->setArgument('$groupIncludes', $groupIncludes); - foreach ($allModuleImplements as $hook => $moduleImplements) { + + $hooksOrderedByAttribute = []; + foreach ($legacyImplementations as $hook => $moduleImplements) { + $getHooks = self::getHooks($hook, $reorderGroups); + $count = count($getHooks); + foreach ($getHooks as $extraHook) { + $moduleImplements += $legacyImplementations[$extraHook] ?? []; + if ($count > 1) { + $hooksOrderedByAttribute[] = str_replace('_alter', '', $extraHook); + } + } foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } @@ -169,8 +197,12 @@ protected static function registerServices(ContainerBuilder $container, HookColl ]); } } + unset($implementations[$hook][$module]); } } + $definition = $container->getDefinition('module_handler'); + $definition->setArgument('$groupIncludes', $groupIncludes); + $definition->setArgument('$hooksOrderedByAttributes', array_unique($hooksOrderedByAttribute) ?? []); $container->setParameter('hook_implementations_map', $map ?? []); } @@ -189,13 +221,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl protected static function reOrderServices(ContainerBuilder $container, array $allOrderAttributes, array $orderGroups, array $implementations): void { $hookPriority = new HookPriority($container); foreach ($allOrderAttributes as $orderAttribute) { - $hooks = [$orderAttribute->hook]; - foreach ($orderGroups as $group) { - if (in_array($orderAttribute->hook, $group)) { - $hooks = array_merge($hooks, $group); - } - } - $hooks = array_unique($hooks); + $hooks = self::getHooks($orderAttribute->hook, $orderGroups); if (isset($orderAttribute->modules)) { $others = []; foreach ($orderAttribute->modules as $module) { -- GitLab From 4f96c147a3c27de3ed72e4d83d4329d839d2ae53 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 18:34:51 -0500 Subject: [PATCH 036/268] Stan --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 923687709a3b..efb09f9ee6f3 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -459,7 +459,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $modules = array_keys($hook_listeners); if (isset($extra_modules)) { $orderingDone = FALSE; - if (count(array_intersect($extra_types, $this->hooksOrderedByAttributes)) === count($extra_types)) { + if (isset($extra_types) && count(array_intersect($extra_types, $this->hooksOrderedByAttributes)) === count($extra_types)) { $orderingDone = TRUE; } -- GitLab From e34ea9dcd5976e66d94a2d043de7de256b9a6585 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 18:49:25 -0500 Subject: [PATCH 037/268] Sometimes a module does not implement a hook --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 4861b7d539e4..f4959c12a1fd 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -179,7 +179,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl } $priority = 0; foreach ($moduleImplements as $module => $v) { - foreach ($implementations[$hook][$module] as $class => $method_hooks) { + foreach ($implementations[$hook][$module] ?? [] as $class => $method_hooks) { if ($container->has($class)) { $definition = $container->findDefinition($class); } -- GitLab From d4a67e1a4595585f5e0cfc8b1b4256d65c1ac69b Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 19:21:50 -0500 Subject: [PATCH 038/268] Handle update hooks --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index f4959c12a1fd..317a71bb9b1a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -166,7 +166,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl $hooksOrderedByAttribute = []; foreach ($legacyImplementations as $hook => $moduleImplements) { - $getHooks = self::getHooks($hook, $reorderGroups); + $getHooks = self::getHooks((string) $hook, $reorderGroups); $count = count($getHooks); foreach ($getHooks as $extraHook) { $moduleImplements += $legacyImplementations[$extraHook] ?? []; -- GitLab From b38ae1fb54ea881a91685804495a342def873b6a Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 19:23:32 -0500 Subject: [PATCH 039/268] Other call --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 317a71bb9b1a..c43e926e2e39 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -221,7 +221,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl protected static function reOrderServices(ContainerBuilder $container, array $allOrderAttributes, array $orderGroups, array $implementations): void { $hookPriority = new HookPriority($container); foreach ($allOrderAttributes as $orderAttribute) { - $hooks = self::getHooks($orderAttribute->hook, $orderGroups); + $hooks = self::getHooks((string) $orderAttribute->hook, $orderGroups); if (isset($orderAttribute->modules)) { $others = []; foreach ($orderAttribute->modules as $module) { -- GitLab From bf34b58d8746d6a3381f50a525c9d00cd075117f Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 20:13:25 -0500 Subject: [PATCH 040/268] Simplify already ordered --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index efb09f9ee6f3..1b0f74a5e65d 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -457,15 +457,8 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // the primary hook, we need to add them to the $modules array in their // appropriate order. $modules = array_keys($hook_listeners); - if (isset($extra_modules)) { - $orderingDone = FALSE; - if (isset($extra_types) && count(array_intersect($extra_types, $this->hooksOrderedByAttributes)) === count($extra_types)) { - $orderingDone = TRUE; - } - - if (!$orderingDone) { - $modules = $this->reOrderModulesForAlter($modules, $hook); - } + if (isset($extra_modules) && array_diff($extra_types, $this->hooksOrderedByAttributes)) { + $modules = $this->reOrderModulesForAlter($modules, $hook); } foreach ($modules as $module) { foreach ($hook_listeners[$module] ?? [] as $listener) { -- GitLab From 4667f02c2e3b530b882698184f77e1e100ad0f95 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 20:13:41 -0500 Subject: [PATCH 041/268] Docs and simplification of interfaces --- .../Drupal/Core/Hook/Attribute/HookAfter.php | 4 +-- .../Drupal/Core/Hook/Attribute/HookBefore.php | 4 +-- .../Drupal/Core/Hook/Attribute/HookFirst.php | 2 ++ .../Drupal/Core/Hook/Attribute/HookLast.php | 2 ++ .../Core/Hook/Attribute/HookOrderBase.php | 29 +++++++++++-------- .../Hook/Attribute/HookOrderInterface.php | 19 ++++++++---- .../Drupal/Core/Hook/HookCollectorPass.php | 5 +--- 7 files changed, 40 insertions(+), 25 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index c1ee29d48ad9..ead9cb498dc2 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -5,10 +5,10 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for marking that a hook should be changed. + * Attribute for marking that a hook's order should be changed. * * This allows you to ensure the hook is executed after - * a specific hook in another module. + * hooks in other modules. * * @section sec_backwards_compatibility Backwards-compatibility * diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index cdbef7f0e983..33658b1fbe10 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -5,10 +5,10 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for marking that a hook should be changed. + * Attribute for marking that a hook's order should be changed. * * This allows you to ensure the hook is executed before - * a specific hook in another module. + * hooks in other modules. * * @section sec_backwards_compatibility Backwards-compatibility * diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php index 774d74f0bb29..45f98cd6d87b 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php @@ -7,6 +7,8 @@ /** * Attribute for marking that a hook should be executed first. * + * This makes sure that this hook runs before all other hooks of the same type. + * * @section sec_backwards_compatibility Backwards-compatibility * * To allow hook implementations to work on older versions of Drupal as well, diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php index 2f23ef11c055..759b6df2e301 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php @@ -7,6 +7,8 @@ /** * Attribute for marking that a hook should be executed last. * + * This makes sure that this hook runs after all other hooks of the same type. + * * @section sec_backwards_compatibility Backwards-compatibility * * To allow hook implementations to work on older versions of Drupal as well, diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php index 752fcea02c71..e207f193c3d2 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php @@ -4,38 +4,43 @@ namespace Drupal\Core\Hook\Attribute; +/** + * Common set of functionality needed by attributes that handle ordering hooks. + */ class HookOrderBase implements HookOrderInterface { /** + * The hook that should be ordered. + * * @internal */ public string $hook; /** + * The class the hook is found in. + * * @internal */ public string $class; /** + * The method of the hook. + * * @internal */ public string $method; - public function __construct(public readonly bool $shouldBeLarger) { - - } + /** + * Constructs a HookOrderBase class. + */ + public function __construct(public readonly bool $shouldBeLarger) {} - public function setHook(string $hook): static { + /** + * {@inheritdoc} + */ + public function set(string $hook, string $class, string $method): static { $this->hook = $hook; - return $this; - } - - public function setClass(string $class): static { $this->class = $class; - return $this; - } - - public function setMethod(string $method): static { $this->method = $method; return $this; } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php index c5406a63801e..be9d8b18825d 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -4,12 +4,21 @@ namespace Drupal\Core\Hook\Attribute; +/** + * Interface for classes that manage hook ordering. + */ interface HookOrderInterface { - public function setHook(string $hook): static; - - public function setClass(string $class): static; - - public function setMethod(string $method): static; + /** + * Set the properties on the attributes using this class. + * + * @param string $hook + * The hook to order. + * @param string $class + * The class the hook is in. + * @param string $method + * The method of the hook. + */ + public function set(string $hook, string $class, string $method): static; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index c43e926e2e39..da00a7577d4e 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -99,10 +99,7 @@ public function process(ContainerBuilder $container): array { } if ($hook) { foreach ($orderAttributes as $orderAttribute) { - $allOrderAttributes[] = $orderAttribute - ->setHook($hook) - ->setClass($class) - ->setMethod($method); + $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method); } if ($orderGroup) { $orderGroups[] = array_merge($orderGroup, [$hook]); -- GitLab From cddd6889a5eb7f9224c738c44cc19e14854fce98 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 20:25:10 -0500 Subject: [PATCH 042/268] Clean up extra hook storage --- .../Drupal/Core/Hook/HookCollectorPass.php | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index da00a7577d4e..b01373097556 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -102,12 +102,16 @@ public function process(ContainerBuilder $container): array { $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method); } if ($orderGroup) { - $orderGroups[] = array_merge($orderGroup, [$hook]); + $orderGroup[] = $hook; + foreach ($orderGroup as $extraHook) { + $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $orderGroup); + } } } } } } + $orderGroups = array_map('array_unique', $orderGroups); // This can be removed when ModuleHandler::add() is removed. if (count($container->getDefinitions()) > 1) { @@ -117,24 +121,6 @@ public function process(ContainerBuilder $container): array { return $implementations; } - /** - * @param mixed $hook - * The hook to get from orderGroups. - * @param array $orderGroups - * The hooks that have HookOrderGroup attributes. - * - * @return array - */ - protected static function getHooks(string $hook, array $orderGroups): array { - $hooks = [$hook]; - foreach ($orderGroups as $group) { - if (in_array($hook, $group)) { - $hooks = array_merge($hooks, $group); - } - } - return array_unique($hooks); - } - /** * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. @@ -163,9 +149,9 @@ protected static function registerServices(ContainerBuilder $container, HookColl $hooksOrderedByAttribute = []; foreach ($legacyImplementations as $hook => $moduleImplements) { - $getHooks = self::getHooks((string) $hook, $reorderGroups); - $count = count($getHooks); - foreach ($getHooks as $extraHook) { + $extraHooks = $reorderGroups[$hook] ?? []; + $count = count($extraHooks); + foreach ($extraHooks as $extraHook) { $moduleImplements += $legacyImplementations[$extraHook] ?? []; if ($count > 1) { $hooksOrderedByAttribute[] = str_replace('_alter', '', $extraHook); @@ -218,7 +204,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl protected static function reOrderServices(ContainerBuilder $container, array $allOrderAttributes, array $orderGroups, array $implementations): void { $hookPriority = new HookPriority($container); foreach ($allOrderAttributes as $orderAttribute) { - $hooks = self::getHooks((string) $orderAttribute->hook, $orderGroups); + $hooks = $orderGroups[$orderAttribute->hook] ?? []; if (isset($orderAttribute->modules)) { $others = []; foreach ($orderAttribute->modules as $module) { -- GitLab From b2d65eafb0f54f7acfa41b381cf0d37416d8b45e Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 21:27:18 -0500 Subject: [PATCH 043/268] Add tests for hookOrderGroups --- .../Drupal/Core/Hook/HookCollectorPass.php | 4 +- .../src/Hook/FirstAlphabeticallyHooks1.php | 15 +--- .../src/Hook/FirstAlphabeticallyHooks2.php | 6 +- .../hook_order_first_alphabetically4.info.yml | 6 ++ .../src/Hook/FirstAlphabeticallyHooks4.php | 29 ++++++++ .../hook_order_first_alphabetically5.info.yml | 6 ++ .../src/Hook/FirstAlphabeticallyHooks5.php | 24 ++++++ .../src/Hook/LastAlphabeticallyHooks1.php | 17 +---- .../src/Hook/LastAlphabeticallyHooks2.php | 4 +- .../hook_order_last_alphabetically4.info.yml | 6 ++ .../src/Hook/LastAlphabeticallyHooks4.php | 22 ++++++ .../hook_order_last_alphabetically5.info.yml | 6 ++ .../src/Hook/LastAlphabeticallyHooks5.php | 25 +++++++ .../Core/Hook/HookCollectorPassTest.php | 74 +++++++++++++++---- 14 files changed, 196 insertions(+), 48 deletions(-) create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index b01373097556..3ea550ab7639 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -204,7 +204,9 @@ protected static function registerServices(ContainerBuilder $container, HookColl protected static function reOrderServices(ContainerBuilder $container, array $allOrderAttributes, array $orderGroups, array $implementations): void { $hookPriority = new HookPriority($container); foreach ($allOrderAttributes as $orderAttribute) { - $hooks = $orderGroups[$orderAttribute->hook] ?? []; + // ::process() adds the hook serving as key to the order group so it + // does not need to be added if there's a group for the hook. + $hooks = $orderGroups[$orderAttribute->hook] ?? [$orderAttribute->hook]; if (isset($orderAttribute->modules)) { $others = []; foreach ($orderAttribute->modules as $module) { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php index d9e190560719..daf9769ff73f 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php @@ -5,7 +5,6 @@ namespace Drupal\hook_order_first_alphabetically1\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookLast; /** * Hook implementations for hook_order_first_alphabetically. @@ -13,23 +12,13 @@ class FirstAlphabeticallyHooks1 { /** - * After LastAlphabeticallyHooks1::cacheFlush1. + * After LastAlphabeticallyHooks1::cacheFlush. */ #[Hook('cache_flush')] - public static function cacheFlush1(): void { + public static function cacheFlush(): void { if (!isset($GLOBALS['HookFirst'])) { $GLOBALS['HookOutOfOrderTestingFirst'] = 'HookOutOfOrderTestingFirst'; } $GLOBALS['HookRanTestingFirst'] = 'HookRanTestingFirst'; } - - /** - * After LastAlphabeticallyHooks1::cacheFlush2. - */ - #[HookLast] - #[Hook('cache_flush')] - public static function cacheFlush2(): void { - $GLOBALS['HookLast'] = 'HookLast'; - } - } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php index 920a685d5f67..c0c2f254d337 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php @@ -6,7 +6,6 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookAfter; -use Drupal\Core\Hook\Attribute\HookOrderGroup; /** * Hook implementations for hook_order_first_alphabetically. @@ -14,12 +13,11 @@ class FirstAlphabeticallyHooks2 { /** - * After LastAlphabeticallyHooks2::cacheFlush1. + * After LastAlphabeticallyHooks2::cacheFlush. */ #[HookAfter(['hook_order_last_alphabetically2'])] - #[HookOrderGroup(['cache_flush'])] #[Hook('cache_flush')] - public static function cacheFlush1(): void { + public static function cacheFlush(): void { $GLOBALS['HookAfter'] = 'HookAfter'; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml new file mode 100644 index 000000000000..d61a2ddc1212 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml @@ -0,0 +1,6 @@ +name: first alphabetically +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php b/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php new file mode 100644 index 000000000000..84c2c1e001c1 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php @@ -0,0 +1,29 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically4\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\HookOrderGroup; + +/** + * Hook implementations for hook_order_first_alphabetically. + */ +class FirstAlphabeticallyHooks4 { + + /** + * After LastAlphabeticallyHooks4::customHookExtraTypes. + */ + #[HookAfter(['hook_order_last_alphabetically4'])] + #[HookOrderGroup(['custom_hook_extra_types2_alter'])] + #[Hook('custom_hook_extra_types1_alter')] + public static function customHookExtraTypes(): void { + if (!isset($GLOBALS['HookOrderGroupExtraTypes'])) { + $GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'] = 'HookOutOfOrderTestingOrderGroupsExtraTypes'; + } + $GLOBALS['HookRanTestingOrderGroupsExtraTypes'] = 'HookRanTestingOrderGroupsExtraTypes'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml new file mode 100644 index 000000000000..d61a2ddc1212 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml @@ -0,0 +1,6 @@ +name: first alphabetically +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php b/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php new file mode 100644 index 000000000000..8b687c359738 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php @@ -0,0 +1,24 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically5\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookLast; + +/** + * Hook implementations for hook_order_first_alphabetically. + */ +class FirstAlphabeticallyHooks5 { + + /** + * After LastAlphabeticallyHooks5::cacheFlush. + */ + #[HookLast] + #[Hook('cache_flush')] + public static function cacheFlush(): void { + $GLOBALS['HookLast'] = 'HookLast'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php index a90d8e6b67fb..83ee0b3b09d1 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php @@ -6,6 +6,7 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookFirst; +use Drupal\Core\Hook\Attribute\HookOrderGroup; /** * Hook implementations for hook_order_last_alphabetically. @@ -13,23 +14,13 @@ class LastAlphabeticallyHooks1 { /** - * Before FirstAlphabeticallyHooks1::cacheFlush1. + * Before FirstAlphabeticallyHooks1::cacheFlush. */ #[HookFirst] #[Hook('cache_flush')] - public static function cacheFlush1(): void { + #[HookOrderGroup(['cache_flush'])] + public static function cacheFlush(): void { $GLOBALS['HookFirst'] = 'HookFirst'; } - /** - * Before FirstAlphabeticallyHooks1::cacheFlush2. - */ - #[Hook('cache_flush')] - public static function cacheFlush2(): void { - if (isset($GLOBALS['HookLast'])) { - $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; - } - $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; - } - } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php index b95ceb3a8531..4210d9364608 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php @@ -12,10 +12,10 @@ class LastAlphabeticallyHooks2 { /** - * Before FirstAlphabeticallyHooks2::cacheFlush1. + * Before FirstAlphabeticallyHooks2::cacheFlush. */ #[Hook('cache_flush')] - public static function cacheFlush1(): void { + public static function cacheFlush(): void { // This should be run before so HookAfter should not be set. if (isset($GLOBALS['HookAfter'])) { $GLOBALS['HookOutOfOrderTestingAfter'] = 'HookOutOfOrderTestingAfter'; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml new file mode 100644 index 000000000000..59334b38d2fc --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml @@ -0,0 +1,6 @@ +name: Hook ordering last +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php b/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php new file mode 100644 index 000000000000..37b36995e5f6 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php @@ -0,0 +1,22 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically4\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Hook implementations for hook_order_last_alphabetically. + */ +class LastAlphabeticallyHooks4 { + + /** + * Before FirstAlphabeticallyHooks4::customHookExtraTypes. + */ + #[Hook('custom_hook_extra_types2_alter')] + public static function customHookExtraTypes(): void { + $GLOBALS['HookOrderGroupExtraTypes'] = 'HookOrderGroupExtraTypes'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml new file mode 100644 index 000000000000..59334b38d2fc --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml @@ -0,0 +1,6 @@ +name: Hook ordering last +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php b/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php new file mode 100644 index 000000000000..cc6b117e4a9d --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically5\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Hook implementations for hook_order_last_alphabetically. + */ +class LastAlphabeticallyHooks5 { + + /** + * Before FirstAlphabeticallyHooks5::cacheFlush. + */ + #[Hook('cache_flush')] + public static function cacheFlush(): void { + if (isset($GLOBALS['HookLast'])) { + $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; + } + $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 28b38812974c..9412573232ca 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -124,27 +124,39 @@ public function testProceduralHooksSkippedWhenConfigured(): void { } /** - * Tests HookFirst. + * Test Hook attribute with named arguments, and class with invoke method. + */ + public function testHookAttribute(): void { + $module_installer = $this->container->get('module_installer'); + $this->assertTrue($module_installer->install(['hook_collector_hook_attribute'])); + $this->assertFalse(isset($GLOBALS['hook_named_arguments'])); + $this->assertFalse(isset($GLOBALS['hook_invoke_method'])); + drupal_flush_all_caches(); + $this->assertTrue(isset($GLOBALS['hook_named_arguments'])); + $this->assertTrue(isset($GLOBALS['hook_invoke_method'])); + } + + /** + * Tests hook ordering with attributes. */ public function testHookFirst(): void { $module_installer = $this->container->get('module_installer'); - $module_handler = $this->container->get('module_handler'); $this->assertTrue($module_installer->install(['hook_order_first_alphabetically1'])); $this->assertTrue($module_installer->install(['hook_order_last_alphabetically1'])); $this->assertFalse(isset($GLOBALS['HookFirst'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingFirst'])); $this->assertFalse(isset($GLOBALS['HookRanTestingFirst'])); - $this->assertFalse(isset($GLOBALS['HookLast'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingLast'])); drupal_flush_all_caches(); $this->assertTrue(isset($GLOBALS['HookFirst'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingFirst'])); $this->assertTrue(isset($GLOBALS['HookRanTestingFirst'])); - $this->assertTrue(isset($GLOBALS['HookLast'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingLast'])); + } + /** + * Tests hook ordering with attributes. + */ + public function testHookAfter(): void { + $module_installer = $this->container->get('module_installer'); $this->assertTrue($module_installer->install(['hook_order_first_alphabetically2'])); $this->assertTrue($module_installer->install(['hook_order_last_alphabetically2'])); $this->assertFalse(isset($GLOBALS['HookAfter'])); @@ -154,7 +166,13 @@ public function testHookFirst(): void { $this->assertTrue(isset($GLOBALS['HookAfter'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); $this->assertTrue(isset($GLOBALS['HookRanTestingAfter'])); + } + /** + * Tests hook ordering with attributes. + */ + public function testHookBefore(): void { + $module_installer = $this->container->get('module_installer'); $this->assertTrue($module_installer->install(['hook_order_first_alphabetically3'])); $this->assertTrue($module_installer->install(['hook_order_last_alphabetically3'])); $this->assertFalse(isset($GLOBALS['HookBefore'])); @@ -167,16 +185,42 @@ public function testHookFirst(): void { } /** - * Test Hook attribute with named arguments, and class with invoke method. + * Tests hook ordering with attributes. */ - public function testHookAttribute(): void { + public function testHookOrderGroup(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_collector_hook_attribute'])); - $this->assertFalse(isset($GLOBALS['hook_named_arguments'])); - $this->assertFalse(isset($GLOBALS['hook_invoke_method'])); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically4'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically4'])); + $this->assertFalse(isset($GLOBALS['HookOrderGroupExtraTypes'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingOrderGroupsExtraTypes'])); + $module_handler = $this->container->get('module_handler'); + $hooks = [ + 'custom_hook', + 'custom_hook_extra_types1', + 'custom_hook_extra_types2', + ]; + $data = ['hi']; + $module_handler->alter($hooks, $data); + $this->assertTrue(isset($GLOBALS['HookOrderGroupExtraTypes'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingOrderGroupsExtraTypes'])); + } + + /** + * Tests hook ordering with attributes. + */ + public function testHookLast(): void { + $module_installer = $this->container->get('module_installer'); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically5'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically5'])); + $this->assertFalse(isset($GLOBALS['HookLast'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingLast'])); drupal_flush_all_caches(); - $this->assertTrue(isset($GLOBALS['hook_named_arguments'])); - $this->assertTrue(isset($GLOBALS['hook_invoke_method'])); + $this->assertTrue(isset($GLOBALS['HookLast'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingLast'])); } } -- GitLab From 22843dc6eaa53afeeecd1a9e12779918c408af7a Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 12 Dec 2024 22:05:07 -0500 Subject: [PATCH 044/268] Stan and CS --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 ++ .../src/Hook/FirstAlphabeticallyHooks1.php | 1 + 2 files changed, 3 insertions(+) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 1b0f74a5e65d..82674d0b9302 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -457,6 +457,8 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // the primary hook, we need to add them to the $modules array in their // appropriate order. $modules = array_keys($hook_listeners); + // If $extra_modules is set then $extra_types must be set. + /** @phpstan-ignore variable.undefined */ if (isset($extra_modules) && array_diff($extra_types, $this->hooksOrderedByAttributes)) { $modules = $this->reOrderModulesForAlter($modules, $hook); } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php index daf9769ff73f..e61ba8bc229c 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php @@ -21,4 +21,5 @@ public static function cacheFlush(): void { } $GLOBALS['HookRanTestingFirst'] = 'HookRanTestingFirst'; } + } -- GitLab From b6a8cefe1db921bbdc512fee44d15f65bfd76003 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 14:50:51 -0500 Subject: [PATCH 045/268] Manage already ordered hooks --- .../Drupal/Core/Extension/ModuleHandler.php | 25 +++++++++++---- .../Drupal/Core/Hook/HookCollectorPass.php | 17 +++++----- core/lib/Drupal/Core/Hook/HookPriority.php | 32 +++++++++++++++---- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 82674d0b9302..bd6ae0dcf931 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -439,10 +439,8 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $hook = $type . '_alter'; $hook_listeners = $this->getHookListeners($hook); if (isset($extra_types)) { - // For multiple hooks, we need $modules to contain every module that - // implements at least one of them in the correct order. - foreach ($extra_types as $extra_type) { - foreach ($this->getHookListeners($extra_type . '_alter') as $module => $listeners) { + $find_listeners = function ($hook) use (&$hook_listeners, &$extra_modules) { + foreach ($this->getHookListeners($hook) as $module => $listeners) { if (isset($hook_listeners[$module])) { $hook_listeners[$module] = array_merge($hook_listeners[$module], $listeners); } @@ -451,15 +449,28 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $extra_modules = TRUE; } } + }; + // For multiple hooks, we need $modules to contain every module that + // implements at least one of them in the correct order. Hooks already + // ordered by attributes are also ordered by + // hook_module_implements_alter() they don't need to be ordered again. + foreach (array_merge($extra_types, [$type]) as $extra_type) { + if (isset($this->hooksOrderedByAttributes[$extra_type])) { + $group = $this->hooksOrderedByAttributes[$extra_type]; + krsort($group); + $find_listeners(implode(':', $group)); + $extra_types = array_diff($extra_types, $group); + } + } + foreach ($extra_types as $extra_type) { + $find_listeners($extra_type . '_alter'); } } // If any modules implement one of the extra hooks that do not implement // the primary hook, we need to add them to the $modules array in their // appropriate order. $modules = array_keys($hook_listeners); - // If $extra_modules is set then $extra_types must be set. - /** @phpstan-ignore variable.undefined */ - if (isset($extra_modules) && array_diff($extra_types, $this->hooksOrderedByAttributes)) { + if (!empty($extra_modules) && !empty($extra_types)) { $modules = $this->reOrderModulesForAlter($modules, $hook); } foreach ($modules as $module) { diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 3ea550ab7639..84625242b085 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -147,15 +147,10 @@ protected static function registerServices(ContainerBuilder $container, HookColl } } - $hooksOrderedByAttribute = []; foreach ($legacyImplementations as $hook => $moduleImplements) { $extraHooks = $reorderGroups[$hook] ?? []; - $count = count($extraHooks); foreach ($extraHooks as $extraHook) { $moduleImplements += $legacyImplementations[$extraHook] ?? []; - if ($count > 1) { - $hooksOrderedByAttribute[] = str_replace('_alter', '', $extraHook); - } } foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); @@ -183,9 +178,15 @@ protected static function registerServices(ContainerBuilder $container, HookColl unset($implementations[$hook][$module]); } } + + $hooksOrderedByAttributes = []; + foreach ($reorderGroups as $key => $values) { + // Remove _alter from the end. + $hooksOrderedByAttributes[substr($key, 0, -6)] = $values; + } $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$hooksOrderedByAttributes', array_unique($hooksOrderedByAttribute) ?? []); + $definition->setArgument('$hooksOrderedByAttributes', array_unique($hooksOrderedByAttributes) ?? []); $container->setParameter('hook_implementations_map', $map ?? []); } @@ -213,7 +214,7 @@ protected static function reOrderServices(ContainerBuilder $container, array $al foreach ($hooks as $hook) { foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { - $others[] = "$class::$method"; + $others[] = [$class, $method,]; } } } @@ -222,7 +223,7 @@ protected static function reOrderServices(ContainerBuilder $container, array $al else { $others = NULL; } - $hookPriority->change($hooks, "$orderAttribute->class::$orderAttribute->method", $orderAttribute->shouldBeLarger, $others); + $hookPriority->change($hooks, $orderAttribute->class, $orderAttribute->method, $orderAttribute->shouldBeLarger, $others); } } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index d0b909b3b60a..64479559614a 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -15,20 +15,38 @@ public function __construct(protected ContainerBuilder $container) {} * * @param array $hooks * The name of the hook. - * @param string $class_and_method - * Class and method separated by :: containing the hook implementation which - * should be changed. + * @param string $class + * Class containing the hook implementation which should be changed. + * @param string $method + * Method of the hook implementation which should be changed. * @param bool $should_be_larger * TRUE for before/first, FALSE for after/last. Larger priority listeners * fire first. * @param array|null $others * Other hook implementations to compare to, if any. The array is a list of - * strings containing a class and method separated by ::. + * arrays containing a class and method. * * @return void */ - public function change(array $hooks, string $class_and_method, bool $should_be_larger, ?array $others = NULL): void { + public function change(array $hooks, string $class, string $method, bool $should_be_larger, ?array $others = NULL): void { + $class_and_method = "$class::$method"; + if ($others) { + $other_specifiers = array_map(fn ($pair) => $pair[0] . '::' . $pair[1], $others); + } $events = array_map(fn ($hook) => "drupal_hook.$hook", $hooks); + if (count($hooks) > 1) { + krsort($hooks); + $combinedHookTag = implode(':', $hooks); + $others[] = [$class, $method]; + foreach ($others as $other) { + $definition = $this->container->getDefinition($other[0]); + $definition->addTag('kernel.event_listener', [ + 'event' => "drupal_hook.$combinedHookTag", + 'method' => $other[1], + 'priority' => 0, + ]); + } + } foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) { foreach ($attributes as $key => $tag) { if (in_array($tag['event'], $events)) { @@ -43,10 +61,10 @@ public function change(array $hooks, string $class_and_method, bool $should_be_l if ($class_and_method === $specifier) { $index_this = $index; } - // $others is specified for before and after, for these compare only + // $others is defined for before and after, for these compare only // the priority of those. For first and last the priority of every // other hook matters. - elseif (!isset($others) || in_array($specifier, $others)) { + elseif (!isset($other_specifiers) || in_array($specifier, $other_specifiers)) { $priorities_other[] = $priority; } } -- GitLab From 602807a8b04411c64b98eb85fca17d46fb394acc Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 15:02:30 -0500 Subject: [PATCH 046/268] Coding standards --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 ++ core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index bd6ae0dcf931..c2d17136cc28 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -446,6 +446,8 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { } else { $hook_listeners[$module] = $listeners; + // It is used below. + // @phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable $extra_modules = TRUE; } } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 84625242b085..21f7f6827882 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -214,7 +214,7 @@ protected static function reOrderServices(ContainerBuilder $container, array $al foreach ($hooks as $hook) { foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { - $others[] = [$class, $method,]; + $others[] = [$class, $method]; } } } -- GitLab From f7be8d475d800c468a5f0e2993724da036a14916 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 17:56:29 -0500 Subject: [PATCH 047/268] grouped alters --- .../Drupal/Core/Extension/ModuleHandler.php | 13 +++-- .../Drupal/Core/Hook/Attribute/HookAfter.php | 2 +- .../Drupal/Core/Hook/Attribute/HookBefore.php | 2 +- .../Core/Hook/Attribute/HookOrderBase.php | 10 +++- .../Hook/Attribute/HookOrderInterface.php | 4 +- .../Drupal/Core/Hook/HookCollectorPass.php | 16 ++--- core/lib/Drupal/Core/Hook/HookPriority.php | 58 ++++++++++--------- 7 files changed, 59 insertions(+), 46 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index c2d17136cc28..a63afc55b562 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -452,20 +452,21 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { } } }; + $extra_hooks = array_map(fn ($x) => $x . '_alter', $extra_types); // For multiple hooks, we need $modules to contain every module that // implements at least one of them in the correct order. Hooks already // ordered by attributes are also ordered by // hook_module_implements_alter() they don't need to be ordered again. - foreach (array_merge($extra_types, [$type]) as $extra_type) { - if (isset($this->hooksOrderedByAttributes[$extra_type])) { - $group = $this->hooksOrderedByAttributes[$extra_type]; + foreach (array_merge($extra_hooks, [$type . '_alter']) as $extra_hook) { + if (isset($this->hooksOrderedByAttributes[$extra_hook])) { + $group = $this->hooksOrderedByAttributes[$extra_hook]; krsort($group); $find_listeners(implode(':', $group)); - $extra_types = array_diff($extra_types, $group); + $extra_types = array_diff($extra_hooks, $group); } } - foreach ($extra_types as $extra_type) { - $find_listeners($extra_type . '_alter'); + foreach ($extra_hooks as $extra_hook) { + $find_listeners($extra_hook); } } // If any modules implement one of the extra hooks that do not implement diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index ead9cb498dc2..80199bc78c09 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -30,7 +30,7 @@ class HookAfter extends HookOrderBase { * The module this implementation should run before. */ public function __construct( - public readonly array $modules, + public array $modules, ) { parent::__construct(FALSE); } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index 33658b1fbe10..e93daa0b83cd 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -30,7 +30,7 @@ class HookBefore extends HookOrderBase { * The module this implementation should run before. */ public function __construct( - public readonly array $modules, + public array $modules, ) { parent::__construct(TRUE); } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php index e207f193c3d2..9d482e18c3d1 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php @@ -30,6 +30,13 @@ class HookOrderBase implements HookOrderInterface { */ public string $method; + /** + * The module of the hook. + * + * @internal + */ + public string $module; + /** * Constructs a HookOrderBase class. */ @@ -38,10 +45,11 @@ public function __construct(public readonly bool $shouldBeLarger) {} /** * {@inheritdoc} */ - public function set(string $hook, string $class, string $method): static { + public function set(string $hook, string $class, string $method, string $module): static { $this->hook = $hook; $this->class = $class; $this->method = $method; + $this->module = $module; return $this; } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php index be9d8b18825d..2e70e18f153f 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -18,7 +18,9 @@ interface HookOrderInterface { * The class the hook is in. * @param string $method * The method of the hook. + * @param string $module + * The module of the hook. */ - public function set(string $hook, string $class, string $method): static; + public function set(string $hook, string $class, string $method, string $module): static; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 21f7f6827882..77fd18129f5e 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -87,7 +87,7 @@ public function process(ContainerBuilder $container): array { if ($attribute->method) { $method = $attribute->method; } - $moduleImplements[$hook][$hookModule] = ''; + $legacyImplementations[$hook][$hookModule] = ''; $implementations[$hook][$hookModule][$class][] = $method; } if ($attribute instanceof HookOrderInterface) { @@ -99,7 +99,7 @@ public function process(ContainerBuilder $container): array { } if ($hook) { foreach ($orderAttributes as $orderAttribute) { - $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method); + $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $hookModule); } if ($orderGroup) { $orderGroup[] = $hook; @@ -115,7 +115,7 @@ public function process(ContainerBuilder $container): array { // This can be removed when ModuleHandler::add() is removed. if (count($container->getDefinitions()) > 1) { - static::registerServices($container, $collector, $implementations, $moduleImplements ?? [], $orderGroups); + static::registerServices($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); static::reOrderServices($container, $allOrderAttributes, $orderGroups, $implementations); } return $implementations; @@ -155,6 +155,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } + $legacyImplementations[$hook] = $moduleImplements; $priority = 0; foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $method_hooks) { @@ -181,12 +182,11 @@ protected static function registerServices(ContainerBuilder $container, HookColl $hooksOrderedByAttributes = []; foreach ($reorderGroups as $key => $values) { - // Remove _alter from the end. - $hooksOrderedByAttributes[substr($key, 0, -6)] = $values; + $hooksOrderedByAttributes[$key] = $values; } $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$hooksOrderedByAttributes', array_unique($hooksOrderedByAttributes) ?? []); + $definition->setArgument('$hooksOrderedByAttributes', $hooksOrderedByAttributes); $container->setParameter('hook_implementations_map', $map ?? []); } @@ -214,7 +214,7 @@ protected static function reOrderServices(ContainerBuilder $container, array $al foreach ($hooks as $hook) { foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { - $others[] = [$class, $method]; + $others[] = [$class, $method, $module]; } } } @@ -223,7 +223,7 @@ protected static function reOrderServices(ContainerBuilder $container, array $al else { $others = NULL; } - $hookPriority->change($hooks, $orderAttribute->class, $orderAttribute->method, $orderAttribute->shouldBeLarger, $others); + $hookPriority->change($hooks, $orderAttribute, $others); } } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 64479559614a..4187918da4a9 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -4,6 +4,7 @@ namespace Drupal\Core\Hook; +use Drupal\Core\Hook\Attribute\HookOrderBase; use Symfony\Component\DependencyInjection\ContainerBuilder; class HookPriority { @@ -15,45 +16,46 @@ public function __construct(protected ContainerBuilder $container) {} * * @param array $hooks * The name of the hook. - * @param string $class - * Class containing the hook implementation which should be changed. - * @param string $method - * Method of the hook implementation which should be changed. - * @param bool $should_be_larger - * TRUE for before/first, FALSE for after/last. Larger priority listeners - * fire first. + * @param \Drupal\Core\Hook\Attribute\HookOrderBase $attribute + * The order attribute. * @param array|null $others * Other hook implementations to compare to, if any. The array is a list of - * arrays containing a class and method. - * - * @return void + * arrays containing a class, a method and module. */ - public function change(array $hooks, string $class, string $method, bool $should_be_larger, ?array $others = NULL): void { - $class_and_method = "$class::$method"; + public function change(array $hooks, HookOrderBase $attribute, ?array $others = NULL): void { + $class_and_method = "$attribute->class::$attribute->method"; if ($others) { $other_specifiers = array_map(fn ($pair) => $pair[0] . '::' . $pair[1], $others); } - $events = array_map(fn ($hook) => "drupal_hook.$hook", $hooks); if (count($hooks) > 1) { + $map = $this->container->getParameter('hook_implementations_map'); krsort($hooks); $combinedHookTag = implode(':', $hooks); - $others[] = [$class, $method]; - foreach ($others as $other) { - $definition = $this->container->getDefinition($other[0]); + $event = "drupal_hook.$combinedHookTag"; + $data = $others; + $data[] = [$attribute->class, $attribute->method, $attribute->module]; + $priority = 0; + foreach ($data as [$class, $method, $module]) { + $definition = $this->container->findDefinition($class); $definition->addTag('kernel.event_listener', [ 'event' => "drupal_hook.$combinedHookTag", - 'method' => $other[1], - 'priority' => 0, + 'method' => $method, + 'priority' => $priority--, ]); + $map[$combinedHookTag][$class][$method] = $module; } + $this->container->setParameter('hook_implementations_map', $map); + } + else { + $event = 'drupal_hook.' . reset($hooks); } - foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) { - foreach ($attributes as $key => $tag) { - if (in_array($tag['event'], $events)) { + foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $tags) { + foreach ($tags as $key => $tag) { + if ($tag['event'] === $event) { $index = "$id.$key"; $priority = $tag['priority']; // Symfony documents event listener priorities to be integers, - // HookCollectorPass sets them to be integers, ::setPriority() only + // HookCollectorPass sets them to be integers, ::set() only // accepts integers. assert(is_int($priority)); $priorities[$index] = $priority; @@ -65,7 +67,7 @@ public function change(array $hooks, string $class, string $method, bool $should // the priority of those. For first and last the priority of every // other hook matters. elseif (!isset($other_specifiers) || in_array($specifier, $other_specifiers)) { - $priorities_other[] = $priority; + $priorities_other[$specifier] = $priority; } } } @@ -76,15 +78,15 @@ public function change(array $hooks, string $class, string $method, bool $should // The priority of the hook being changed. $priority_this = $priorities[$index_this]; // The priority of the hook being compared to. - $priority_other = $should_be_larger ? max($priorities_other) : min($priorities_other); + $priority_other = $attribute->shouldBeLarger ? max($priorities_other) : min($priorities_other); // If the order is correct there is nothing to do. If the two priorities // are the same then the order is undefined and so it can't be correct. // If they are not the same and $priority_this is already larger exactly - // when $should_be_larger says then it's the correct order. - if ($priority_this !== $priority_other && ($should_be_larger === ($priority_this > $priority_other))) { + // when $attribute->shouldBeLarger says then it's the correct order. + if ($priority_this !== $priority_other && ($attribute->shouldBeLarger === ($priority_this > $priority_other))) { return; } - $priority_new = $priority_other + ($should_be_larger ? 1 : -1); + $priority_new = $priority_other + ($attribute->shouldBeLarger ? 1 : -1); // For first and last this new priority is already larger/smaller // than all existing priorities but for before / after it might belong to // an already existing hook. In this case set the new priority temporarily @@ -94,7 +96,7 @@ public function change(array $hooks, string $class, string $method, bool $should // relative to both $priority_other and the hook whose priority was // $priority_new. if (in_array($priority_new, $priorities)) { - $priorities[$index_this] = $priority_other + ($should_be_larger ? 0.5 : -0.5); + $priorities[$index_this] = $priority_other + ($attribute->shouldBeLarger ? 0.5 : -0.5); asort($priorities); $changed_indexes = array_keys($priorities); $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); -- GitLab From 0f2c1eeac0314d8400207aed0b0145987a427e18 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 18:48:51 -0500 Subject: [PATCH 048/268] Stan --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 77fd18129f5e..c3ebb3ad0df9 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -99,6 +99,9 @@ public function process(ContainerBuilder $container): array { } if ($hook) { foreach ($orderAttributes as $orderAttribute) { + // $hookModule is set in the same clause as $hook + // if $hook is set then $hookModule is. + /** @phpstan-ignore variable.undefined */ $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $hookModule); } if ($orderGroup) { -- GitLab From baa832c0c3b8aa3ddba550c95c916822e77da8bc Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 20:58:24 -0500 Subject: [PATCH 049/268] Fix all the alters --- .../Drupal/Core/Extension/ModuleHandler.php | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index a63afc55b562..220a6bd1b8c4 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -438,21 +438,13 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $this->alterEventListeners[$cid] = []; $hook = $type . '_alter'; $hook_listeners = $this->getHookListeners($hook); + $extra_modules = FALSE; + $extra_listeners = []; if (isset($extra_types)) { - $find_listeners = function ($hook) use (&$hook_listeners, &$extra_modules) { - foreach ($this->getHookListeners($hook) as $module => $listeners) { - if (isset($hook_listeners[$module])) { - $hook_listeners[$module] = array_merge($hook_listeners[$module], $listeners); - } - else { - $hook_listeners[$module] = $listeners; - // It is used below. - // @phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable - $extra_modules = TRUE; - } - } - }; $extra_hooks = array_map(fn ($x) => $x . '_alter', $extra_types); + foreach ($extra_hooks as $extra_hook) { + $hook_listeners = $this->findListenersForAlter($extra_hook, $hook_listeners, $extra_modules); + } // For multiple hooks, we need $modules to contain every module that // implements at least one of them in the correct order. Hooks already // ordered by attributes are also ordered by @@ -461,20 +453,22 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { if (isset($this->hooksOrderedByAttributes[$extra_hook])) { $group = $this->hooksOrderedByAttributes[$extra_hook]; krsort($group); - $find_listeners(implode(':', $group)); + $extra_listeners = $this->findListenersForAlter(implode(':', $group)); $extra_types = array_diff($extra_hooks, $group); } } - foreach ($extra_hooks as $extra_hook) { - $find_listeners($extra_hook); - } } // If any modules implement one of the extra hooks that do not implement // the primary hook, we need to add them to the $modules array in their // appropriate order. - $modules = array_keys($hook_listeners); - if (!empty($extra_modules) && !empty($extra_types)) { - $modules = $this->reOrderModulesForAlter($modules, $hook); + if (isset($extra_types) && !$extra_types) { + $modules = array_keys(array_intersect_key($extra_listeners, $hook_listeners)); + } + else { + $modules = array_keys($hook_listeners); + if ($extra_modules) { + $modules = $this->reOrderModulesForAlter($modules, $hook); + } } foreach ($modules as $module) { foreach ($hook_listeners[$module] ?? [] as $listener) { @@ -603,4 +597,19 @@ protected function getHookListeners(string $hook): array { return $this->invokeMap[$hook] ?? []; } + public function findListenersForAlter($hook, array $hook_listeners = [], ?bool &$extra_modules = NULL): array { + foreach ($this->getHookListeners($hook) as $module => $listeners) { + if (isset($hook_listeners[$module])) { + $hook_listeners[$module] = array_merge($hook_listeners[$module], $listeners); + } + else { + $hook_listeners[$module] = $listeners; + // It is used below. + // @phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable + $extra_modules = TRUE; + } + } + return $hook_listeners; + } + } -- GitLab From 4a7019f501614256565777154b5478cacab7de52 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 22:19:09 -0500 Subject: [PATCH 050/268] Comments --- .../Drupal/Core/Extension/ModuleHandler.php | 19 +++++++++++++++++-- .../Drupal/Core/Hook/Attribute/HookAfter.php | 4 ++-- .../Drupal/Core/Hook/Attribute/HookBefore.php | 4 ++-- .../Core/Hook/Attribute/HookOrderBase.php | 3 +++ .../Core/Hook/Attribute/HookOrderGroup.php | 8 ++++---- .../Drupal/Core/Hook/HookCollectorPass.php | 9 ++++++++- core/lib/Drupal/Core/Hook/HookPriority.php | 6 +++++- .../Tests/ckEditor5CodeSyntaxTest.js | 1 - 8 files changed, 41 insertions(+), 13 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 220a6bd1b8c4..1188915adcba 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -452,6 +452,8 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { foreach (array_merge($extra_hooks, [$type . '_alter']) as $extra_hook) { if (isset($this->hooksOrderedByAttributes[$extra_hook])) { $group = $this->hooksOrderedByAttributes[$extra_hook]; + // When checking for already ordered groups ensure the listener + // is in the same order as when we set it. krsort($group); $extra_listeners = $this->findListenersForAlter(implode(':', $group)); $extra_types = array_diff($extra_hooks, $group); @@ -597,14 +599,27 @@ protected function getHookListeners(string $hook): array { return $this->invokeMap[$hook] ?? []; } - public function findListenersForAlter($hook, array $hook_listeners = [], ?bool &$extra_modules = NULL): array { + /** + * Helper to get hook listeners when in alter. + * + * @param string $hook + * The extra hook or combination hook to check for. + * @param array $hook_listeners + * Hook listeners for the current hook_alter. + * @param bool $extra_modules + * Whether there are extra modules to order. + * + * @return array + * The hook listeners. + */ + public function findListenersForAlter(string $hook, array $hook_listeners = [], ?bool &$extra_modules = NULL): array { foreach ($this->getHookListeners($hook) as $module => $listeners) { if (isset($hook_listeners[$module])) { $hook_listeners[$module] = array_merge($hook_listeners[$module], $listeners); } else { $hook_listeners[$module] = $listeners; - // It is used below. + // It is used by reference. // @phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable $extra_modules = TRUE; } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index 80199bc78c09..dd7de1b3ceab 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -7,7 +7,7 @@ /** * Attribute for marking that a hook's order should be changed. * - * This allows you to ensure the hook is executed after + * This allows you to ensure the hook is executed after similar * hooks in other modules. * * @section sec_backwards_compatibility Backwards-compatibility @@ -27,7 +27,7 @@ class HookAfter extends HookOrderBase { * Constructs a HookAfter attribute. * * @param array $modules - * The module this implementation should run before. + * The modules this implementation should run before. */ public function __construct( public array $modules, diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index e93daa0b83cd..6ab83a3e4c83 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -7,7 +7,7 @@ /** * Attribute for marking that a hook's order should be changed. * - * This allows you to ensure the hook is executed before + * This allows you to ensure the hook is executed before similar * hooks in other modules. * * @section sec_backwards_compatibility Backwards-compatibility @@ -27,7 +27,7 @@ class HookBefore extends HookOrderBase { * Constructs a HookBefore lib/Drupal/Core/Hook/Attribute/attribute. * * @param array $modules - * The module this implementation should run before. + * The modules this implementation should run before. */ public function __construct( public array $modules, diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php index 9d482e18c3d1..4a657bd8497c 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php @@ -39,6 +39,9 @@ class HookOrderBase implements HookOrderInterface { /** * Constructs a HookOrderBase class. + * + * @param bool $shouldBeLarger + * Determines whether the hook should increase or decrease priority. */ public function __construct(public readonly bool $shouldBeLarger) {} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php index 346a78ee7e67..7b31153235ec 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php @@ -7,8 +7,8 @@ /** * Attribute for marking which specific implementations to group. * - * This allows hook ordering to handle extra types such as ordering form_alter - * relative to hook_form_FORM_ID_alter. + * This allows hook ordering to handle extra types such as ordering + * hook_form_alter relative to hook_form_FORM_ID_alter. * * @section sec_backwards_compatibility Backwards-compatibility * @@ -24,13 +24,13 @@ class HookOrderGroup { /** - * Constructs a core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php attribute object. + * Constructs a HookOrderGroup attribute object. * * @param array $group * A list of hooks to sort together. For example, if a method implementing * form_BASE_FORM_ID_alter wants to sort itself relative to some * implementations of form_FORM_ID_alter then this would contain those. - * See Ckeditor5::formFilterFormatFormAlter() for example. + * See Ckeditor5::formFilterFormatFormAlter() for an example. */ public function __construct( public array $group, diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index c3ebb3ad0df9..7b642cbe256f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -116,7 +116,7 @@ public function process(ContainerBuilder $container): array { } $orderGroups = array_map('array_unique', $orderGroups); - // This can be removed when ModuleHandler::add() is removed. + // @todo remove if statement wrapper when ModuleHandler::add() is removed. if (count($container->getDefinitions()) > 1) { static::registerServices($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); static::reOrderServices($container, $allOrderAttributes, $orderGroups, $implementations); @@ -125,6 +125,11 @@ public function process(ContainerBuilder $container): array { } /** + * Register hook implementations as event listeners. + * + * Passes required include information to module_handler. + * Passes required runtime ordering information to module_handler. + * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. * @param \Drupal\Core\Hook\HookCollectorPass $collector @@ -194,6 +199,8 @@ protected static function registerServices(ContainerBuilder $container, HookColl } /** + * Reorder services that have attributes specifying an order. + * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. * @param array $allOrderAttributes diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 4187918da4a9..c712aa4ed634 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -7,6 +7,9 @@ use Drupal\Core\Hook\Attribute\HookOrderBase; use Symfony\Component\DependencyInjection\ContainerBuilder; +/** + * A class to handle updating priority of hook listeners. + */ class HookPriority { public function __construct(protected ContainerBuilder $container) {} @@ -20,7 +23,7 @@ public function __construct(protected ContainerBuilder $container) {} * The order attribute. * @param array|null $others * Other hook implementations to compare to, if any. The array is a list of - * arrays containing a class, a method and module. + * arrays containing a class, a method, and module. */ public function change(array $hooks, HookOrderBase $attribute, ?array $others = NULL): void { $class_and_method = "$attribute->class::$attribute->method"; @@ -29,6 +32,7 @@ public function change(array $hooks, HookOrderBase $attribute, ?array $others = } if (count($hooks) > 1) { $map = $this->container->getParameter('hook_implementations_map'); + // Order the complex listener so we can find it runtime. krsort($hooks); $combinedHookTag = implode(':', $hooks); $event = "drupal_hook.$combinedHookTag"; diff --git a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js b/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js index 42cc0a335f90..2bf3247862c7 100644 --- a/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js +++ b/core/modules/ckeditor5/tests/src/Nightwatch/Tests/ckEditor5CodeSyntaxTest.js @@ -43,7 +43,6 @@ module.exports = { // Wait for new source editing vertical tab to be present before continuing. .waitForElementVisible( '[href*=edit-editor-settings-plugins-ckeditor5-sourceediting]', - 9000, ) .click('.ckeditor5-toolbar-item-codeBlock') // Select the Code Block button. // Hit the down arrow key to move it to the toolbar. -- GitLab From 5641a1e8c9ab0b339f364680d51fc2cd1adb8bde Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 22:28:03 -0500 Subject: [PATCH 051/268] Add comment to test modules --- .../src/Hook/FirstAlphabeticallyHooks1.php | 13 ++++++++++++- .../src/Hook/FirstAlphabeticallyHooks2.php | 13 ++++++++++++- .../src/Hook/FirstAlphabeticallyHooks3.php | 13 ++++++++++++- .../src/Hook/FirstAlphabeticallyHooks4.php | 14 +++++++++++++- .../src/Hook/FirstAlphabeticallyHooks5.php | 13 ++++++++++++- .../src/Hook/LastAlphabeticallyHooks1.php | 13 ++++++++++++- .../src/Hook/LastAlphabeticallyHooks2.php | 13 ++++++++++++- .../src/Hook/LastAlphabeticallyHooks3.php | 13 ++++++++++++- .../src/Hook/LastAlphabeticallyHooks4.php | 14 +++++++++++++- .../src/Hook/LastAlphabeticallyHooks5.php | 13 ++++++++++++- 10 files changed, 122 insertions(+), 10 deletions(-) diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php index e61ba8bc229c..6cc3477c5a14 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php @@ -7,7 +7,18 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for hook_order_first_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookFirst]. */ class FirstAlphabeticallyHooks1 { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php index c0c2f254d337..341688ce600d 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php @@ -8,7 +8,18 @@ use Drupal\Core\Hook\Attribute\HookAfter; /** - * Hook implementations for hook_order_first_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookAfter]. */ class FirstAlphabeticallyHooks2 { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php index dc84d9c8fef2..eeb79e8e630c 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php @@ -7,7 +7,18 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for hook_order_first_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookBefore]. */ class FirstAlphabeticallyHooks3 { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php b/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php index 84c2c1e001c1..4b7778bc5f0b 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php @@ -9,7 +9,19 @@ use Drupal\Core\Hook\Attribute\HookOrderGroup; /** - * Hook implementations for hook_order_first_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookOrderGroup]. + * This attribute must be paired with #[HookAfter] or #[HookBefore]. */ class FirstAlphabeticallyHooks4 { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php b/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php index 8b687c359738..540cd86c604c 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php @@ -8,7 +8,18 @@ use Drupal\Core\Hook\Attribute\HookLast; /** - * Hook implementations for hook_order_first_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookLast]. */ class FirstAlphabeticallyHooks5 { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php index 83ee0b3b09d1..6a55234725d9 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php @@ -9,7 +9,18 @@ use Drupal\Core\Hook\Attribute\HookOrderGroup; /** - * Hook implementations for hook_order_last_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookFirst]. */ class LastAlphabeticallyHooks1 { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php index 4210d9364608..02df5b541c46 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php @@ -7,7 +7,18 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for hook_order_last_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookAfter]. */ class LastAlphabeticallyHooks2 { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php index 3f985067ba9b..676f897a93a7 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php @@ -9,7 +9,18 @@ use Drupal\Core\Hook\Attribute\HookOrderGroup; /** - * Hook implementations for hook_order_last_alphabetically3. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookBefore]. */ class LastAlphabeticallyHooks3 { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php b/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php index 37b36995e5f6..cc7244a9eeb6 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php @@ -7,7 +7,19 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for hook_order_last_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookOrderGroup]. + * This attribute must be paired with #[HookAfter] or #[HookBefore]. */ class LastAlphabeticallyHooks4 { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php b/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php index cc6b117e4a9d..ae9fbfd24a7e 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php @@ -7,7 +7,18 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for hook_order_last_alphabetically. + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. All of these modules + * come in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each pair tests one hook order attribute. + * + * This pair tests #[HookLast]. */ class LastAlphabeticallyHooks5 { -- GitLab From 07ab746d04bb09377410c256cd48548224ce0a0e Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 22:32:59 -0500 Subject: [PATCH 052/268] Clean up test modules --- .../src/Hook/LastAlphabeticallyHooks1.php | 2 -- .../src/Hook/LastAlphabeticallyHooks3.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php index 6a55234725d9..059e95d0f4c1 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php @@ -6,7 +6,6 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookFirst; -use Drupal\Core\Hook\Attribute\HookOrderGroup; /** * Hook implementations for verifying ordering hooks by attributes. @@ -29,7 +28,6 @@ class LastAlphabeticallyHooks1 { */ #[HookFirst] #[Hook('cache_flush')] - #[HookOrderGroup(['cache_flush'])] public static function cacheFlush(): void { $GLOBALS['HookFirst'] = 'HookFirst'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php index 676f897a93a7..a25ac5454247 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php @@ -6,7 +6,6 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookBefore; -use Drupal\Core\Hook\Attribute\HookOrderGroup; /** * Hook implementations for verifying ordering hooks by attributes. @@ -28,7 +27,6 @@ class LastAlphabeticallyHooks3 { * Before FirstAlphabeticallyHooks3::cacheFlush. */ #[HookBefore(['hook_order_first_alphabetically3'])] - #[HookOrderGroup(['cache_flush'])] #[Hook('cache_flush')] public static function cacheFlush(): void { $GLOBALS['HookBefore'] = 'HookBefore'; -- GitLab From 0248111ccd1f231f0780e6efb3bd3dac624d6e46 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 13 Dec 2024 22:36:56 -0500 Subject: [PATCH 053/268] Add comments to tests --- .../src/Hook/FirstAlphabeticallyHooks1.php | 1 + .../src/Hook/FirstAlphabeticallyHooks3.php | 1 + .../src/Hook/FirstAlphabeticallyHooks4.php | 1 + .../src/Hook/LastAlphabeticallyHooks5.php | 1 + 4 files changed, 4 insertions(+) diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php index 6cc3477c5a14..f197cea8d043 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php @@ -27,6 +27,7 @@ class FirstAlphabeticallyHooks1 { */ #[Hook('cache_flush')] public static function cacheFlush(): void { + // This should be run after so HookFirst should not be set. if (!isset($GLOBALS['HookFirst'])) { $GLOBALS['HookOutOfOrderTestingFirst'] = 'HookOutOfOrderTestingFirst'; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php index eeb79e8e630c..479c7d85d149 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php @@ -27,6 +27,7 @@ class FirstAlphabeticallyHooks3 { */ #[Hook('cache_flush')] public static function cacheFlush(): void { + // This should be run after so HookBefore should not be set. if (!isset($GLOBALS['HookBefore'])) { $GLOBALS['HookOutOfOrderTestingBefore'] = 'HookOutOfOrderTestingBefore'; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php b/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php index 4b7778bc5f0b..08eef7984c09 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php @@ -32,6 +32,7 @@ class FirstAlphabeticallyHooks4 { #[HookOrderGroup(['custom_hook_extra_types2_alter'])] #[Hook('custom_hook_extra_types1_alter')] public static function customHookExtraTypes(): void { + // This should be run after so HookOrderGroupExtraTypes should not be set. if (!isset($GLOBALS['HookOrderGroupExtraTypes'])) { $GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'] = 'HookOutOfOrderTestingOrderGroupsExtraTypes'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php b/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php index ae9fbfd24a7e..75a0dbe4c785 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php @@ -27,6 +27,7 @@ class LastAlphabeticallyHooks5 { */ #[Hook('cache_flush')] public static function cacheFlush(): void { + // This should be run before so HookLast should not be set. if (isset($GLOBALS['HookLast'])) { $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; } -- GitLab From ab4088b84a93242e4c77c44ba14fa176e73fc442 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 10:14:36 -0500 Subject: [PATCH 054/268] Documentation updates --- .../Drupal/Core/Extension/ModuleHandler.php | 22 ++-- .../Drupal/Core/Hook/Attribute/HookAfter.php | 13 +-- .../Drupal/Core/Hook/Attribute/HookBefore.php | 13 +-- .../Drupal/Core/Hook/Attribute/HookFirst.php | 12 +- .../Drupal/Core/Hook/Attribute/HookLast.php | 12 +- .../Core/Hook/Attribute/HookOrderGroup.php | 27 +++-- .../Hook/Attribute/HookOrderInterface.php | 3 +- .../Drupal/Core/Hook/HookCollectorPass.php | 107 +++++++++++------- core/lib/Drupal/Core/Hook/HookPriority.php | 13 ++- 9 files changed, 117 insertions(+), 105 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 1188915adcba..e44e4396ab5a 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -442,13 +442,14 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $extra_listeners = []; if (isset($extra_types)) { $extra_hooks = array_map(fn ($x) => $x . '_alter', $extra_types); + // First get the listeners implementing extra hooks. foreach ($extra_hooks as $extra_hook) { $hook_listeners = $this->findListenersForAlter($extra_hook, $hook_listeners, $extra_modules); } - // For multiple hooks, we need $modules to contain every module that - // implements at least one of them in the correct order. Hooks already - // ordered by attributes are also ordered by - // hook_module_implements_alter() they don't need to be ordered again. + // Second, gather implementations defined in a + // Drupal\Core\Hook\Attribute\HookOrderGroup attribute. These are only + // used for ordering because the group might contain hooks not included + // in this alter() call. foreach (array_merge($extra_hooks, [$type . '_alter']) as $extra_hook) { if (isset($this->hooksOrderedByAttributes[$extra_hook])) { $group = $this->hooksOrderedByAttributes[$extra_hook]; @@ -456,20 +457,21 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // is in the same order as when we set it. krsort($group); $extra_listeners = $this->findListenersForAlter(implode(':', $group)); + // Remove already ordered hooks. $extra_types = array_diff($extra_hooks, $group); } } } - // If any modules implement one of the extra hooks that do not implement - // the primary hook, we need to add them to the $modules array in their - // appropriate order. - if (isset($extra_types) && !$extra_types) { + // If multiple alters were called, but they were already ordered by + // ordering attributes then keep that order. + if (isset($extra_types) && empty($extra_types)) { $modules = array_keys(array_intersect_key($extra_listeners, $hook_listeners)); } else { + // Otherwise, use a legacy ordering mechanism if needed. $modules = array_keys($hook_listeners); if ($extra_modules) { - $modules = $this->reOrderModulesForAlter($modules, $hook); + $modules = $this->legacyReOrderModulesForAlter($modules, $hook); } } foreach ($modules as $module) { @@ -495,7 +497,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { * The list, potentially reordered and changed by * hook_module_implements_alter(). */ - protected function reOrderModulesForAlter(array $modules, string $hook): array { + protected function legacyReOrderModulesForAlter(array $modules, string $hook): array { // Order by module order first. $modules = array_intersect(array_keys($this->moduleList), $modules); // Alter expects the module list to be in the keys. diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index dd7de1b3ceab..01a68e6f5e59 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -5,20 +5,11 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for marking that a hook's order should be changed. - * - * This allows you to ensure the hook is executed after similar - * hooks in other modules. + * Attribute to request this hook implementation to fire after others. * * @section sec_backwards_compatibility Backwards-compatibility * - * To allow hook implementations to work on older versions of Drupal as well, - * keep the hook_module_implements_alter implementation and add the appropriate - * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and - * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to - * the hook_module_implements_alter() implementation. - * - * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + * @see HookOrderGroup */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] class HookAfter extends HookOrderBase { diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index 6ab83a3e4c83..ee87e431418c 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -5,20 +5,11 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for marking that a hook's order should be changed. - * - * This allows you to ensure the hook is executed before similar - * hooks in other modules. + * Attribute to request this hook implementation to fire before others. * * @section sec_backwards_compatibility Backwards-compatibility * - * To allow hook implementations to work on older versions of Drupal as well, - * keep the hook_module_implements_alter implementation and add the appropriate - * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and - * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to - * the hook_module_implements_alter() implementation. - * - * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + * @see HookOrderGroup */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] class HookBefore extends HookOrderBase { diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php index 45f98cd6d87b..b7d82aab5d13 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php @@ -5,19 +5,11 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for marking that a hook should be executed first. - * - * This makes sure that this hook runs before all other hooks of the same type. + * Attribute to request this hook implementation to fire first. * * @section sec_backwards_compatibility Backwards-compatibility * - * To allow hook implementations to work on older versions of Drupal as well, - * keep the hook_module_implements_alter implementation and add the appropriate - * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and - * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to - * the hook_module_implements_alter() implementation. - * - * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + * @see HookOrderGroup */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] class HookFirst extends HookOrderBase { diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php index 759b6df2e301..d5c92d00eb77 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php @@ -5,19 +5,11 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for marking that a hook should be executed last. - * - * This makes sure that this hook runs after all other hooks of the same type. + * Attribute to request this hook implementation to fire after others. * * @section sec_backwards_compatibility Backwards-compatibility * - * To allow hook implementations to work on older versions of Drupal as well, - * keep the hook_module_implements_alter implementation and add the appropriate - * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and - * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to - * the hook_module_implements_alter() implementation. - * - * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + * @see HookOrderGroup */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] class HookLast extends HookOrderBase { diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php index 7b31153235ec..0e0b43044af6 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php @@ -5,18 +5,26 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for marking which specific implementations to group. + * List of alter hooks called together. * - * This allows hook ordering to handle extra types such as ordering - * hook_form_alter relative to hook_form_FORM_ID_alter. + * Ordering by attributes happens at build time by setting up the order of + * the listeners of a hook correctly. However, ModuleHandlerInterface::alter() + * can be called with multiple hooks runtime. If the hook defined on this + * method/class requires ordering relative to other such hooks then this + * attribute can be used to order relative to implementations of all hooks in + * the group. Include all alter hooks to be ordered against in the group even + * if no single alter() call includes all of them. For example, this can be + * used to order a hook_form_BASE_FORM_ID_alter() implementation relative to + * multiple hook_form_FORM_ID_alter() implementations as + * Drupal\ckeditor5\Hook\Ckeditor5Hooks::formFilterFormatFormAlter() does. * * @section sec_backwards_compatibility Backwards-compatibility * * To allow hook implementations to work on older versions of Drupal as well, - * keep the hook_module_implements_alter implementation and add the appropriate - * combination of #[HookFirst], #[HookLast], #[HookBefore], #[HookAfter], and - * #[HookOrderGroup] attributes. Then ensure you have added #[LegacyHook] to - * the hook_module_implements_alter() implementation. + * keep the hook_module_implements_alter() implementation and execute the + * same ordering as prescribed by the hook order attributes. Then add + * #[LegacyHook] to the hook_module_implements_alter() implementation so it + * only gets executed in older Drupal versions. * * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. */ @@ -27,10 +35,7 @@ class HookOrderGroup { * Constructs a HookOrderGroup attribute object. * * @param array $group - * A list of hooks to sort together. For example, if a method implementing - * form_BASE_FORM_ID_alter wants to sort itself relative to some - * implementations of form_FORM_ID_alter then this would contain those. - * See Ckeditor5::formFilterFormatFormAlter() for an example. + * A list of hooks to sort together. */ public function __construct( public array $group, diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php index 2e70e18f153f..1c87e3b51a08 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -19,7 +19,8 @@ interface HookOrderInterface { * @param string $method * The method of the hook. * @param string $module - * The module of the hook. + * The module of the hook. Note this might be different from the module the + * class is in. */ public function set(string $hook, string $class, string $method, string $module): static; diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 7b642cbe256f..aeaec41fd46e 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -58,7 +58,14 @@ class HookCollectorPass implements CompilerPassInterface { private array $groupIncludes = []; /** - * A list of attributes in modules for Hooks. + * A list of attributes for hook implementations. + * + * Keys are module, class and method. Values are all possible attributes on + * hook implementations: Hook to define the hook, HookOrderInterface to + * define the order in which the implementations fire, HookOrderGroup to + * define a group of hooks to be ordered together. + * + * @var array<string, <array string, <array string, Hook|HookOrderInterface|HookOrderGroup>>> */ protected array $moduleAttributes = []; @@ -78,31 +85,31 @@ public function process(ContainerBuilder $container): array { $orderGroup = FALSE; $hook = FALSE; foreach ($attributes as $attribute) { - if ($attribute instanceof Hook) { - if ($class !== ProceduralCall::class) { - self::checkForProceduralOnlyHooks($attribute, $class); - } - $hook = $attribute->hook; - $hookModule = $attribute->module ?: $module; - if ($attribute->method) { - $method = $attribute->method; - } - $legacyImplementations[$hook][$hookModule] = ''; - $implementations[$hook][$hookModule][$class][] = $method; - } - if ($attribute instanceof HookOrderInterface) { - $orderAttributes[] = $attribute; - } - if ($attribute instanceof HookOrderGroup) { - $orderGroup = $attribute->group; + switch (TRUE) { + case $attribute instanceof Hook: + if ($class !== ProceduralCall::class) { + self::checkForProceduralOnlyHooks($attribute, $class); + } + $hook = $attribute->hook; + if ($attribute->module) { + $module = $attribute->module; + } + $legacyImplementations[$hook][$module] = ''; + $implementations[$hook][$module][$class][] = $attribute->method ?: $method; + break; + + case $attribute instanceof HookOrderInterface: + $orderAttributes[] = $attribute; + break; + + case $attribute instanceof HookOrderGroup: + $orderGroup = $attribute->group; + break; } } if ($hook) { foreach ($orderAttributes as $orderAttribute) { - // $hookModule is set in the same clause as $hook - // if $hook is set then $hookModule is. - /** @phpstan-ignore variable.undefined */ - $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $hookModule); + $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $module); } if ($orderGroup) { $orderGroup[] = $hook; @@ -116,7 +123,9 @@ public function process(ContainerBuilder $container): array { } $orderGroups = array_map('array_unique', $orderGroups); - // @todo remove if statement wrapper when ModuleHandler::add() is removed. + // @todo investigate whether this if() is needed after ModuleHandler::add() + // is removed. + // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { static::registerServices($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); static::reOrderServices($container, $allOrderAttributes, $orderGroups, $implementations); @@ -253,8 +262,8 @@ protected static function reOrderServices(ContainerBuilder $container, array $al * @internal * This method is only used by ModuleHandler. * - * @todo Pass only $container when ModuleHandler->add is removed - * https://www.drupal.org/project/drupal/issues/3481778 + * @todo Pass only $container when ModuleHandler::add() is removed + * @see https://www.drupal.org/project/drupal/issues/3481778 */ public static function collectAllHookImplementations(array $module_filenames, ?ContainerBuilder $container = NULL): static { $modules = array_map(fn ($x) => preg_quote($x, '/'), array_keys($module_filenames)); @@ -317,14 +326,9 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $attributes = []; if (class_exists($class)) { $reflectionClass = new \ReflectionClass($class); - if ($class_attributes = $reflectionClass->getAttributes()) { - $attributes['__invoke'] = array_map(fn ($x) => $x->newInstance(), $class_attributes); - } - foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $methodReflection) { - if ($method_attributes = $methodReflection->getAttributes()) { - $attributes[$methodReflection->getName()] = array_map(fn ($x) => $x->newInstance(), $method_attributes); - } - } + $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); + $reflections[] = $reflectionClass; + $attributes = self::getAttributeInstances($attributes, $reflections); $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } } @@ -385,14 +389,14 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv * The file this procedural implementation is in. * @param string $hook * The name of the hook. - * @param string $hookModule - * The name of the module this hook implementation belongs to. It can be - * different to the file where $function is in. + * @param string $module + * The module of the hook. Note this might be different from the module the + * function is in. * @param string $function * The name of function implementing the hook. */ - protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $hookModule, string $function): void { - $this->moduleAttributes[$hookModule][ProceduralCall::class][$function] = [new Hook($hook, $hookModule . '_' . $hook)]; + protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { + $this->moduleAttributes[$module][ProceduralCall::class][$function] = [new Hook($hook, $module . '_' . $hook)]; if ($hook === 'hook_info') { $this->hookInfo[] = $function; } @@ -407,6 +411,9 @@ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $h /** * This method is only to be used by ModuleHandler. * + * @todo remove when ModuleHandler::add() is removed. + * @see https://www.drupal.org/project/drupal/issues/3481778 + * * @internal */ public function loadAllIncludes(): void { @@ -418,6 +425,9 @@ public function loadAllIncludes(): void { /** * This method is only to be used by ModuleHandler. * + * @todo remove when ModuleHandler::add() is removed. + * @see https://www.drupal.org/project/drupal/issues/3481778 + * * @internal */ public function getImplementations($paths): array { @@ -452,4 +462,25 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v } } + /** + * Get attribute instances from class and method reflections. + * + * @param array $attributes + * The current attributes. + * @param array $reflections + * A list of class and method reflections. + * + * @return array + * A list of Hook|HookOrderInterface|HookOrderGroup attribute instances. + */ + protected static function getAttributeInstances(array $attributes, array $reflections): array { + foreach ($reflections as $reflection) { + if ($reflection_attributes = $reflection->getAttributes()) { + $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; + $attributes[$method] = array_map(fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflection_attributes); + } + } + return $attributes; + } + } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index c712aa4ed634..2fd7c0b0f653 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -8,7 +8,9 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; /** - * A class to handle updating priority of hook listeners. + * Helper class for HookCollectorPass to change the priority of listeners. + * + * @internal */ class HookPriority { @@ -18,12 +20,17 @@ public function __construct(protected ContainerBuilder $container) {} * Change the priority of a hook implementation. * * @param array $hooks - * The name of the hook. + * The list of hooks to order. The list always contains the hook defined + * in Drupal\Core\Hook\Attribute, and it might also contain + * the hooks listed in the Drupal\Core\Hook\Attribute\HookOrderGroup + * attribute. * @param \Drupal\Core\Hook\Attribute\HookOrderBase $attribute * The order attribute. * @param array|null $others * Other hook implementations to compare to, if any. The array is a list of - * arrays containing a class, a method, and module. + * lists, each containing class, method, module. + * + * @internal */ public function change(array $hooks, HookOrderBase $attribute, ?array $others = NULL): void { $class_and_method = "$attribute->class::$attribute->method"; -- GitLab From 8b4c170e6fe9837f8b29febee842525cb0dee58d Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 10:41:02 -0500 Subject: [PATCH 055/268] Handle hooks implemented on behalf of other modules --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index aeaec41fd46e..2bb0a30d18f4 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -91,9 +91,10 @@ public function process(ContainerBuilder $container): array { self::checkForProceduralOnlyHooks($attribute, $class); } $hook = $attribute->hook; - if ($attribute->module) { - $module = $attribute->module; - } + // This hook may be implemented on behalf of another module. + // This prevents one hook implementing on behalf of another + // from overriding all following hooks. + $moduleForHook = $attribute->module ?: $module; $legacyImplementations[$hook][$module] = ''; $implementations[$hook][$module][$class][] = $attribute->method ?: $method; break; @@ -109,7 +110,7 @@ public function process(ContainerBuilder $container): array { } if ($hook) { foreach ($orderAttributes as $orderAttribute) { - $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $module); + $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $moduleForHook); } if ($orderGroup) { $orderGroup[] = $hook; -- GitLab From 8061d7b7690951487390ef074a8da45ae2fc2235 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 10:59:52 -0500 Subject: [PATCH 056/268] Stan --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 2bb0a30d18f4..c7b9309d95e0 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -85,16 +85,19 @@ public function process(ContainerBuilder $container): array { $orderGroup = FALSE; $hook = FALSE; foreach ($attributes as $attribute) { + // A hook may be implemented on behalf of another module. + // This prevents one hook implementing on behalf of another + // from overriding all following hooks in this class. + $currentModule = $module; switch (TRUE) { case $attribute instanceof Hook: if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($attribute, $class); } $hook = $attribute->hook; - // This hook may be implemented on behalf of another module. - // This prevents one hook implementing on behalf of another - // from overriding all following hooks. - $moduleForHook = $attribute->module ?: $module; + if ($attribute->module) { + $currentModule = $attribute->module; + } $legacyImplementations[$hook][$module] = ''; $implementations[$hook][$module][$class][] = $attribute->method ?: $method; break; @@ -110,7 +113,7 @@ public function process(ContainerBuilder $container): array { } if ($hook) { foreach ($orderAttributes as $orderAttribute) { - $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $moduleForHook); + $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $currentModule); } if ($orderGroup) { $orderGroup[] = $hook; -- GitLab From dd2eef96c3705fcba41f5839f4d0a8c1ca55ef12 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 11:23:25 -0500 Subject: [PATCH 057/268] Stan --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index c7b9309d95e0..fcf0512a4a0b 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -85,9 +85,9 @@ public function process(ContainerBuilder $container): array { $orderGroup = FALSE; $hook = FALSE; foreach ($attributes as $attribute) { - // A hook may be implemented on behalf of another module. // This prevents one hook implementing on behalf of another - // from overriding all following hooks in this class. + // module from overriding any following methods in this + // class. $currentModule = $module; switch (TRUE) { case $attribute instanceof Hook: @@ -113,6 +113,8 @@ public function process(ContainerBuilder $container): array { } if ($hook) { foreach ($orderAttributes as $orderAttribute) { + // If $hook is not false then $currentModule is set. + /** @phpstan-ignore variable.undefined */ $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $currentModule); } if ($orderGroup) { -- GitLab From e4359d100d199ddac53752207de5c6327a74af6d Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 12:04:38 -0500 Subject: [PATCH 058/268] Refactor attribute loop --- .../Core/Hook/Attribute/HookOrderBase.php | 11 +++++--- .../Hook/Attribute/HookOrderInterface.php | 11 +++----- .../Drupal/Core/Hook/HookCollectorPass.php | 27 +++++++++---------- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php index 4a657bd8497c..d222e979a1fb 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php @@ -33,6 +33,9 @@ class HookOrderBase implements HookOrderInterface { /** * The module of the hook. * + * The module of the hook. Note this might be different from the module the + * function is in. + * * @internal */ public string $module; @@ -48,11 +51,11 @@ public function __construct(public readonly bool $shouldBeLarger) {} /** * {@inheritdoc} */ - public function set(string $hook, string $class, string $method, string $module): static { - $this->hook = $hook; + public function set(Hook $hook, string $class): static { + $this->hook = $hook->hook; $this->class = $class; - $this->method = $method; - $this->module = $module; + $this->method = $hook->method; + $this->module = $hook->module; return $this; } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php index 1c87e3b51a08..3f8ddb76283a 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -12,16 +12,11 @@ interface HookOrderInterface { /** * Set the properties on the attributes using this class. * - * @param string $hook - * The hook to order. + * @param Drupal\Core\Hook\Attribute\Hook $hook + * The hook attribute to order. * @param string $class * The class the hook is in. - * @param string $method - * The method of the hook. - * @param string $module - * The module of the hook. Note this might be different from the module the - * class is in. */ - public function set(string $hook, string $class, string $method, string $module): static; + public function set(Hook $hook, string $class): static; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index fcf0512a4a0b..f510cef342d0 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -83,23 +83,21 @@ public function process(ContainerBuilder $container): array { foreach ($methods as $method => $attributes) { $orderAttributes = []; $orderGroup = FALSE; - $hook = FALSE; foreach ($attributes as $attribute) { - // This prevents one hook implementing on behalf of another - // module from overriding any following methods in this - // class. - $currentModule = $module; switch (TRUE) { case $attribute instanceof Hook: if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($attribute, $class); } - $hook = $attribute->hook; - if ($attribute->module) { - $currentModule = $attribute->module; + $hookAttribute = $attribute; + if (!$attribute->module) { + $attribute->module = $module; } - $legacyImplementations[$hook][$module] = ''; - $implementations[$hook][$module][$class][] = $attribute->method ?: $method; + if (!$attribute->method) { + $attribute->method = $method; + } + $legacyImplementations[$attribute->hook][$attribute->module] = ''; + $implementations[$attribute->hook][$attribute->module][$class][] = $attribute->method; break; case $attribute instanceof HookOrderInterface: @@ -111,18 +109,17 @@ public function process(ContainerBuilder $container): array { break; } } - if ($hook) { + if (isset($hookAttribute)) { foreach ($orderAttributes as $orderAttribute) { - // If $hook is not false then $currentModule is set. - /** @phpstan-ignore variable.undefined */ - $allOrderAttributes[] = $orderAttribute->set(hook: $hook, class: $class, method: $method, module: $currentModule); + $allOrderAttributes[] = $orderAttribute->set(hook: $hookAttribute, class: $class); } if ($orderGroup) { - $orderGroup[] = $hook; + $orderGroup[] = $hookAttribute->hook; foreach ($orderGroup as $extraHook) { $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $orderGroup); } } + unset($hookAttribute); } } } -- GitLab From 6c801f794afb9135e4b2cfd6566fa0cbff77ca99 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 12:18:55 -0500 Subject: [PATCH 059/268] Handle multiple hook attributes --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index f510cef342d0..afe3fa9a80fe 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -83,19 +83,20 @@ public function process(ContainerBuilder $container): array { foreach ($methods as $method => $attributes) { $orderAttributes = []; $orderGroup = FALSE; + $hookAttributes = []; foreach ($attributes as $attribute) { switch (TRUE) { case $attribute instanceof Hook: if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($attribute, $class); } - $hookAttribute = $attribute; if (!$attribute->module) { $attribute->module = $module; } if (!$attribute->method) { $attribute->method = $method; } + $hookAttributes[] = $attribute; $legacyImplementations[$attribute->hook][$attribute->module] = ''; $implementations[$attribute->hook][$attribute->module][$class][] = $attribute->method; break; @@ -109,11 +110,16 @@ public function process(ContainerBuilder $container): array { break; } } - if (isset($hookAttribute)) { + $hooksCount = count($hookAttributes); + if ($hooksCount === 1) { + $hookAttribute = reset($hookAttributes); foreach ($orderAttributes as $orderAttribute) { $allOrderAttributes[] = $orderAttribute->set(hook: $hookAttribute, class: $class); } if ($orderGroup) { + if (!$orderAttributes) { + throw new \LogicException('HookOrderGroup requires an order to be specified.'); + } $orderGroup[] = $hookAttribute->hook; foreach ($orderGroup as $extraHook) { $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $orderGroup); @@ -121,6 +127,9 @@ public function process(ContainerBuilder $container): array { } unset($hookAttribute); } + elseif ($hooksCount > 1 && $orderAttributes) { + throw new \LogicException('Hook ordering can only be applied to methods with one Hook attribute.'); + } } } } -- GitLab From 5624e558058ae8018b8ff55ef8cf093f82378097 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 14:53:17 -0500 Subject: [PATCH 060/268] Clean up exceptions --- .../Drupal/Core/Hook/Attribute/HookAfter.php | 5 +- .../Drupal/Core/Hook/Attribute/HookBefore.php | 5 +- .../Core/Hook/Attribute/HookOrderBase.php | 11 ---- .../Core/Hook/Attribute/HookOrderGroup.php | 2 +- .../Hook/Attribute/HookOrderInterface.php | 2 +- .../Drupal/Core/Hook/HookCollectorPass.php | 60 ++++++++++++++----- core/lib/Drupal/Core/Hook/HookPriority.php | 22 +++---- 7 files changed, 65 insertions(+), 42 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index 01a68e6f5e59..6e82bd17d848 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -18,10 +18,11 @@ class HookAfter extends HookOrderBase { * Constructs a HookAfter attribute. * * @param array $modules - * The modules this implementation should run before. + * A list of things this implementation should run after. Each thing is + * either a module name or a list of class and method. */ public function __construct( - public array $modules, + public readonly array $modules, ) { parent::__construct(FALSE); } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index ee87e431418c..bde7335ea576 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -18,10 +18,11 @@ class HookBefore extends HookOrderBase { * Constructs a HookBefore lib/Drupal/Core/Hook/Attribute/attribute. * * @param array $modules - * The modules this implementation should run before. + * A list of things this implementation should run before. Each thing is + * either a module name or a list of class and method. */ public function __construct( - public array $modules, + public readonly array $modules, ) { parent::__construct(TRUE); } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php index d222e979a1fb..f7297b3486e3 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php @@ -30,16 +30,6 @@ class HookOrderBase implements HookOrderInterface { */ public string $method; - /** - * The module of the hook. - * - * The module of the hook. Note this might be different from the module the - * function is in. - * - * @internal - */ - public string $module; - /** * Constructs a HookOrderBase class. * @@ -55,7 +45,6 @@ public function set(Hook $hook, string $class): static { $this->hook = $hook->hook; $this->class = $class; $this->method = $hook->method; - $this->module = $hook->module; return $this; } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php index 0e0b43044af6..c4f45c6728b1 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php @@ -38,7 +38,7 @@ class HookOrderGroup { * A list of hooks to sort together. */ public function __construct( - public array $group, + public readonly array $group, ) {} } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php index 3f8ddb76283a..edd5be7186e9 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php @@ -12,7 +12,7 @@ interface HookOrderInterface { /** * Set the properties on the attributes using this class. * - * @param Drupal\Core\Hook\Attribute\Hook $hook + * @param \Drupal\Core\Hook\Attribute\Hook $hook * The hook attribute to order. * @param string $class * The class the hook is in. diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index afe3fa9a80fe..ead37f80d1a1 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -15,6 +15,7 @@ use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; /** * Collects and registers hook implementations. @@ -110,24 +111,30 @@ public function process(ContainerBuilder $container): array { break; } } - $hooksCount = count($hookAttributes); + // If no ordering is required the processing of this method is done. + if (!$orderAttributes) { + if ($orderGroup) { + throw new \LogicException('HookOrderGroup requires an order to be specified.'); + } + continue; + } + // Now process ordering, if possible. + if (!$hooksCount = count($hookAttributes)) { + throw new \LogicException('Order attributes require a Hook attribute.'); + } if ($hooksCount === 1) { $hookAttribute = reset($hookAttributes); foreach ($orderAttributes as $orderAttribute) { $allOrderAttributes[] = $orderAttribute->set(hook: $hookAttribute, class: $class); } if ($orderGroup) { - if (!$orderAttributes) { - throw new \LogicException('HookOrderGroup requires an order to be specified.'); - } $orderGroup[] = $hookAttribute->hook; foreach ($orderGroup as $extraHook) { $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $orderGroup); } } - unset($hookAttribute); } - elseif ($hooksCount > 1 && $orderAttributes) { + else { throw new \LogicException('Hook ordering can only be applied to methods with one Hook attribute.'); } } @@ -198,11 +205,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl } foreach ($method_hooks as $method) { $map[$hook][$class][$method] = $module; - $definition->addTag('kernel.event_listener', [ - 'event' => "drupal_hook.$hook", - 'method' => $method, - 'priority' => $priority--, - ]); + $priority = self::addTagToDefinition($definition, "drupal_hook.$hook", $method, $priority); } } unset($implementations[$hook][$module]); @@ -243,9 +246,14 @@ protected static function reOrderServices(ContainerBuilder $container, array $al $others = []; foreach ($orderAttribute->modules as $module) { foreach ($hooks as $hook) { - foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { - foreach ($methods as $method) { - $others[] = [$class, $method, $module]; + if (is_array($module)) { + $others[] = $module; + } + else { + foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { + foreach ($methods as $method) { + $others[] = [$class, $method]; + } } } } @@ -495,4 +503,28 @@ protected static function getAttributeInstances(array $attributes, array $reflec return $attributes; } + /** + * Adds an event listener tag to a service definition. + * + * @param \Symfony\Component\DependencyInjection\Definition $definition + * The service definition. + * @param string $event + * The name of the event, typically starts with drupal_hook. + * @param string $method + * The method. + * @param int $priority + * The priority. + * + * @return int + * A new priority, guaranteed to be lower than $priority. + */ + public static function addTagToDefinition(Definition $definition, string $event, string $method, int $priority): int { + $definition->addTag('kernel.event_listener', [ + 'event' => $event, + 'method' => $method, + 'priority' => $priority--, + ]); + return $priority; + } + } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 2fd7c0b0f653..59bb9a89243e 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -28,7 +28,7 @@ public function __construct(protected ContainerBuilder $container) {} * The order attribute. * @param array|null $others * Other hook implementations to compare to, if any. The array is a list of - * lists, each containing class, method, module. + * class and method pairs. * * @internal */ @@ -44,16 +44,16 @@ public function change(array $hooks, HookOrderBase $attribute, ?array $others = $combinedHookTag = implode(':', $hooks); $event = "drupal_hook.$combinedHookTag"; $data = $others; - $data[] = [$attribute->class, $attribute->method, $attribute->module]; + $data[] = [$attribute->class, $attribute->method]; $priority = 0; - foreach ($data as [$class, $method, $module]) { - $definition = $this->container->findDefinition($class); - $definition->addTag('kernel.event_listener', [ - 'event' => "drupal_hook.$combinedHookTag", - 'method' => $method, - 'priority' => $priority--, - ]); - $map[$combinedHookTag][$class][$method] = $module; + foreach ($data as [$class, $method]) { + foreach ($hooks as $hook) { + if (isset($map[$hook][$class][$method])) { + $map[$combinedHookTag][$class][$method] = $map[$hook][$class][$method]; + $priority = HookCollectorPass::addTagToDefinition($this->container->findDefinition($class), $event, $method, $priority); + break; + } + } } $this->container->setParameter('hook_implementations_map', $map); } @@ -136,7 +136,7 @@ public function change(array $hooks, HookOrderBase $attribute, ?array $others = * @return void */ public function set(string $class, int $key, int $priority): void { - $definition = $this->container->getDefinition($class); + $definition = $this->container->findDefinition($class); $tags = $definition->getTags(); $tags['kernel.event_listener'][$key]['priority'] = $priority; $definition->setTags($tags); -- GitLab From d4d00e8a929362ce86772cd223aa7e39bb8e79ed Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 16:31:00 -0500 Subject: [PATCH 061/268] Reorganize test modules --- .../hook_order_first_alphabetically.info.yml} | 1 + .../src/Hook/TestHookAfter.php | 33 +++++++++++++++++ .../src/Hook/TestHookBefore.php | 35 ++++++++++++++++++ .../src/Hook/TestHookFirst.php | 35 ++++++++++++++++++ .../src/Hook/TestHookLast.php} | 18 ++++----- .../src/Hook/TestHookOrderGroup.php} | 17 ++++----- .../src/Hook/FirstAlphabeticallyHooks1.php | 37 ------------------- .../hook_order_first_alphabetically2.info.yml | 6 --- .../src/Hook/FirstAlphabeticallyHooks2.php | 35 ------------------ .../hook_order_first_alphabetically3.info.yml | 6 --- .../src/Hook/FirstAlphabeticallyHooks3.php | 37 ------------------- .../hook_order_first_alphabetically4.info.yml | 6 --- .../hook_order_first_alphabetically5.info.yml | 6 --- .../hook_order_last_alphabetically.info.yml} | 1 + .../src/Hook/TestHookAfter.php | 35 ++++++++++++++++++ .../src/Hook/TestHookBefore.php} | 18 ++++----- .../src/Hook/TestHookFirst.php} | 18 ++++----- .../src/Hook/TestHookLast.php | 35 ++++++++++++++++++ .../src/Hook/TestHookOrderGroup.php} | 15 +++----- .../hook_order_last_alphabetically2.info.yml | 6 --- .../src/Hook/LastAlphabeticallyHooks2.php | 37 ------------------- .../hook_order_last_alphabetically3.info.yml | 6 --- .../hook_order_last_alphabetically4.info.yml | 6 --- .../hook_order_last_alphabetically5.info.yml | 6 --- .../src/Hook/LastAlphabeticallyHooks5.php | 37 ------------------- 25 files changed, 212 insertions(+), 280 deletions(-) rename core/modules/system/tests/modules/{hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml => hook_order_first_alphabetically/hook_order_first_alphabetically.info.yml} (82%) create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php rename core/modules/system/tests/modules/{hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php => hook_order_first_alphabetically/src/Hook/TestHookLast.php} (52%) rename core/modules/system/tests/modules/{hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php => hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php} (66%) delete mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php delete mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml delete mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php delete mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml delete mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php delete mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml delete mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml rename core/modules/system/tests/modules/{hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml => hook_order_last_alphabetically/hook_order_last_alphabetically.info.yml} (82%) create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php rename core/modules/system/tests/modules/{hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php => hook_order_last_alphabetically/src/Hook/TestHookBefore.php} (53%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php => hook_order_last_alphabetically/src/Hook/TestHookFirst.php} (52%) create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php rename core/modules/system/tests/modules/{hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php => hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php} (55%) delete mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml delete mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php delete mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml delete mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml delete mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml delete mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically/hook_order_first_alphabetically.info.yml similarity index 82% rename from core/modules/system/tests/modules/hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml rename to core/modules/system/tests/modules/hook_order_first_alphabetically/hook_order_first_alphabetically.info.yml index d61a2ddc1212..bd8dc0b2f73d 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically1/hook_order_first_alphabetically1.info.yml +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/hook_order_first_alphabetically.info.yml @@ -3,4 +3,5 @@ type: module description: 'Test module used to test hook ordering.' package: Testing version: VERSION +core_version_requirement: '*' hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php new file mode 100644 index 000000000000..49748e2eb9fa --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookAfter; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering attribute. + */ +class TestHookAfter { + + /** + * This pair tests #[HookAfter]. + */ + #[HookAfter(['hook_order_last_alphabetically'])] + #[Hook('custom_hook_test_hook_after')] + public static function hookAfter(): void { + $GLOBALS['HookAfter'] = 'HookAfter'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php new file mode 100644 index 000000000000..6ac7468053d6 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering attribute. + */ +class TestHookBefore { + + /** + * This pair tests #[HookBefore]. + */ + #[Hook('custom_hook_test_hook_before')] + public static function hookBefore(): void { + // This should be run after so HookBefore should not be set. + if (!isset($GLOBALS['HookBefore'])) { + $GLOBALS['HookOutOfOrderTestingHookBefore'] = 'HookOutOfOrderTestingHookBefore'; + } + $GLOBALS['HookRanTestingHookBefore'] = 'HookRanTestingHookBefore'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php new file mode 100644 index 000000000000..012e3f0d2c54 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering attribute. + */ +class TestHookFirst { + + /** + * This pair tests #[HookFirst]. + */ + #[Hook('custom_hook_test_hook_first')] + public static function hookFirst(): void { + // This should be run after so HookFirst should not be set. + if (!isset($GLOBALS['HookFirst'])) { + $GLOBALS['HookOutOfOrderTestingHookFirst'] = 'HookOutOfOrderTestingHookFirst'; + } + $GLOBALS['HookRanTestingHookFirst'] = 'HookRanTestingHookFirst'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php similarity index 52% rename from core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php rename to core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php index 540cd86c604c..0a82ba6e55b3 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically5/src/Hook/FirstAlphabeticallyHooks5.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically5\Hook; +namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookLast; @@ -11,24 +11,22 @@ * Hook implementations for verifying ordering hooks by attributes. * * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. * * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookLast]. + * Each method pair tests one hook ordering attribute. */ -class FirstAlphabeticallyHooks5 { +class TestHookLast { /** - * After LastAlphabeticallyHooks5::cacheFlush. + * This pair tests #[HookLast]. */ #[HookLast] - #[Hook('cache_flush')] - public static function cacheFlush(): void { + #[Hook('custom_hook_test_hook_last')] + public static function hookLast(): void { $GLOBALS['HookLast'] = 'HookLast'; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php similarity index 66% rename from core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php rename to core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php index 08eef7984c09..be8b4274cc26 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically4/src/Hook/FirstAlphabeticallyHooks4.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically4\Hook; +namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookAfter; @@ -12,23 +12,20 @@ * Hook implementations for verifying ordering hooks by attributes. * * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. * * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookOrderGroup]. - * This attribute must be paired with #[HookAfter] or #[HookBefore]. + * Each method pair tests one hook ordering attribute. */ -class FirstAlphabeticallyHooks4 { +class TestHookOrderGroup { /** - * After LastAlphabeticallyHooks4::customHookExtraTypes. + * This pair tests #[HookOrderGroup]. */ - #[HookAfter(['hook_order_last_alphabetically4'])] + #[HookAfter(['hook_order_last_alphabetically'])] #[HookOrderGroup(['custom_hook_extra_types2_alter'])] #[Hook('custom_hook_extra_types1_alter')] public static function customHookExtraTypes(): void { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php deleted file mode 100644 index f197cea8d043..000000000000 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically1/src/Hook/FirstAlphabeticallyHooks1.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hook_order_first_alphabetically1\Hook; - -use Drupal\Core\Hook\Attribute\Hook; - -/** - * Hook implementations for verifying ordering hooks by attributes. - * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. - * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookFirst]. - */ -class FirstAlphabeticallyHooks1 { - - /** - * After LastAlphabeticallyHooks1::cacheFlush. - */ - #[Hook('cache_flush')] - public static function cacheFlush(): void { - // This should be run after so HookFirst should not be set. - if (!isset($GLOBALS['HookFirst'])) { - $GLOBALS['HookOutOfOrderTestingFirst'] = 'HookOutOfOrderTestingFirst'; - } - $GLOBALS['HookRanTestingFirst'] = 'HookRanTestingFirst'; - } - -} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml deleted file mode 100644 index d61a2ddc1212..000000000000 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically2/hook_order_first_alphabetically2.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: first alphabetically -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php deleted file mode 100644 index 341688ce600d..000000000000 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically2/src/Hook/FirstAlphabeticallyHooks2.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hook_order_first_alphabetically2\Hook; - -use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookAfter; - -/** - * Hook implementations for verifying ordering hooks by attributes. - * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. - * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookAfter]. - */ -class FirstAlphabeticallyHooks2 { - - /** - * After LastAlphabeticallyHooks2::cacheFlush. - */ - #[HookAfter(['hook_order_last_alphabetically2'])] - #[Hook('cache_flush')] - public static function cacheFlush(): void { - $GLOBALS['HookAfter'] = 'HookAfter'; - } - -} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml deleted file mode 100644 index d61a2ddc1212..000000000000 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically3/hook_order_first_alphabetically3.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: first alphabetically -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php deleted file mode 100644 index 479c7d85d149..000000000000 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically3/src/Hook/FirstAlphabeticallyHooks3.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hook_order_first_alphabetically3\Hook; - -use Drupal\Core\Hook\Attribute\Hook; - -/** - * Hook implementations for verifying ordering hooks by attributes. - * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. - * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookBefore]. - */ -class FirstAlphabeticallyHooks3 { - - /** - * After LastAlphabeticallyHooks3::cacheFlush. - */ - #[Hook('cache_flush')] - public static function cacheFlush(): void { - // This should be run after so HookBefore should not be set. - if (!isset($GLOBALS['HookBefore'])) { - $GLOBALS['HookOutOfOrderTestingBefore'] = 'HookOutOfOrderTestingBefore'; - } - $GLOBALS['HookRanTestingBefore'] = 'HookRanTestingBefore'; - } - -} diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml deleted file mode 100644 index d61a2ddc1212..000000000000 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically4/hook_order_first_alphabetically4.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: first alphabetically -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml b/core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml deleted file mode 100644 index d61a2ddc1212..000000000000 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically5/hook_order_first_alphabetically5.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: first alphabetically -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically/hook_order_last_alphabetically.info.yml similarity index 82% rename from core/modules/system/tests/modules/hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml rename to core/modules/system/tests/modules/hook_order_last_alphabetically/hook_order_last_alphabetically.info.yml index 59334b38d2fc..aa08f259dc2e 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically1/hook_order_last_alphabetically1.info.yml +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/hook_order_last_alphabetically.info.yml @@ -3,4 +3,5 @@ type: module description: 'Test module used to test hook ordering.' package: Testing version: VERSION +core_version_requirement: '*' hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php new file mode 100644 index 000000000000..c01e15b71f61 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering attribute. + */ +class TestHookAfter { + + /** + * This pair tests #[HookAfter]. + */ + #[Hook('custom_hook_test_hook_after')] + public static function hookAfter(): void { + // This should be run before so HookAfter should not be set. + if (isset($GLOBALS['HookAfter'])) { + $GLOBALS['HookOutOfOrderTestingHookAfter'] = 'HookOutOfOrderTestingHookAfter'; + } + $GLOBALS['HookRanTestingHookAfter'] = 'HookRanTestingHookAfter'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php similarity index 53% rename from core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php rename to core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php index a25ac5454247..946377092261 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically3/src/Hook/LastAlphabeticallyHooks3.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically3\Hook; +namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookBefore; @@ -11,23 +11,21 @@ * Hook implementations for verifying ordering hooks by attributes. * * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. * * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookBefore]. + * Each method pair tests one hook ordering attribute. */ -class LastAlphabeticallyHooks3 { +class TestHookBefore { /** - * Before FirstAlphabeticallyHooks3::cacheFlush. + * This pair tests #[HookBefore]. */ - #[HookBefore(['hook_order_first_alphabetically3'])] - #[Hook('cache_flush')] + #[HookBefore(['hook_order_first_alphabetically'])] + #[Hook('custom_hook_test_hook_before')] public static function cacheFlush(): void { $GLOBALS['HookBefore'] = 'HookBefore'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php similarity index 52% rename from core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php rename to core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php index 059e95d0f4c1..3980b23bfb85 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically1/src/Hook/LastAlphabeticallyHooks1.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically1\Hook; +namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookFirst; @@ -11,24 +11,22 @@ * Hook implementations for verifying ordering hooks by attributes. * * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. * * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookFirst]. + * Each method pair tests one hook ordering attribute. */ -class LastAlphabeticallyHooks1 { +class TestHookFirst { /** - * Before FirstAlphabeticallyHooks1::cacheFlush. + * This pair tests #[HookFirst]. */ #[HookFirst] - #[Hook('cache_flush')] - public static function cacheFlush(): void { + #[Hook('custom_hook_test_hook_first')] + public static function hookFirst(): void { $GLOBALS['HookFirst'] = 'HookFirst'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php new file mode 100644 index 000000000000..864016a538b1 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering attribute. + */ +class TestHookLast { + + /** + * This pair tests #[HookLast]. + */ + #[Hook('custom_hook_test_hook_last')] + public static function hookLast(): void { + // This should be run before so HookLast should not be set. + if (isset($GLOBALS['HookLast'])) { + $GLOBALS['HookOutOfOrderTestingHookLast'] = 'HookOutOfOrderTestingHookLast'; + } + $GLOBALS['HookRanTestingHookLast'] = 'HookRanTestingHookLast'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php similarity index 55% rename from core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php rename to core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php index cc7244a9eeb6..607a9d376290 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically4/src/Hook/LastAlphabeticallyHooks4.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically4\Hook; +namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; @@ -10,21 +10,18 @@ * Hook implementations for verifying ordering hooks by attributes. * * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. * * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookOrderGroup]. - * This attribute must be paired with #[HookAfter] or #[HookBefore]. + * Each method pair tests one hook ordering attribute. */ -class LastAlphabeticallyHooks4 { +class TestHookOrderGroup { /** - * Before FirstAlphabeticallyHooks4::customHookExtraTypes. + * This pair tests #[HookOrderGroup]. */ #[Hook('custom_hook_extra_types2_alter')] public static function customHookExtraTypes(): void { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml deleted file mode 100644 index 59334b38d2fc..000000000000 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically2/hook_order_last_alphabetically2.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: Hook ordering last -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php b/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php deleted file mode 100644 index 02df5b541c46..000000000000 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically2/src/Hook/LastAlphabeticallyHooks2.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hook_order_last_alphabetically2\Hook; - -use Drupal\Core\Hook\Attribute\Hook; - -/** - * Hook implementations for verifying ordering hooks by attributes. - * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. - * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookAfter]. - */ -class LastAlphabeticallyHooks2 { - - /** - * Before FirstAlphabeticallyHooks2::cacheFlush. - */ - #[Hook('cache_flush')] - public static function cacheFlush(): void { - // This should be run before so HookAfter should not be set. - if (isset($GLOBALS['HookAfter'])) { - $GLOBALS['HookOutOfOrderTestingAfter'] = 'HookOutOfOrderTestingAfter'; - } - $GLOBALS['HookRanTestingAfter'] = 'HookRanTestingAfter'; - } - -} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml deleted file mode 100644 index 59334b38d2fc..000000000000 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically3/hook_order_last_alphabetically3.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: Hook ordering last -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml deleted file mode 100644 index 59334b38d2fc..000000000000 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically4/hook_order_last_alphabetically4.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: Hook ordering last -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml b/core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml deleted file mode 100644 index 59334b38d2fc..000000000000 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically5/hook_order_last_alphabetically5.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: Hook ordering last -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php b/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php deleted file mode 100644 index 75a0dbe4c785..000000000000 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically5/src/Hook/LastAlphabeticallyHooks5.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hook_order_last_alphabetically5\Hook; - -use Drupal\Core\Hook\Attribute\Hook; - -/** - * Hook implementations for verifying ordering hooks by attributes. - * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. All of these modules - * come in a pair first alphabetically and last alphabetically. - * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each pair tests one hook order attribute. - * - * This pair tests #[HookLast]. - */ -class LastAlphabeticallyHooks5 { - - /** - * Before FirstAlphabeticallyHooks5::cacheFlush. - */ - #[Hook('cache_flush')] - public static function cacheFlush(): void { - // This should be run before so HookLast should not be set. - if (isset($GLOBALS['HookLast'])) { - $GLOBALS['HookOutOfOrderTestingLast'] = 'HookOutOfOrderTestingLast'; - } - $GLOBALS['HookRanTestingLast'] = 'HookRanTestingLast'; - } - -} -- GitLab From ed01479689a30dccdf12e9a0bbe1a59bd8c0b399 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 16:31:52 -0500 Subject: [PATCH 062/268] Update test to use new modules --- .../Core/Hook/HookCollectorPassTest.php | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 9412573232ca..9db34afb478f 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -141,15 +141,17 @@ public function testHookAttribute(): void { */ public function testHookFirst(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically1'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically1'])); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); $this->assertFalse(isset($GLOBALS['HookFirst'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingFirst'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingFirst'])); - drupal_flush_all_caches(); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookFirst'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingHookFirst'])); + $module_handler = $this->container->get('module_handler'); + $data = ['hi']; + $module_handler->invokeAll('custom_hook_test_hook_first', $data); $this->assertTrue(isset($GLOBALS['HookFirst'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingFirst'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingFirst'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookFirst'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingHookFirst'])); } /** @@ -157,15 +159,17 @@ public function testHookFirst(): void { */ public function testHookAfter(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically2'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically2'])); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); $this->assertFalse(isset($GLOBALS['HookAfter'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingAfter'])); - drupal_flush_all_caches(); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfter'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingHookAfter'])); + $module_handler = $this->container->get('module_handler'); + $data = ['hi']; + $module_handler->invokeAll('custom_hook_test_hook_after', $data); $this->assertTrue(isset($GLOBALS['HookAfter'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingAfter'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingAfter'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfter'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingHookAfter'])); } /** @@ -173,15 +177,17 @@ public function testHookAfter(): void { */ public function testHookBefore(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically3'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically3'])); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); $this->assertFalse(isset($GLOBALS['HookBefore'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingBefore'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingBefore'])); - drupal_flush_all_caches(); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookBefore'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingHookBefore'])); + $module_handler = $this->container->get('module_handler'); + $data = ['hi']; + $module_handler->invokeAll('custom_hook_test_hook_before', $data); $this->assertTrue(isset($GLOBALS['HookBefore'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingBefore'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingBefore'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookBefore'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingHookBefore'])); } /** @@ -189,8 +195,8 @@ public function testHookBefore(): void { */ public function testHookOrderGroup(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically4'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically4'])); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); $this->assertFalse(isset($GLOBALS['HookOrderGroupExtraTypes'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'])); $this->assertFalse(isset($GLOBALS['HookRanTestingOrderGroupsExtraTypes'])); @@ -212,15 +218,17 @@ public function testHookOrderGroup(): void { */ public function testHookLast(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically5'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically5'])); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); $this->assertFalse(isset($GLOBALS['HookLast'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingLast'])); - drupal_flush_all_caches(); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookLast'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingHookLast'])); + $module_handler = $this->container->get('module_handler'); + $data = ['hi']; + $module_handler->invokeAll('custom_hook_test_hook_last', $data); $this->assertTrue(isset($GLOBALS['HookLast'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingLast'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingLast'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookLast'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingHookLast'])); } } -- GitLab From fa4fc002007bcea71c95603825acbb8d0bdc9c2f Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 16:58:21 -0500 Subject: [PATCH 063/268] Test ordering on a class and method --- ...second_order_first_alphabetically.info.yml | 7 ++++ .../src/Hook/TestHookAfterClassMethod.php | 33 +++++++++++++++++ ..._second_order_last_alphabetically.info.yml | 7 ++++ .../src/Hook/TestHookAfterClassMethod.php | 35 +++++++++++++++++++ .../Core/Hook/HookCollectorPassTest.php | 18 ++++++++++ 5 files changed, 100 insertions(+) create mode 100644 core/modules/system/tests/modules/hook_second_order_first_alphabetically/hook_second_order_first_alphabetically.info.yml create mode 100644 core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php create mode 100644 core/modules/system/tests/modules/hook_second_order_last_alphabetically/hook_second_order_last_alphabetically.info.yml create mode 100644 core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/hook_second_order_first_alphabetically.info.yml b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/hook_second_order_first_alphabetically.info.yml new file mode 100644 index 000000000000..bd8dc0b2f73d --- /dev/null +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/hook_second_order_first_alphabetically.info.yml @@ -0,0 +1,7 @@ +name: first alphabetically +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +core_version_requirement: '*' +hidden: true diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php new file mode 100644 index 000000000000..8402e415bc6c --- /dev/null +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_second_order_first_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookAfter; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering attribute. + */ +class TestHookAfterClassMethod { + + /** + * This pair tests #[HookAfter] with a passed class and method. + */ + #[HookAfter(['hook_second_order_last_alphabetically', 'TestHookAfterClassMethod::hookAfterClassMethod'])] + #[Hook('custom_hook_test_hook_after_class_method')] + public static function hookAfterClassMethod(): void { + $GLOBALS['HookAfterClassMethod'] = 'HookAfterMethod'; + } + +} diff --git a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/hook_second_order_last_alphabetically.info.yml b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/hook_second_order_last_alphabetically.info.yml new file mode 100644 index 000000000000..aa08f259dc2e --- /dev/null +++ b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/hook_second_order_last_alphabetically.info.yml @@ -0,0 +1,7 @@ +name: Hook ordering last +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +core_version_requirement: '*' +hidden: true diff --git a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php new file mode 100644 index 000000000000..dc3e6db8d2c3 --- /dev/null +++ b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_second_order_last_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering attribute. + */ +class TestHookAfterClassMethod { + + /** + * This pair tests #[HookAfter]. + */ + #[Hook('custom_hook_test_hook_after_class_method')] + public static function hookAfterClassMethod(): void { + // This should be run before so HookAfter should not be set. + if (isset($GLOBALS['HookAfterClassMethod'])) { + $GLOBALS['HookOutOfOrderTestingHookAfterClassMethod'] = 'HookOutOfOrderTestingHookAfterClassMethod'; + } + $GLOBALS['HookRanTestingHookAfterClassMethod'] = 'HookRanTestingHookAfterClassMethod'; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 9db34afb478f..7109df1e9357 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -172,6 +172,24 @@ public function testHookAfter(): void { $this->assertTrue(isset($GLOBALS['HookRanTestingHookAfter'])); } + /** + * Tests hook ordering with attributes. + */ + public function testHookAfterClassMethod(): void { + $module_installer = $this->container->get('module_installer'); + $this->assertTrue($module_installer->install(['hook_second_order_first_alphabetically'])); + $this->assertTrue($module_installer->install(['hook_second_order_last_alphabetically'])); + $this->assertFalse(isset($GLOBALS['HookAfterClassMethod'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfterClassMethod'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingHookAfterClassMethod'])); + $module_handler = $this->container->get('module_handler'); + $data = ['hi']; + $module_handler->invokeAll('custom_hook_test_hook_after_class_method', $data); + $this->assertTrue(isset($GLOBALS['HookAfterClassMethod'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfterClassMethod'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingHookAfterClassMethod'])); + } + /** * Tests hook ordering with attributes. */ -- GitLab From 4c936860c13d0f442fa085561d0745d647d9fd2c Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 17:22:04 -0500 Subject: [PATCH 064/268] Rename variable for clarity and test reordering on class method --- core/lib/Drupal/Core/Hook/Attribute/HookAfter.php | 7 +++---- core/lib/Drupal/Core/Hook/Attribute/HookBefore.php | 7 +++---- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 10 +++++----- .../src/Hook/TestHookAfterClassMethod.php | 3 ++- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php index 6e82bd17d848..ce92b5512836 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php @@ -17,12 +17,11 @@ class HookAfter extends HookOrderBase { /** * Constructs a HookAfter attribute. * - * @param array $modules - * A list of things this implementation should run after. Each thing is - * either a module name or a list of class and method. + * @param array $orderings + * Each ordering is either a module name or a class and method pair array. */ public function __construct( - public readonly array $modules, + public readonly array $orderings, ) { parent::__construct(FALSE); } diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php index bde7335ea576..1c26d7db4171 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php @@ -17,12 +17,11 @@ class HookBefore extends HookOrderBase { /** * Constructs a HookBefore lib/Drupal/Core/Hook/Attribute/attribute. * - * @param array $modules - * A list of things this implementation should run before. Each thing is - * either a module name or a list of class and method. + * @param array $orderings + * Each ordering is either a module name or a class and method pair array. */ public function __construct( - public readonly array $modules, + public readonly array $orderings, ) { parent::__construct(TRUE); } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index ead37f80d1a1..fa2e9f2551ff 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -242,15 +242,15 @@ protected static function reOrderServices(ContainerBuilder $container, array $al // ::process() adds the hook serving as key to the order group so it // does not need to be added if there's a group for the hook. $hooks = $orderGroups[$orderAttribute->hook] ?? [$orderAttribute->hook]; - if (isset($orderAttribute->modules)) { + if (isset($orderAttribute->orderings)) { $others = []; - foreach ($orderAttribute->modules as $module) { + foreach ($orderAttribute->orderings as $ordering) { foreach ($hooks as $hook) { - if (is_array($module)) { - $others[] = $module; + if (is_array($ordering)) { + $others[] = $ordering; } else { - foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { + foreach ($implementations[$hook][$ordering] ?? [] as $class => $methods) { foreach ($methods as $method) { $others[] = [$class, $method]; } diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php index 8402e415bc6c..1752a787e9a6 100644 --- a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -6,6 +6,7 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; /** * Hook implementations for verifying ordering hooks by attributes. @@ -24,7 +25,7 @@ class TestHookAfterClassMethod { /** * This pair tests #[HookAfter] with a passed class and method. */ - #[HookAfter(['hook_second_order_last_alphabetically', 'TestHookAfterClassMethod::hookAfterClassMethod'])] + #[HookAfter([[TestHookAfterClassMethodForAfter::class, 'hookAfterClassMethod']])] #[Hook('custom_hook_test_hook_after_class_method')] public static function hookAfterClassMethod(): void { $GLOBALS['HookAfterClassMethod'] = 'HookAfterMethod'; -- GitLab From 92e8d1f9d1e5b6f9a13e60db6ff34d49a6c4f5f6 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 18:43:19 -0500 Subject: [PATCH 065/268] Rename variables for consistency --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 11 ++++------- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 12 ++++-------- core/lib/Drupal/Core/Hook/HookPriority.php | 2 -- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index e44e4396ab5a..ea72ec0acbec 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -80,13 +80,13 @@ class ModuleHandler implements ModuleHandlerInterface { * An array keyed by hook, classname, method and the value is the module. * @param array $groupIncludes * An array of .inc files to get helpers from. - * @param array $hooksOrderedByAttributes + * @param array $orderGroups * An array of hooks that have been ordered by attributes. * * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider */ - public function __construct($root, array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], protected array $hooksOrderedByAttributes = []) { + public function __construct($root, array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], protected array $orderGroups = []) { $this->root = $root; $this->moduleList = []; foreach ($module_list as $name => $module) { @@ -451,11 +451,8 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // used for ordering because the group might contain hooks not included // in this alter() call. foreach (array_merge($extra_hooks, [$type . '_alter']) as $extra_hook) { - if (isset($this->hooksOrderedByAttributes[$extra_hook])) { - $group = $this->hooksOrderedByAttributes[$extra_hook]; - // When checking for already ordered groups ensure the listener - // is in the same order as when we set it. - krsort($group); + if (isset($this->orderGroups[$extra_hook])) { + $group = $this->orderGroups[$extra_hook]; $extra_listeners = $this->findListenersForAlter(implode(':', $group)); // Remove already ordered hooks. $extra_types = array_diff($extra_hooks, $group); diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index fa2e9f2551ff..adc96f9af1af 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -166,12 +166,12 @@ public function process(ContainerBuilder $container): array { * All implementations. * @param array $legacyImplementations * Modules that implement hooks. - * @param array $reorderGroups + * @param array $orderGroups * Groups of hooks to reorder. * * @return void */ - protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementations, array $reorderGroups): void { + protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementations, array $orderGroups): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); $groupIncludes = []; @@ -184,7 +184,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl } foreach ($legacyImplementations as $hook => $moduleImplements) { - $extraHooks = $reorderGroups[$hook] ?? []; + $extraHooks = $orderGroups[$hook] ?? []; foreach ($extraHooks as $extraHook) { $moduleImplements += $legacyImplementations[$extraHook] ?? []; } @@ -212,13 +212,9 @@ protected static function registerServices(ContainerBuilder $container, HookColl } } - $hooksOrderedByAttributes = []; - foreach ($reorderGroups as $key => $values) { - $hooksOrderedByAttributes[$key] = $values; - } $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$hooksOrderedByAttributes', $hooksOrderedByAttributes); + $definition->setArgument('$orderGroups', $orderGroups); $container->setParameter('hook_implementations_map', $map ?? []); } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 59bb9a89243e..b507d809dbff 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -39,8 +39,6 @@ public function change(array $hooks, HookOrderBase $attribute, ?array $others = } if (count($hooks) > 1) { $map = $this->container->getParameter('hook_implementations_map'); - // Order the complex listener so we can find it runtime. - krsort($hooks); $combinedHookTag = implode(':', $hooks); $event = "drupal_hook.$combinedHookTag"; $data = $others; -- GitLab From 6a78fa2cc45be422fbb68ff4cddacf79808f48ef Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 20:38:54 -0500 Subject: [PATCH 066/268] Unified approach --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 10 ++ core/lib/Drupal/Core/Hook/Attribute/Order.php | 20 +++ .../Drupal/Core/Hook/Attribute/OrderType.php | 19 +++ .../Core/Hook/Attribute/SimpleOrderType.php | 19 +++ .../Drupal/Core/Hook/HookCollectorPass.php | 119 ++++++------------ core/lib/Drupal/Core/Hook/HookPriority.php | 17 +-- .../ckeditor5/src/Hook/Ckeditor5Hooks.php | 12 +- 7 files changed, 127 insertions(+), 89 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/Attribute/Order.php create mode 100644 core/lib/Drupal/Core/Hook/Attribute/OrderType.php create mode 100644 core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 1b220577a131..1d31055255fa 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -90,6 +90,8 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Hook { + public string $class = ''; + /** * Constructs a Hook attribute object. * @@ -109,6 +111,7 @@ public function __construct( public string $hook, public string $method = '', public ?string $module = NULL, + public Order|SimpleOrderType|NULL $order = NULL, ) {} /** @@ -117,10 +120,17 @@ public function __construct( * @param string $method * The method that the hook attribute applies to. * This only needs to be set when the attribute is on the class. + * + * @internal */ public function setMethod(string $method): static { $this->method = $method; return $this; } + public function setClass(string $class): static { + $this->class = $class; + return $this; + } + } diff --git a/core/lib/Drupal/Core/Hook/Attribute/Order.php b/core/lib/Drupal/Core/Hook/Attribute/Order.php new file mode 100644 index 000000000000..6a2bbf8cd09e --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/Order.php @@ -0,0 +1,20 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +class Order { + + public function __construct( + public ?OrderType $type, + public ?array $modules = [], + public ?array $classesAndMethods = [], + public ?array $group = NULL, + ) { + if (!$this->modules && !$this->classesAndMethods) { + throw new \LogicException('Order against what?'); + } + } + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/OrderType.php b/core/lib/Drupal/Core/Hook/Attribute/OrderType.php new file mode 100644 index 000000000000..1855b54c6716 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/OrderType.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +enum OrderType { + + case Before; + case After; + + public function shouldBeLast(): bool { + return match($this) { + OrderType::Before => TRUE, + OrderType::After => FALSE, + }; + } + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php b/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php new file mode 100644 index 000000000000..7957b1198df2 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +enum SimpleOrderType { + + case First; + case Last; + + public function shouldBeLast(): bool { + return match($this) { + SimpleOrderType::First => TRUE, + SimpleOrderType::Last => FALSE, + }; + } + +} diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index adc96f9af1af..6396e3e6eab2 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -9,9 +9,8 @@ use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookOrderGroup; -use Drupal\Core\Hook\Attribute\HookOrderInterface; use Drupal\Core\Hook\Attribute\LegacyHook; +use Drupal\Core\Hook\Attribute\Order; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -61,14 +60,9 @@ class HookCollectorPass implements CompilerPassInterface { /** * A list of attributes for hook implementations. * - * Keys are module, class and method. Values are all possible attributes on - * hook implementations: Hook to define the hook, HookOrderInterface to - * define the order in which the implementations fire, HookOrderGroup to - * define a group of hooks to be ordered together. - * - * @var array<string, <array string, <array string, Hook|HookOrderInterface|HookOrderGroup>>> + * Keys are module, class and method. values are Hook attributes. */ - protected array $moduleAttributes = []; + protected array $moduleHooks = []; /** * {@inheritdoc} @@ -77,66 +71,33 @@ public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); $implementations = []; $orderGroups = []; - /** @var \Drupal\Core\Hook\Attribute\HookOrderBase[] $allOrderAttributes */ - $allOrderAttributes = []; + $orderAttributes = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { - foreach ($collector->moduleAttributes[$module] ?? [] as $class => $methods) { - foreach ($methods as $method => $attributes) { - $orderAttributes = []; - $orderGroup = FALSE; - $hookAttributes = []; - foreach ($attributes as $attribute) { - switch (TRUE) { - case $attribute instanceof Hook: - if ($class !== ProceduralCall::class) { - self::checkForProceduralOnlyHooks($attribute, $class); - } - if (!$attribute->module) { - $attribute->module = $module; - } - if (!$attribute->method) { - $attribute->method = $method; - } - $hookAttributes[] = $attribute; - $legacyImplementations[$attribute->hook][$attribute->module] = ''; - $implementations[$attribute->hook][$attribute->module][$class][] = $attribute->method; - break; - - case $attribute instanceof HookOrderInterface: - $orderAttributes[] = $attribute; - break; - - case $attribute instanceof HookOrderGroup: - $orderGroup = $attribute->group; - break; + foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { + foreach ($methods as $method => $hooks) { + foreach ($hooks as $hook) { + assert($hook instanceof Hook); + $hook->setClass($class); + if ($class !== ProceduralCall::class) { + self::checkForProceduralOnlyHooks($hook); } - } - // If no ordering is required the processing of this method is done. - if (!$orderAttributes) { - if ($orderGroup) { - throw new \LogicException('HookOrderGroup requires an order to be specified.'); + if (!$hook->module) { + $hook->module = $module; } - continue; - } - // Now process ordering, if possible. - if (!$hooksCount = count($hookAttributes)) { - throw new \LogicException('Order attributes require a Hook attribute.'); - } - if ($hooksCount === 1) { - $hookAttribute = reset($hookAttributes); - foreach ($orderAttributes as $orderAttribute) { - $allOrderAttributes[] = $orderAttribute->set(hook: $hookAttribute, class: $class); + if (!$hook->method) { + $hook->method = $method; } - if ($orderGroup) { - $orderGroup[] = $hookAttribute->hook; - foreach ($orderGroup as $extraHook) { - $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $orderGroup); + $legacyImplementations[$hook->hook][$hook->module] = ''; + $implementations[$hook->hook][$hook->module][$class][] = $hook->method; + if ($hook->order) { + $orderAttributes[] = $hook; + if ($hook->order instanceof Order && $hook->order->group) { + foreach ($hook->order->group as $extraHook) { + $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $hook->order->group); + } } } } - else { - throw new \LogicException('Hook ordering can only be applied to methods with one Hook attribute.'); - } } } } @@ -147,7 +108,7 @@ public function process(ContainerBuilder $container): array { // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { static::registerServices($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); - static::reOrderServices($container, $allOrderAttributes, $orderGroups, $implementations); + static::reOrderServices($container, $orderAttributes, $orderGroups, $implementations); } return $implementations; } @@ -223,7 +184,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. - * @param array $allOrderAttributes + * @param array $orderAttributes * All attributes related to ordering. * @param array $orderGroups * Groups to order by. @@ -232,28 +193,25 @@ protected static function registerServices(ContainerBuilder $container, HookColl * * @return void */ - protected static function reOrderServices(ContainerBuilder $container, array $allOrderAttributes, array $orderGroups, array $implementations): void { + protected static function reOrderServices(ContainerBuilder $container, array $orderAttributes, array $orderGroups, array $implementations): void { $hookPriority = new HookPriority($container); - foreach ($allOrderAttributes as $orderAttribute) { + foreach ($orderAttributes as $orderAttribute) { + assert($orderAttribute instanceof Hook); // ::process() adds the hook serving as key to the order group so it // does not need to be added if there's a group for the hook. $hooks = $orderGroups[$orderAttribute->hook] ?? [$orderAttribute->hook]; - if (isset($orderAttribute->orderings)) { + if ($orderAttribute->order instanceof Order) { $others = []; - foreach ($orderAttribute->orderings as $ordering) { + foreach ($orderAttribute->order->modules as $modules) { foreach ($hooks as $hook) { - if (is_array($ordering)) { - $others[] = $ordering; - } - else { - foreach ($implementations[$hook][$ordering] ?? [] as $class => $methods) { - foreach ($methods as $method) { - $others[] = [$class, $method]; - } + foreach ($implementations[$hook][$modules] ?? [] as $class => $methods) { + foreach ($methods as $method) { + $others[] = [$class, $method]; } } } } + $others = array_merge($others, $orderAttribute->order->classesAndMethods); } else { $others = NULL; @@ -348,7 +306,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } } - $this->moduleAttributes[$module][$class] = $attributes; + $this->moduleHooks[$module][$class] = $attributes; } elseif (!$skip_procedural) { $implementations = $procedural_hook_file_cache->get($filename); @@ -412,7 +370,7 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv * The name of function implementing the hook. */ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { - $this->moduleAttributes[$module][ProceduralCall::class][$function] = [new Hook($hook, $module . '_' . $hook)]; + $this->moduleHooks[$module][ProceduralCall::class][$function] = [new Hook($hook, $module . '_' . $hook)]; if ($hook === 'hook_info') { $this->hookInfo[] = $function; } @@ -460,7 +418,7 @@ public function getImplementations($paths): array { * @param string $class * The class the hook is implemented on. */ - public static function checkForProceduralOnlyHooks(Hook $hook, string $class): void { + public static function checkForProceduralOnlyHooks(Hook $hook, string $class = ''): void { $staticDenyHooks = [ 'hook_info', 'install', @@ -474,6 +432,9 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v ]; if (in_array($hook->hook, $staticDenyHooks) || preg_match('/^(post_update_|preprocess_|update_\d+$)/', $hook->hook)) { + if (!$class) { + $class = $hook->class; + } throw new \LogicException("The hook $hook->hook on class $class does not support attributes and must remain procedural."); } } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index b507d809dbff..775610f98ec1 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -4,7 +4,9 @@ namespace Drupal\Core\Hook; +use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookOrderBase; +use Drupal\Core\Hook\Attribute\SimpleOrderType; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -32,8 +34,8 @@ public function __construct(protected ContainerBuilder $container) {} * * @internal */ - public function change(array $hooks, HookOrderBase $attribute, ?array $others = NULL): void { - $class_and_method = "$attribute->class::$attribute->method"; + public function change(array $hooks, Hook $hook, ?array $others = NULL): void { + $class_and_method = "$hook->class::$hook->method"; if ($others) { $other_specifiers = array_map(fn ($pair) => $pair[0] . '::' . $pair[1], $others); } @@ -42,7 +44,7 @@ public function change(array $hooks, HookOrderBase $attribute, ?array $others = $combinedHookTag = implode(':', $hooks); $event = "drupal_hook.$combinedHookTag"; $data = $others; - $data[] = [$attribute->class, $attribute->method]; + $data[] = [$hook->class, $hook->method]; $priority = 0; foreach ($data as [$class, $method]) { foreach ($hooks as $hook) { @@ -84,18 +86,19 @@ public function change(array $hooks, HookOrderBase $attribute, ?array $others = if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { return; } + $shouldBeLarger = $hook->order instanceof SimpleOrderType ? $hook->order->shouldBeLast() : $hook->order->type->shouldBeLast(); // The priority of the hook being changed. $priority_this = $priorities[$index_this]; // The priority of the hook being compared to. - $priority_other = $attribute->shouldBeLarger ? max($priorities_other) : min($priorities_other); + $priority_other = $shouldBeLarger ? max($priorities_other) : min($priorities_other); // If the order is correct there is nothing to do. If the two priorities // are the same then the order is undefined and so it can't be correct. // If they are not the same and $priority_this is already larger exactly // when $attribute->shouldBeLarger says then it's the correct order. - if ($priority_this !== $priority_other && ($attribute->shouldBeLarger === ($priority_this > $priority_other))) { + if ($priority_this !== $priority_other && ($shouldBeLarger === ($priority_this > $priority_other))) { return; } - $priority_new = $priority_other + ($attribute->shouldBeLarger ? 1 : -1); + $priority_new = $priority_other + ($shouldBeLarger ? 1 : -1); // For first and last this new priority is already larger/smaller // than all existing priorities but for before / after it might belong to // an already existing hook. In this case set the new priority temporarily @@ -105,7 +108,7 @@ public function change(array $hooks, HookOrderBase $attribute, ?array $others = // relative to both $priority_other and the hook whose priority was // $priority_new. if (in_array($priority_new, $priorities)) { - $priorities[$index_this] = $priority_other + ($attribute->shouldBeLarger ? 0.5 : -0.5); + $priorities[$index_this] = $priority_other + ($shouldBeLarger ? 0.5 : -0.5); asort($priorities); $changed_indexes = array_keys($priorities); $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index fd6c2dc3cb16..c28145c95896 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -3,6 +3,8 @@ namespace Drupal\ckeditor5\Hook; use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\Order; +use Drupal\Core\Hook\Attribute\OrderType; use Drupal\Core\Hook\Attribute\HookOrderGroup; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Asset\AttachedAssetsInterface; @@ -110,9 +112,13 @@ public function theme() : array { * removed from the validation chain, as that validator is not needed with * CKEditor 5 and will trigger a false error. */ - #[Hook('form_filter_format_form_alter')] - #[HookOrderGroup(['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'])] - #[HookAfter(['editor', 'media'])] + #[Hook('form_filter_format_form_alter', + order: new Order( + type: OrderType::After, + modules: ['editor', 'media'], + group: ['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'], + ) + )] public function formFilterFormatFormAlter(array &$form, FormStateInterface $form_state, $form_id) : void { $editor = $form_state->get('editor'); // CKEditor 5 plugin config determines the available HTML tags. If an HTML -- GitLab From 8012b6526fcf61cff62ff9834da13d1d93888b44 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 20:53:40 -0500 Subject: [PATCH 067/268] Round 2 --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 19 ------------------- .../Drupal/Core/Hook/Attribute/OrderType.php | 13 +++---------- .../Core/Hook/Attribute/SimpleOrderType.php | 13 +++---------- .../Drupal/Core/Hook/HookCollectorPass.php | 2 +- core/lib/Drupal/Core/Hook/HookPriority.php | 7 +++---- 5 files changed, 10 insertions(+), 44 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 1d31055255fa..2804a72ce795 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -114,23 +114,4 @@ public function __construct( public Order|SimpleOrderType|NULL $order = NULL, ) {} - /** - * Set the method the hook should apply to. - * - * @param string $method - * The method that the hook attribute applies to. - * This only needs to be set when the attribute is on the class. - * - * @internal - */ - public function setMethod(string $method): static { - $this->method = $method; - return $this; - } - - public function setClass(string $class): static { - $this->class = $class; - return $this; - } - } diff --git a/core/lib/Drupal/Core/Hook/Attribute/OrderType.php b/core/lib/Drupal/Core/Hook/Attribute/OrderType.php index 1855b54c6716..fcb054c1d250 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/OrderType.php +++ b/core/lib/Drupal/Core/Hook/Attribute/OrderType.php @@ -4,16 +4,9 @@ namespace Drupal\Core\Hook\Attribute; -enum OrderType { +enum OrderType: int { - case Before; - case After; - - public function shouldBeLast(): bool { - return match($this) { - OrderType::Before => TRUE, - OrderType::After => FALSE, - }; - } + case Before = 1; + case After = 0; } diff --git a/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php b/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php index 7957b1198df2..95c2d7352cda 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php +++ b/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php @@ -4,16 +4,9 @@ namespace Drupal\Core\Hook\Attribute; -enum SimpleOrderType { +enum SimpleOrderType: int { - case First; - case Last; - - public function shouldBeLast(): bool { - return match($this) { - SimpleOrderType::First => TRUE, - SimpleOrderType::Last => FALSE, - }; - } + case First = 1; + case Last = 0; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 6396e3e6eab2..5a54b6ca2fca 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -77,7 +77,7 @@ public function process(ContainerBuilder $container): array { foreach ($methods as $method => $hooks) { foreach ($hooks as $hook) { assert($hook instanceof Hook); - $hook->setClass($class); + $hook->class = $class; if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hook); } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 775610f98ec1..4326fb7cebd6 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -5,7 +5,6 @@ namespace Drupal\Core\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookOrderBase; use Drupal\Core\Hook\Attribute\SimpleOrderType; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -26,8 +25,8 @@ public function __construct(protected ContainerBuilder $container) {} * in Drupal\Core\Hook\Attribute, and it might also contain * the hooks listed in the Drupal\Core\Hook\Attribute\HookOrderGroup * attribute. - * @param \Drupal\Core\Hook\Attribute\HookOrderBase $attribute - * The order attribute. + * @param \Drupal\Core\Hook\Attribute\Hook $hook + * The hook attribute. * @param array|null $others * Other hook implementations to compare to, if any. The array is a list of * class and method pairs. @@ -86,7 +85,7 @@ public function change(array $hooks, Hook $hook, ?array $others = NULL): void { if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { return; } - $shouldBeLarger = $hook->order instanceof SimpleOrderType ? $hook->order->shouldBeLast() : $hook->order->type->shouldBeLast(); + $shouldBeLarger = boolval($hook->order instanceof SimpleOrderType ? $hook->order->value : $hook->order->type->value); // The priority of the hook being changed. $priority_this = $priorities[$index_this]; // The priority of the hook being compared to. -- GitLab From 08378741cfc4776d625c0d39f4972a2ef4a0bc3a Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 21:12:48 -0500 Subject: [PATCH 068/268] Priority update --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 10 ++++++++++ core/lib/Drupal/Core/Hook/Attribute/Order.php | 8 ++++---- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 10 ++-------- core/lib/Drupal/Core/Hook/HookPriority.php | 2 +- .../src/Hook/TestHookAfter.php | 10 +++++++--- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 2804a72ce795..1384b992f5e4 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -114,4 +114,14 @@ public function __construct( public Order|SimpleOrderType|NULL $order = NULL, ) {} + public function set(string $class, string $module, string $method): void { + $this->class = $class; + if (!$this->module) { + $this->module = $module; + } + if (!$this->method) { + $this->method = $method; + } + } + } diff --git a/core/lib/Drupal/Core/Hook/Attribute/Order.php b/core/lib/Drupal/Core/Hook/Attribute/Order.php index 6a2bbf8cd09e..d8c97659df96 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Order.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Order.php @@ -7,10 +7,10 @@ class Order { public function __construct( - public ?OrderType $type, - public ?array $modules = [], - public ?array $classesAndMethods = [], - public ?array $group = NULL, + public OrderType $type, + public array $modules = [], + public array $classesAndMethods = [], + public array $group = [], ) { if (!$this->modules && !$this->classesAndMethods) { throw new \LogicException('Order against what?'); diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 5a54b6ca2fca..9d38f355423f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -77,21 +77,15 @@ public function process(ContainerBuilder $container): array { foreach ($methods as $method => $hooks) { foreach ($hooks as $hook) { assert($hook instanceof Hook); - $hook->class = $class; + $hook->set(class: $class, module: $module, method: $method); if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hook); } - if (!$hook->module) { - $hook->module = $module; - } - if (!$hook->method) { - $hook->method = $method; - } $legacyImplementations[$hook->hook][$hook->module] = ''; $implementations[$hook->hook][$hook->module][$class][] = $hook->method; if ($hook->order) { $orderAttributes[] = $hook; - if ($hook->order instanceof Order && $hook->order->group) { + if ($hook->order instanceof Order) { foreach ($hook->order->group as $extraHook) { $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $hook->order->group); } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 4326fb7cebd6..a0b8866c3636 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -93,7 +93,7 @@ public function change(array $hooks, Hook $hook, ?array $others = NULL): void { // If the order is correct there is nothing to do. If the two priorities // are the same then the order is undefined and so it can't be correct. // If they are not the same and $priority_this is already larger exactly - // when $attribute->shouldBeLarger says then it's the correct order. + // when $shouldBeLarger says then it's the correct order. if ($priority_this !== $priority_other && ($shouldBeLarger === ($priority_this > $priority_other))) { return; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php index 49748e2eb9fa..6efe80367e48 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php @@ -6,6 +6,8 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\Order; +use Drupal\Core\Hook\Attribute\OrderType; /** * Hook implementations for verifying ordering hooks by attributes. @@ -22,10 +24,12 @@ class TestHookAfter { /** - * This pair tests #[HookAfter]. + * This pair tests OrderType::After. */ - #[HookAfter(['hook_order_last_alphabetically'])] - #[Hook('custom_hook_test_hook_after')] + #[Hook('custom_hook_test_hook_after', order: new Order( + type: OrderType::After, + modules: ['hook_order_last_alphabetically'] + ))] public static function hookAfter(): void { $GLOBALS['HookAfter'] = 'HookAfter'; } -- GitLab From dcee1a51c0a160c6ed512313a2fa5ac004a19c3e Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 21:13:46 -0500 Subject: [PATCH 069/268] Remove attributes that are not needed --- .../Drupal/Core/Hook/Attribute/HookAfter.php | 29 ----------- .../Drupal/Core/Hook/Attribute/HookBefore.php | 29 ----------- .../Drupal/Core/Hook/Attribute/HookFirst.php | 24 --------- .../Drupal/Core/Hook/Attribute/HookLast.php | 24 --------- .../Core/Hook/Attribute/HookOrderBase.php | 51 ------------------- .../Core/Hook/Attribute/HookOrderGroup.php | 44 ---------------- .../Hook/Attribute/HookOrderInterface.php | 22 -------- 7 files changed, 223 deletions(-) delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookAfter.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookBefore.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookFirst.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookLast.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php b/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php deleted file mode 100644 index ce92b5512836..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/HookAfter.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -/** - * Attribute to request this hook implementation to fire after others. - * - * @section sec_backwards_compatibility Backwards-compatibility - * - * @see HookOrderGroup - */ -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookAfter extends HookOrderBase { - - /** - * Constructs a HookAfter attribute. - * - * @param array $orderings - * Each ordering is either a module name or a class and method pair array. - */ - public function __construct( - public readonly array $orderings, - ) { - parent::__construct(FALSE); - } - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php b/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php deleted file mode 100644 index 1c26d7db4171..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/HookBefore.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -/** - * Attribute to request this hook implementation to fire before others. - * - * @section sec_backwards_compatibility Backwards-compatibility - * - * @see HookOrderGroup - */ -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookBefore extends HookOrderBase { - - /** - * Constructs a HookBefore lib/Drupal/Core/Hook/Attribute/attribute. - * - * @param array $orderings - * Each ordering is either a module name or a class and method pair array. - */ - public function __construct( - public readonly array $orderings, - ) { - parent::__construct(TRUE); - } - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php b/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php deleted file mode 100644 index b7d82aab5d13..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/HookFirst.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -/** - * Attribute to request this hook implementation to fire first. - * - * @section sec_backwards_compatibility Backwards-compatibility - * - * @see HookOrderGroup - */ -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookFirst extends HookOrderBase { - - /** - * Constructs a HookFirst attribute. - */ - public function __construct() { - parent::__construct(TRUE); - } - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php b/core/lib/Drupal/Core/Hook/Attribute/HookLast.php deleted file mode 100644 index d5c92d00eb77..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/HookLast.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -/** - * Attribute to request this hook implementation to fire after others. - * - * @section sec_backwards_compatibility Backwards-compatibility - * - * @see HookOrderGroup - */ -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookLast extends HookOrderBase { - - /** - * Constructs a HookLast attribute. - */ - public function __construct() { - parent::__construct(FALSE); - } - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php deleted file mode 100644 index f7297b3486e3..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderBase.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -/** - * Common set of functionality needed by attributes that handle ordering hooks. - */ -class HookOrderBase implements HookOrderInterface { - - /** - * The hook that should be ordered. - * - * @internal - */ - public string $hook; - - /** - * The class the hook is found in. - * - * @internal - */ - public string $class; - - /** - * The method of the hook. - * - * @internal - */ - public string $method; - - /** - * Constructs a HookOrderBase class. - * - * @param bool $shouldBeLarger - * Determines whether the hook should increase or decrease priority. - */ - public function __construct(public readonly bool $shouldBeLarger) {} - - /** - * {@inheritdoc} - */ - public function set(Hook $hook, string $class): static { - $this->hook = $hook->hook; - $this->class = $class; - $this->method = $hook->method; - return $this; - } - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php deleted file mode 100644 index c4f45c6728b1..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderGroup.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -/** - * List of alter hooks called together. - * - * Ordering by attributes happens at build time by setting up the order of - * the listeners of a hook correctly. However, ModuleHandlerInterface::alter() - * can be called with multiple hooks runtime. If the hook defined on this - * method/class requires ordering relative to other such hooks then this - * attribute can be used to order relative to implementations of all hooks in - * the group. Include all alter hooks to be ordered against in the group even - * if no single alter() call includes all of them. For example, this can be - * used to order a hook_form_BASE_FORM_ID_alter() implementation relative to - * multiple hook_form_FORM_ID_alter() implementations as - * Drupal\ckeditor5\Hook\Ckeditor5Hooks::formFilterFormatFormAlter() does. - * - * @section sec_backwards_compatibility Backwards-compatibility - * - * To allow hook implementations to work on older versions of Drupal as well, - * keep the hook_module_implements_alter() implementation and execute the - * same ordering as prescribed by the hook order attributes. Then add - * #[LegacyHook] to the hook_module_implements_alter() implementation so it - * only gets executed in older Drupal versions. - * - * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. - */ -#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class HookOrderGroup { - - /** - * Constructs a HookOrderGroup attribute object. - * - * @param array $group - * A list of hooks to sort together. - */ - public function __construct( - public readonly array $group, - ) {} - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php deleted file mode 100644 index edd5be7186e9..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/HookOrderInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -/** - * Interface for classes that manage hook ordering. - */ -interface HookOrderInterface { - - /** - * Set the properties on the attributes using this class. - * - * @param \Drupal\Core\Hook\Attribute\Hook $hook - * The hook attribute to order. - * @param string $class - * The class the hook is in. - */ - public function set(Hook $hook, string $class): static; - -} -- GitLab From fea28ee13892bdaf621affe5e365c177edc0d838 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 21:37:21 -0500 Subject: [PATCH 070/268] Update tests --- core/lib/Drupal/Core/Hook/Attribute/Order.php | 2 +- .../src/Hook/TestHookAfter.php | 1 - .../src/Hook/TestHookBefore.php | 2 +- .../src/Hook/TestHookFirst.php | 2 +- .../src/Hook/TestHookLast.php | 9 ++++----- .../src/Hook/TestHookOrderGroup.php | 16 ++++++++++------ .../src/Hook/TestHookAfter.php | 2 +- .../src/Hook/TestHookBefore.php | 11 +++++++---- .../src/Hook/TestHookFirst.php | 7 +++---- .../src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderGroup.php | 2 +- .../src/Hook/TestHookAfterClassMethod.php | 13 +++++++++---- 12 files changed, 39 insertions(+), 30 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Order.php b/core/lib/Drupal/Core/Hook/Attribute/Order.php index d8c97659df96..ebd1cd025c69 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Order.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Order.php @@ -13,7 +13,7 @@ public function __construct( public array $group = [], ) { if (!$this->modules && !$this->classesAndMethods) { - throw new \LogicException('Order against what?'); + throw new \LogicException('Order must provide elements to order against.'); } } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php index 6efe80367e48..15fbb62434da 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php @@ -5,7 +5,6 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookAfter; use Drupal\Core\Hook\Attribute\Order; use Drupal\Core\Hook\Attribute\OrderType; diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php index 6ac7468053d6..c83a3a89f0ce 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php @@ -21,7 +21,7 @@ class TestHookBefore { /** - * This pair tests #[HookBefore]. + * This pair tests OrderType::Before. */ #[Hook('custom_hook_test_hook_before')] public static function hookBefore(): void { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php index 012e3f0d2c54..a1bc165a33ff 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php @@ -21,7 +21,7 @@ class TestHookFirst { /** - * This pair tests #[HookFirst]. + * This pair tests OrderType::First. */ #[Hook('custom_hook_test_hook_first')] public static function hookFirst(): void { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php index 0a82ba6e55b3..7655b6bed92f 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookLast; +use Drupal\Core\Hook\Attribute\SimpleOrderType; /** * Hook implementations for verifying ordering hooks by attributes. @@ -21,11 +21,10 @@ */ class TestHookLast { - /** - * This pair tests #[HookLast]. + /** + * This pair tests OrderType::Last. */ - #[HookLast] - #[Hook('custom_hook_test_hook_last')] + #[Hook('custom_hook_test_hook_last', order: SimpleOrderType::Last)] public static function hookLast(): void { $GLOBALS['HookLast'] = 'HookLast'; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php index be8b4274cc26..bc21ff1a3ea6 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php @@ -5,8 +5,8 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookAfter; -use Drupal\Core\Hook\Attribute\HookOrderGroup; +use Drupal\Core\Hook\Attribute\Order; +use Drupal\Core\Hook\Attribute\OrderType; /** * Hook implementations for verifying ordering hooks by attributes. @@ -23,11 +23,15 @@ class TestHookOrderGroup { /** - * This pair tests #[HookOrderGroup]. + * This pair tests OrderType::After with Group. */ - #[HookAfter(['hook_order_last_alphabetically'])] - #[HookOrderGroup(['custom_hook_extra_types2_alter'])] - #[Hook('custom_hook_extra_types1_alter')] + #[Hook('custom_hook_extra_types1_alter', + order: new Order( + type: OrderType::After, + modules: ['hook_order_last_alphabetically'], + group: ['custom_hook_extra_types2_alter'], + ) + )] public static function customHookExtraTypes(): void { // This should be run after so HookOrderGroupExtraTypes should not be set. if (!isset($GLOBALS['HookOrderGroupExtraTypes'])) { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php index c01e15b71f61..e15ca55aea6c 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php @@ -21,7 +21,7 @@ class TestHookAfter { /** - * This pair tests #[HookAfter]. + * This pair tests OrderType::After. */ #[Hook('custom_hook_test_hook_after')] public static function hookAfter(): void { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php index 946377092261..99934423ea48 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php @@ -5,7 +5,8 @@ namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookBefore; +use Drupal\Core\Hook\Attribute\Order; +use Drupal\Core\Hook\Attribute\OrderType; /** * Hook implementations for verifying ordering hooks by attributes. @@ -22,10 +23,12 @@ class TestHookBefore { /** - * This pair tests #[HookBefore]. + * This pair tests OrderType::Before. */ - #[HookBefore(['hook_order_first_alphabetically'])] - #[Hook('custom_hook_test_hook_before')] + #[Hook('custom_hook_test_hook_before', order: new Order( + type: OrderType::Before, + modules: ['hook_order_first_alphabetically'] + ))] public static function cacheFlush(): void { $GLOBALS['HookBefore'] = 'HookBefore'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php index 3980b23bfb85..06972d5c97d0 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookFirst; +use Drupal\Core\Hook\Attribute\SimpleOrderType; /** * Hook implementations for verifying ordering hooks by attributes. @@ -22,10 +22,9 @@ class TestHookFirst { /** - * This pair tests #[HookFirst]. + * This pair tests OrderType::First. */ - #[HookFirst] - #[Hook('custom_hook_test_hook_first')] + #[Hook('custom_hook_test_hook_first', order: SimpleOrderType::First)] public static function hookFirst(): void { $GLOBALS['HookFirst'] = 'HookFirst'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php index 864016a538b1..a7738173c2d2 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php @@ -21,7 +21,7 @@ class TestHookLast { /** - * This pair tests #[HookLast]. + * This pair tests OrderType::Last. */ #[Hook('custom_hook_test_hook_last')] public static function hookLast(): void { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php index 607a9d376290..cbf72ddd77b5 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php @@ -21,7 +21,7 @@ class TestHookOrderGroup { /** - * This pair tests #[HookOrderGroup]. + * This pair tests OrderType::After with Group. */ #[Hook('custom_hook_extra_types2_alter')] public static function customHookExtraTypes(): void { diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php index 1752a787e9a6..f4fc3fe81ee0 100644 --- a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -5,7 +5,8 @@ namespace Drupal\hook_second_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\HookAfter; +use Drupal\Core\Hook\Attribute\Order; +use Drupal\Core\Hook\Attribute\OrderType; use Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; /** @@ -23,10 +24,14 @@ class TestHookAfterClassMethod { /** - * This pair tests #[HookAfter] with a passed class and method. + * This pair tests OrderType::After with a passed class and method. */ - #[HookAfter([[TestHookAfterClassMethodForAfter::class, 'hookAfterClassMethod']])] - #[Hook('custom_hook_test_hook_after_class_method')] + #[Hook('custom_hook_test_hook_after_class_method', + order: new Order( + type: OrderType::After, + classesAndMethods: [TestHookAfterClassMethodForAfter::class, 'hookAfterClassMethod'], + ) + )] public static function hookAfterClassMethod(): void { $GLOBALS['HookAfterClassMethod'] = 'HookAfterMethod'; } -- GitLab From bd6fed4241a7e8b37f76245422b30db0bee20946 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 21:40:11 -0500 Subject: [PATCH 071/268] Code sniffing --- core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php | 2 -- .../hook_order_first_alphabetically/src/Hook/TestHookLast.php | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index c28145c95896..c0fec656e43c 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -2,10 +2,8 @@ namespace Drupal\ckeditor5\Hook; -use Drupal\Core\Hook\Attribute\HookAfter; use Drupal\Core\Hook\Attribute\Order; use Drupal\Core\Hook\Attribute\OrderType; -use Drupal\Core\Hook\Attribute\HookOrderGroup; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Render\Element; diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php index 7655b6bed92f..2f364d8d1dc5 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php @@ -21,7 +21,7 @@ */ class TestHookLast { - /** + /** * This pair tests OrderType::Last. */ #[Hook('custom_hook_test_hook_last', order: SimpleOrderType::Last)] -- GitLab From 0e1ecbe5253b1fa211dddefbd51578ff1b052245 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sat, 14 Dec 2024 21:47:30 -0500 Subject: [PATCH 072/268] Add comments --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 1384b992f5e4..3f54b5f84da9 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -106,6 +106,9 @@ class Hook { * (optional) The module this implementation is for. This allows one module * to implement a hook on behalf of another module. Defaults to the module * the implementation is in. + * @param Order|SimpleOrderType|null $order + * (optional) Ordering information if you need to change the current order + * of the implementation. */ public function __construct( public string $hook, @@ -114,6 +117,16 @@ public function __construct( public Order|SimpleOrderType|NULL $order = NULL, ) {} + /** + * Set necessary parameters for the hook attribute. + * + * @param string $class + * The class for the hook. + * @param string $module + * The module for the hook. + * @param string $method + * The method for the hook. + */ public function set(string $class, string $module, string $method): void { $this->class = $class; if (!$this->module) { -- GitLab From 93b499a05d12aeb3dd28b316e27b6939f0191e39 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sun, 15 Dec 2024 08:27:08 -0500 Subject: [PATCH 073/268] Refactor ordering --- .../Drupal/Core/Extension/ModuleHandler.php | 8 +-- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 16 +++-- core/lib/Drupal/Core/Hook/Attribute/Order.php | 20 ------ .../Drupal/Core/Hook/Attribute/OrderType.php | 12 ---- .../Core/Hook/Attribute/SimpleOrderType.php | 12 ---- core/lib/Drupal/Core/Hook/ComplexOrder.php | 64 +++++++++++++++++++ .../Drupal/Core/Hook/HookCollectorPass.php | 10 +-- core/lib/Drupal/Core/Hook/HookPriority.php | 25 +++++--- core/lib/Drupal/Core/Hook/Order.php | 22 +++++++ core/lib/Drupal/Core/Hook/OrderAfter.php | 17 +++++ core/lib/Drupal/Core/Hook/OrderBefore.php | 17 +++++ .../ckeditor5/src/Hook/Ckeditor5Hooks.php | 6 +- .../src/Hook/TestHookAfter.php | 10 +-- .../src/Hook/TestHookBefore.php | 2 +- .../src/Hook/TestHookFirst.php | 2 +- .../src/Hook/TestHookLast.php | 6 +- .../src/Hook/TestHookOrderGroup.php | 8 +-- .../src/Hook/TestHookAfter.php | 2 +- .../src/Hook/TestHookBefore.php | 10 +-- .../src/Hook/TestHookFirst.php | 6 +- .../src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderGroup.php | 2 +- .../src/Hook/TestHookAfterClassMethod.php | 10 ++- 23 files changed, 184 insertions(+), 105 deletions(-) delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/Order.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/OrderType.php delete mode 100644 core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php create mode 100644 core/lib/Drupal/Core/Hook/ComplexOrder.php create mode 100644 core/lib/Drupal/Core/Hook/Order.php create mode 100644 core/lib/Drupal/Core/Hook/OrderAfter.php create mode 100644 core/lib/Drupal/Core/Hook/OrderBefore.php diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index ea72ec0acbec..93ccc2bc694d 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -446,10 +446,10 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { foreach ($extra_hooks as $extra_hook) { $hook_listeners = $this->findListenersForAlter($extra_hook, $hook_listeners, $extra_modules); } - // Second, gather implementations defined in a - // Drupal\Core\Hook\Attribute\HookOrderGroup attribute. These are only - // used for ordering because the group might contain hooks not included - // in this alter() call. + // Second, gather implementations grouped together. These are only used + // for ordering because the group might contain hooks not included in + // this alter() call. \Drupal\Core\Hook\HookPriority::change() + // registers the implementations of a grouped hook. foreach (array_merge($extra_hooks, [$type . '_alter']) as $extra_hook) { if (isset($this->orderGroups[$extra_hook])) { $group = $this->orderGroups[$extra_hook]; diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 3f54b5f84da9..2a4cfc0e9095 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -4,12 +4,16 @@ namespace Drupal\Core\Hook\Attribute; +use Drupal\Core\Hook\ComplexOrder; +use Drupal\Core\Hook\Order; + /** * Attribute for defining a class method as a hook implementation. * * Hook implementations in classes need to be marked with this attribute, * using one of the following techniques: * - On a method, use this attribute with the hook name: + * * @code * #[Hook('user_cancel')] * public function userCancel(...) {} @@ -90,6 +94,11 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Hook { + /** + * The class the hook implementation is in. + * + * @var string + */ public string $class = ''; /** @@ -106,15 +115,14 @@ class Hook { * (optional) The module this implementation is for. This allows one module * to implement a hook on behalf of another module. Defaults to the module * the implementation is in. - * @param Order|SimpleOrderType|null $order - * (optional) Ordering information if you need to change the current order - * of the implementation. + * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order + * (optional) Set the order of the implementation. */ public function __construct( public string $hook, public string $method = '', public ?string $module = NULL, - public Order|SimpleOrderType|NULL $order = NULL, + public Order|ComplexOrder|NULL $order = NULL, ) {} /** diff --git a/core/lib/Drupal/Core/Hook/Attribute/Order.php b/core/lib/Drupal/Core/Hook/Attribute/Order.php deleted file mode 100644 index ebd1cd025c69..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/Order.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -class Order { - - public function __construct( - public OrderType $type, - public array $modules = [], - public array $classesAndMethods = [], - public array $group = [], - ) { - if (!$this->modules && !$this->classesAndMethods) { - throw new \LogicException('Order must provide elements to order against.'); - } - } - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/OrderType.php b/core/lib/Drupal/Core/Hook/Attribute/OrderType.php deleted file mode 100644 index fcb054c1d250..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/OrderType.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -enum OrderType: int { - - case Before = 1; - case After = 0; - -} diff --git a/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php b/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php deleted file mode 100644 index 95c2d7352cda..000000000000 --- a/core/lib/Drupal/Core/Hook/Attribute/SimpleOrderType.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook\Attribute; - -enum SimpleOrderType: int { - - case First = 1; - case Last = 0; - -} diff --git a/core/lib/Drupal/Core/Hook/ComplexOrder.php b/core/lib/Drupal/Core/Hook/ComplexOrder.php new file mode 100644 index 000000000000..30d05a3bde06 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/ComplexOrder.php @@ -0,0 +1,64 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +/** + * Set this implementation to be before or after others. + */ +abstract readonly class ComplexOrder { + + /** + * Whether the priority of this hook should be larger than others. + */ + const bool VALUE = FALSE; + + /** + * Whether the priority of this hook should be larger than others. + * + * The value of this variable is the same as the constant ::VALUE, it only + * exists so ComplexOrder and Order types both have the same value property. + * + * @var bool + */ + public bool $value; + + /** + * Constructs a ComplexOrder object. + * + * @param array $modules + * A list of modules. + * @param array $classesAndMethods + * A list of classes and methods, for example: + * @code + * [ + * [Foo::class, 'someMethod'], + * [Bar::class, 'someOtherMethod'], + * ] + * @endcode + * @param array $group + * A list of hooks to be ordered together. Ordering by attributes happens + * at build time by setting up the order of the listeners of a hook + * correctly. However, ModuleHandlerInterface::alter() can be called with + * multiple hooks runtime. If the hook defined on this method/class + * requires ordering relative to other such hooks then this parameter can + * be used to order relative to implementations of all hooks in the group. + * Include all alter hooks to be ordered against in the group even if no + * single alter() call includes all of them. For example, this can be used + * to order a hook_form_BASE_FORM_ID_alter() implementation relative to + * multiple hook_form_FORM_ID_alter() implementations as + * Drupal\ckeditor5\Hook\Ckeditor5Hooks::formFilterFormatFormAlter() does. + */ + public function __construct( + public array $modules = [], + public array $classesAndMethods = [], + public array $group = [], + ) { + if (!$this->modules && !$this->classesAndMethods) { + throw new \LogicException('Order must provide either modules or class-method pairs to order against.'); + } + $this->value = static::VALUE; + } + +} diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 9d38f355423f..6cfd9129fbce 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -10,7 +10,6 @@ use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\LegacyHook; -use Drupal\Core\Hook\Attribute\Order; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -85,9 +84,10 @@ public function process(ContainerBuilder $container): array { $implementations[$hook->hook][$hook->module][$class][] = $hook->method; if ($hook->order) { $orderAttributes[] = $hook; - if ($hook->order instanceof Order) { - foreach ($hook->order->group as $extraHook) { - $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $hook->order->group); + if ($hook->order instanceof ComplexOrder && ($group = $hook->order->group)) { + $group[] = $hook->hook; + foreach ($group as $extraHook) { + $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $group); } } } @@ -194,7 +194,7 @@ protected static function reOrderServices(ContainerBuilder $container, array $or // ::process() adds the hook serving as key to the order group so it // does not need to be added if there's a group for the hook. $hooks = $orderGroups[$orderAttribute->hook] ?? [$orderAttribute->hook]; - if ($orderAttribute->order instanceof Order) { + if ($orderAttribute->order instanceof ComplexOrder) { $others = []; foreach ($orderAttribute->order->modules as $modules) { foreach ($hooks as $hook) { diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index a0b8866c3636..ccdd3ef5657a 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -5,7 +5,6 @@ namespace Drupal\Core\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\SimpleOrderType; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -34,11 +33,14 @@ public function __construct(protected ContainerBuilder $container) {} * @internal */ public function change(array $hooks, Hook $hook, ?array $others = NULL): void { - $class_and_method = "$hook->class::$hook->method"; if ($others) { - $other_specifiers = array_map(fn ($pair) => $pair[0] . '::' . $pair[1], $others); + $other_specifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $others); } if (count($hooks) > 1) { + // Mark $hook implementation and everything in $others as implementing + // a single combined hook made from $hooks. This is necessary because + // ordering is only possible between the implementations of the same + // hook. $map = $this->container->getParameter('hook_implementations_map'); $combinedHookTag = implode(':', $hooks); $event = "drupal_hook.$combinedHookTag"; @@ -46,9 +48,15 @@ public function change(array $hooks, Hook $hook, ?array $others = NULL): void { $data[] = [$hook->class, $hook->method]; $priority = 0; foreach ($data as [$class, $method]) { - foreach ($hooks as $hook) { - if (isset($map[$hook][$class][$method])) { - $map[$combinedHookTag][$class][$method] = $map[$hook][$class][$method]; + // If the class and method exists at all it surely implements a hook + // because it is being ordered against and then this implementation + // is already registered in the implementation map and allows finding + // out what the corresponding module is. This can't be found out from + // parsing the class name because a hook might be implemented on + // behalf of another module. + foreach ($hooks as $indexHook) { + if (isset($map[$indexHook][$class][$method])) { + $map[$combinedHookTag][$class][$method] = $map[$indexHook][$class][$method]; $priority = HookCollectorPass::addTagToDefinition($this->container->findDefinition($class), $event, $method, $priority); break; } @@ -70,7 +78,7 @@ public function change(array $hooks, Hook $hook, ?array $others = NULL): void { assert(is_int($priority)); $priorities[$index] = $priority; $specifier = "$id::" . $tag['method']; - if ($class_and_method === $specifier) { + if ($specifier === "$hook->class::$hook->method") { $index_this = $index; } // $others is defined for before and after, for these compare only @@ -85,7 +93,8 @@ public function change(array $hooks, Hook $hook, ?array $others = NULL): void { if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { return; } - $shouldBeLarger = boolval($hook->order instanceof SimpleOrderType ? $hook->order->value : $hook->order->type->value); + + $shouldBeLarger = (bool) $hook->order->value; // The priority of the hook being changed. $priority_this = $priorities[$index_this]; // The priority of the hook being compared to. diff --git a/core/lib/Drupal/Core/Hook/Order.php b/core/lib/Drupal/Core/Hook/Order.php new file mode 100644 index 000000000000..e8c8898d2a03 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Order.php @@ -0,0 +1,22 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +/** + * Set this implementation to be first or last. + */ +enum Order: int { + + /** + * This implementation should fire first. + */ + case First = 1; + + /** + * This implementation should fire last. + */ + case Last = 0; + +} diff --git a/core/lib/Drupal/Core/Hook/OrderAfter.php b/core/lib/Drupal/Core/Hook/OrderAfter.php new file mode 100644 index 000000000000..be7811c1b79c --- /dev/null +++ b/core/lib/Drupal/Core/Hook/OrderAfter.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +/** + * Set this implementation to be after others. + */ +readonly class OrderAfter extends ComplexOrder { + + /** + * After means the priority should not be larger than others. + */ + const bool VALUE = FALSE; + +} diff --git a/core/lib/Drupal/Core/Hook/OrderBefore.php b/core/lib/Drupal/Core/Hook/OrderBefore.php new file mode 100644 index 000000000000..4b1a1df6208b --- /dev/null +++ b/core/lib/Drupal/Core/Hook/OrderBefore.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +/** + * Set this implementation to be before others. + */ +readonly class OrderBefore extends ComplexOrder { + + /** + * Before means the priority should be larger than others. + */ + const bool VALUE = TRUE; + +} diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index c0fec656e43c..1c6128a8c3cd 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -2,8 +2,7 @@ namespace Drupal\ckeditor5\Hook; -use Drupal\Core\Hook\Attribute\Order; -use Drupal\Core\Hook\Attribute\OrderType; +use Drupal\Core\Hook\OrderAfter; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Render\Element; @@ -111,8 +110,7 @@ public function theme() : array { * CKEditor 5 and will trigger a false error. */ #[Hook('form_filter_format_form_alter', - order: new Order( - type: OrderType::After, + order: new OrderAfter( modules: ['editor', 'media'], group: ['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'], ) diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php index 15fbb62434da..58139a01a2e4 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php @@ -5,8 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\Order; -use Drupal\Core\Hook\Attribute\OrderType; +use Drupal\Core\Hook\OrderAfter; /** * Hook implementations for verifying ordering hooks by attributes. @@ -23,12 +22,9 @@ class TestHookAfter { /** - * This pair tests OrderType::After. + * This pair tests OrderAfter. */ - #[Hook('custom_hook_test_hook_after', order: new Order( - type: OrderType::After, - modules: ['hook_order_last_alphabetically'] - ))] + #[Hook('custom_hook_test_hook_after', order: new OrderAfter(['hook_order_last_alphabetically']))] public static function hookAfter(): void { $GLOBALS['HookAfter'] = 'HookAfter'; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php index c83a3a89f0ce..be9fad86510a 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php @@ -21,7 +21,7 @@ class TestHookBefore { /** - * This pair tests OrderType::Before. + * This pair tests OrderBefore. */ #[Hook('custom_hook_test_hook_before')] public static function hookBefore(): void { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php index a1bc165a33ff..003d3ac2a685 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php @@ -21,7 +21,7 @@ class TestHookFirst { /** - * This pair tests OrderType::First. + * This pair tests OrderFirst. */ #[Hook('custom_hook_test_hook_first')] public static function hookFirst(): void { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php index 2f364d8d1dc5..c7a218995111 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\SimpleOrderType; +use Drupal\Core\Hook\Order; /** * Hook implementations for verifying ordering hooks by attributes. @@ -22,9 +22,9 @@ class TestHookLast { /** - * This pair tests OrderType::Last. + * This pair tests OrderLast. */ - #[Hook('custom_hook_test_hook_last', order: SimpleOrderType::Last)] + #[Hook('custom_hook_test_hook_last', order: Order::Last)] public static function hookLast(): void { $GLOBALS['HookLast'] = 'HookLast'; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php index bc21ff1a3ea6..2814a5cfa2ed 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php @@ -5,8 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\Order; -use Drupal\Core\Hook\Attribute\OrderType; +use Drupal\Core\Hook\OrderAfter; /** * Hook implementations for verifying ordering hooks by attributes. @@ -23,11 +22,10 @@ class TestHookOrderGroup { /** - * This pair tests OrderType::After with Group. + * This pair tests OrderAfter with Group. */ #[Hook('custom_hook_extra_types1_alter', - order: new Order( - type: OrderType::After, + order: new OrderAfter( modules: ['hook_order_last_alphabetically'], group: ['custom_hook_extra_types2_alter'], ) diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php index e15ca55aea6c..87973afcf32c 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php @@ -21,7 +21,7 @@ class TestHookAfter { /** - * This pair tests OrderType::After. + * This pair tests OrderAfter. */ #[Hook('custom_hook_test_hook_after')] public static function hookAfter(): void { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php index 99934423ea48..3ecaea3dc84e 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php @@ -5,8 +5,7 @@ namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\Order; -use Drupal\Core\Hook\Attribute\OrderType; +use Drupal\Core\Hook\OrderBefore; /** * Hook implementations for verifying ordering hooks by attributes. @@ -23,12 +22,9 @@ class TestHookBefore { /** - * This pair tests OrderType::Before. + * This pair tests OrderBefore. */ - #[Hook('custom_hook_test_hook_before', order: new Order( - type: OrderType::Before, - modules: ['hook_order_first_alphabetically'] - ))] + #[Hook('custom_hook_test_hook_before', order: new OrderBefore(['hook_order_first_alphabetically']))] public static function cacheFlush(): void { $GLOBALS['HookBefore'] = 'HookBefore'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php index 06972d5c97d0..0ab7697bef63 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\SimpleOrderType; +use Drupal\Core\Hook\Order; /** * Hook implementations for verifying ordering hooks by attributes. @@ -22,9 +22,9 @@ class TestHookFirst { /** - * This pair tests OrderType::First. + * This pair tests OrderFirst. */ - #[Hook('custom_hook_test_hook_first', order: SimpleOrderType::First)] + #[Hook('custom_hook_test_hook_first', order: Order::First)] public static function hookFirst(): void { $GLOBALS['HookFirst'] = 'HookFirst'; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php index a7738173c2d2..2999ee66a1ca 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php @@ -21,7 +21,7 @@ class TestHookLast { /** - * This pair tests OrderType::Last. + * This pair tests OrderLast. */ #[Hook('custom_hook_test_hook_last')] public static function hookLast(): void { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php index cbf72ddd77b5..3840fd26061b 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php @@ -21,7 +21,7 @@ class TestHookOrderGroup { /** - * This pair tests OrderType::After with Group. + * This pair tests OrderAfter with Group. */ #[Hook('custom_hook_extra_types2_alter')] public static function customHookExtraTypes(): void { diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php index f4fc3fe81ee0..5a50f166efac 100644 --- a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -5,8 +5,7 @@ namespace Drupal\hook_second_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\Order; -use Drupal\Core\Hook\Attribute\OrderType; +use Drupal\Core\Hook\OrderAfter; use Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; /** @@ -24,12 +23,11 @@ class TestHookAfterClassMethod { /** - * This pair tests OrderType::After with a passed class and method. + * This pair tests OrderAfter with a passed class and method. */ #[Hook('custom_hook_test_hook_after_class_method', - order: new Order( - type: OrderType::After, - classesAndMethods: [TestHookAfterClassMethodForAfter::class, 'hookAfterClassMethod'], + order: new OrderAfter( + classesAndMethods: [[TestHookAfterClassMethodForAfter::class, 'hookAfterClassMethod']], ) )] public static function hookAfterClassMethod(): void { -- GitLab From 35c6007b29a61beb0c4e38ae217b5809cf8fbd88 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Sun, 15 Dec 2024 09:44:59 -0500 Subject: [PATCH 074/268] Comments and rename method and variable for clarity --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 4 ++-- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 14 +++++++------- core/lib/Drupal/Core/Hook/HookPriority.php | 3 +-- .../src/Hook/TestHookAfter.php | 2 +- .../src/Hook/TestHookBefore.php | 2 +- .../src/Hook/TestHookFirst.php | 2 +- .../src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderGroup.php | 2 +- .../src/Hook/TestHookAfter.php | 2 +- .../src/Hook/TestHookBefore.php | 2 +- .../src/Hook/TestHookFirst.php | 2 +- .../src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderGroup.php | 2 +- .../src/Hook/TestHookAfterClassMethod.php | 2 +- .../src/Hook/TestHookAfterClassMethod.php | 2 +- 15 files changed, 22 insertions(+), 23 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 93ccc2bc694d..129fde02abc5 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -455,13 +455,13 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $group = $this->orderGroups[$extra_hook]; $extra_listeners = $this->findListenersForAlter(implode(':', $group)); // Remove already ordered hooks. - $extra_types = array_diff($extra_hooks, $group); + $extra_hooks = array_diff($extra_hooks, $group); } } } // If multiple alters were called, but they were already ordered by // ordering attributes then keep that order. - if (isset($extra_types) && empty($extra_types)) { + if (isset($extra_hooks) && empty($extra_hooks)) { $modules = array_keys(array_intersect_key($extra_listeners, $hook_listeners)); } else { diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 6cfd9129fbce..e6dbc90b59f5 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -59,7 +59,7 @@ class HookCollectorPass implements CompilerPassInterface { /** * A list of attributes for hook implementations. * - * Keys are module, class and method. values are Hook attributes. + * Keys are module, class and method. Values are Hook attributes. */ protected array $moduleHooks = []; @@ -101,8 +101,8 @@ public function process(ContainerBuilder $container): array { // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { - static::registerServices($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); - static::reOrderServices($container, $orderAttributes, $orderGroups, $implementations); + static::registerImplementations($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); + static::reOrderImplementations($container, $orderAttributes, $orderGroups, $implementations); } return $implementations; } @@ -126,7 +126,7 @@ public function process(ContainerBuilder $container): array { * * @return void */ - protected static function registerServices(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementations, array $orderGroups): void { + protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementations, array $orderGroups): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); $groupIncludes = []; @@ -174,7 +174,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl } /** - * Reorder services that have attributes specifying an order. + * Reorder hook implementations specifying an order. * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. @@ -187,7 +187,7 @@ protected static function registerServices(ContainerBuilder $container, HookColl * * @return void */ - protected static function reOrderServices(ContainerBuilder $container, array $orderAttributes, array $orderGroups, array $implementations): void { + protected static function reOrderImplementations(ContainerBuilder $container, array $orderAttributes, array $orderGroups, array $implementations): void { $hookPriority = new HookPriority($container); foreach ($orderAttributes as $orderAttribute) { assert($orderAttribute instanceof Hook); @@ -442,7 +442,7 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class = ' * A list of class and method reflections. * * @return array - * A list of Hook|HookOrderInterface|HookOrderGroup attribute instances. + * A list of Hook attribute instances. */ protected static function getAttributeInstances(array $attributes, array $reflections): array { foreach ($reflections as $reflection) { diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index ccdd3ef5657a..603dbaa6f379 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -22,8 +22,7 @@ public function __construct(protected ContainerBuilder $container) {} * @param array $hooks * The list of hooks to order. The list always contains the hook defined * in Drupal\Core\Hook\Attribute, and it might also contain - * the hooks listed in the Drupal\Core\Hook\Attribute\HookOrderGroup - * attribute. + * the hooks listed in the Drupal\Core\Hook\ComplexOrder $group * @param \Drupal\Core\Hook\Attribute\Hook $hook * The hook attribute. * @param array|null $others diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php index 58139a01a2e4..9dd41b0cbf06 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php @@ -17,7 +17,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookAfter { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php index be9fad86510a..3b7ce8df15ac 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php @@ -16,7 +16,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookBefore { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php index 003d3ac2a685..31841f75549a 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php @@ -16,7 +16,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookFirst { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php index c7a218995111..b93fd5155a62 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php @@ -17,7 +17,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookLast { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php index 2814a5cfa2ed..87b441159a3d 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php @@ -17,7 +17,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookOrderGroup { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php index 87973afcf32c..c132f940c319 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php @@ -16,7 +16,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookAfter { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php index 3ecaea3dc84e..03b19b10ffe9 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php @@ -17,7 +17,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookBefore { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php index 0ab7697bef63..ee659a32585f 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php @@ -17,7 +17,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookFirst { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php index 2999ee66a1ca..36b32f08678b 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php @@ -16,7 +16,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookLast { diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php index 3840fd26061b..faac9868ce90 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php @@ -16,7 +16,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookOrderGroup { diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php index 5a50f166efac..b37339173578 100644 --- a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -18,7 +18,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookAfterClassMethod { diff --git a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php index dc3e6db8d2c3..d69aaa546fb0 100644 --- a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -16,7 +16,7 @@ * In the normal order a hook implemented by first alphabetically would run * before the same hook in last alphabetically. * - * Each method pair tests one hook ordering attribute. + * Each method pair tests one hook ordering permutation. */ class TestHookAfterClassMethod { -- GitLab From 31bd213abd784b3791f166314d7606195cd24e62 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 16 Dec 2024 09:21:11 -0500 Subject: [PATCH 075/268] Move complex hook registration to HCP --- .../Drupal/Core/Hook/HookCollectorPass.php | 73 +++++++++++++++---- core/lib/Drupal/Core/Hook/HookPriority.php | 52 ++----------- 2 files changed, 68 insertions(+), 57 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index e6dbc90b59f5..5c9b25697ec1 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -71,6 +71,7 @@ public function process(ContainerBuilder $container): array { $implementations = []; $orderGroups = []; $orderAttributes = []; + $moduleFinder = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { @@ -82,6 +83,7 @@ public function process(ContainerBuilder $container): array { } $legacyImplementations[$hook->hook][$hook->module] = ''; $implementations[$hook->hook][$hook->module][$class][] = $hook->method; + $moduleFinder[$class][$method] = $hook->module; if ($hook->order) { $orderAttributes[] = $hook; if ($hook->order instanceof ComplexOrder && ($group = $hook->order->group)) { @@ -102,7 +104,7 @@ public function process(ContainerBuilder $container): array { // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { static::registerImplementations($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); - static::reOrderImplementations($container, $orderAttributes, $orderGroups, $implementations); + static::reOrderImplementations($container, $orderAttributes, $orderGroups, $implementations, $moduleFinder); } return $implementations; } @@ -160,7 +162,7 @@ protected static function registerImplementations(ContainerBuilder $container, H } foreach ($method_hooks as $method) { $map[$hook][$class][$method] = $module; - $priority = self::addTagToDefinition($definition, "drupal_hook.$hook", $method, $priority); + $priority = self::addTagToDefinition($definition, $hook, $method, $priority); } } unset($implementations[$hook][$module]); @@ -184,33 +186,53 @@ protected static function registerImplementations(ContainerBuilder $container, H * Groups to order by. * @param array $implementations * Hook implementations. + * @param array $moduleFinder + * An array keyed by the class and method of a hook implementation, value + * is the module. This is not necessarily the same as the module the class + * is in because the implementation might be on behalf of another module. * * @return void */ - protected static function reOrderImplementations(ContainerBuilder $container, array $orderAttributes, array $orderGroups, array $implementations): void { + protected static function reOrderImplementations(ContainerBuilder $container, array $orderAttributes, array $orderGroups, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); foreach ($orderAttributes as $orderAttribute) { assert($orderAttribute instanceof Hook); // ::process() adds the hook serving as key to the order group so it // does not need to be added if there's a group for the hook. $hooks = $orderGroups[$orderAttribute->hook] ?? [$orderAttribute->hook]; + $combinedHook = implode(':', $hooks); if ($orderAttribute->order instanceof ComplexOrder) { - $others = []; + // Verify the correct structure of + // $orderAttribute->order->classesAndMethods and create specifiers + // for HookPriority::change() while at it. + $otherSpecifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $orderAttribute->order->classesAndMethods); + // Collect classes and methods for + // self::registerComplexHookImplementations(). + $classesAndMethods = $orderAttribute->order->classesAndMethods; foreach ($orderAttribute->order->modules as $modules) { foreach ($hooks as $hook) { foreach ($implementations[$hook][$modules] ?? [] as $class => $methods) { foreach ($methods as $method) { - $others[] = [$class, $method]; + $classesAndMethods[] = [$class, $method]; + $otherSpecifiers[] = "$class::$method"; } } } } - $others = array_merge($others, $orderAttribute->order->classesAndMethods); + if (count($hooks) > 1) { + // The hook implementation in $orderAttribute and everything in + // $classesAndMethods will be ordered relative to each other as if + // they were implementing a single hook. This needs to be marked on + // their service definition and added to the + // hook_implementations_map container parameter. + $classesAndMethods[] = [$orderAttribute->class, $orderAttribute->method]; + self::registerComplexHookImplementations($container, $classesAndMethods, $moduleFinder, $combinedHook); + } } else { - $others = NULL; + $otherSpecifiers = NULL; } - $hookPriority->change($hooks, $orderAttribute, $others); + $hookPriority->change("drupal_hook.$combinedHook", $orderAttribute, $otherSpecifiers); } } @@ -220,7 +242,7 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar * @param array $module_filenames * An associative array. Keys are the module names, values are relevant * info yml file path. - * @param Symfony\Component\DependencyInjection\ContainerBuilder|null $container + * @param \Symfony\Component\DependencyInjection\ContainerBuilder|null $container * The container. * * @return static @@ -459,8 +481,8 @@ protected static function getAttributeInstances(array $attributes, array $reflec * * @param \Symfony\Component\DependencyInjection\Definition $definition * The service definition. - * @param string $event - * The name of the event, typically starts with drupal_hook. + * @param string $hook + * The name of the hook. * @param string $method * The method. * @param int $priority @@ -469,13 +491,38 @@ protected static function getAttributeInstances(array $attributes, array $reflec * @return int * A new priority, guaranteed to be lower than $priority. */ - public static function addTagToDefinition(Definition $definition, string $event, string $method, int $priority): int { + protected static function addTagToDefinition(Definition $definition, string $hook, string $method, int $priority): int { $definition->addTag('kernel.event_listener', [ - 'event' => $event, + 'event' => "drupal_hook.$hook", 'method' => $method, 'priority' => $priority--, ]); return $priority; } + /** + * Register complex hook implementations. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * The container. + * @param array $classesAndMethods + * A list of class-and-method pairs. + * @param array $moduleFinder + * A module finder array, see ::reOrderImplementations() for explanation. + * @param string $combinedHook + * A string made form list of hooks separated by : + */ + protected static function registerComplexHookImplementations(ContainerBuilder $container, array $classesAndMethods, array $moduleFinder, string $combinedHook): void { + $map = $container->getParameter('hook_implementations_map'); + $priority = 0; + foreach ($classesAndMethods as [$class, $method]) { + // Ordering against not installed modules is possible. + if (isset($moduleFinder[$class][$method])) { + $map[$combinedHook][$class][$method] = $moduleFinder[$class][$method]; + $priority = self::addTagToDefinition($container->findDefinition($class), $combinedHook, $method, $priority); + } + } + $container->setParameter('hook_implementations_map', $map); + } + } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 603dbaa6f379..72d931963d47 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -19,53 +19,17 @@ public function __construct(protected ContainerBuilder $container) {} /** * Change the priority of a hook implementation. * - * @param array $hooks - * The list of hooks to order. The list always contains the hook defined - * in Drupal\Core\Hook\Attribute, and it might also contain - * the hooks listed in the Drupal\Core\Hook\ComplexOrder $group + * @param string $event + * Listeners to this event will be ordered. * @param \Drupal\Core\Hook\Attribute\Hook $hook * The hook attribute. - * @param array|null $others + * @param array|null $other_specifiers * Other hook implementations to compare to, if any. The array is a list of - * class and method pairs. + * strings, each string is a class and method separated by ::. * * @internal */ - public function change(array $hooks, Hook $hook, ?array $others = NULL): void { - if ($others) { - $other_specifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $others); - } - if (count($hooks) > 1) { - // Mark $hook implementation and everything in $others as implementing - // a single combined hook made from $hooks. This is necessary because - // ordering is only possible between the implementations of the same - // hook. - $map = $this->container->getParameter('hook_implementations_map'); - $combinedHookTag = implode(':', $hooks); - $event = "drupal_hook.$combinedHookTag"; - $data = $others; - $data[] = [$hook->class, $hook->method]; - $priority = 0; - foreach ($data as [$class, $method]) { - // If the class and method exists at all it surely implements a hook - // because it is being ordered against and then this implementation - // is already registered in the implementation map and allows finding - // out what the corresponding module is. This can't be found out from - // parsing the class name because a hook might be implemented on - // behalf of another module. - foreach ($hooks as $indexHook) { - if (isset($map[$indexHook][$class][$method])) { - $map[$combinedHookTag][$class][$method] = $map[$indexHook][$class][$method]; - $priority = HookCollectorPass::addTagToDefinition($this->container->findDefinition($class), $event, $method, $priority); - break; - } - } - } - $this->container->setParameter('hook_implementations_map', $map); - } - else { - $event = 'drupal_hook.' . reset($hooks); - } + public function change(string $event, Hook $hook, ?array $other_specifiers = NULL): void { foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $tags) { foreach ($tags as $key => $tag) { if ($tag['event'] === $event) { @@ -80,9 +44,9 @@ public function change(array $hooks, Hook $hook, ?array $others = NULL): void { if ($specifier === "$hook->class::$hook->method") { $index_this = $index; } - // $others is defined for before and after, for these compare only - // the priority of those. For first and last the priority of every - // other hook matters. + // $other_specifiers is defined for before and after, for these + // compare only the priority of those. For first and last the + // priority of every other hook matters. elseif (!isset($other_specifiers) || in_array($specifier, $other_specifiers)) { $priorities_other[$specifier] = $priority; } -- GitLab From 74066a1c8b5f7649ebee1c3d2e226c216aa31e23 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 16 Dec 2024 10:53:56 -0500 Subject: [PATCH 076/268] Type fixing --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 5c9b25697ec1..4b45bf841141 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -481,8 +481,8 @@ protected static function getAttributeInstances(array $attributes, array $reflec * * @param \Symfony\Component\DependencyInjection\Definition $definition * The service definition. - * @param string $hook - * The name of the hook. + * @param string|int $hook + * The name of the hook * @param string $method * The method. * @param int $priority @@ -491,7 +491,7 @@ protected static function getAttributeInstances(array $attributes, array $reflec * @return int * A new priority, guaranteed to be lower than $priority. */ - protected static function addTagToDefinition(Definition $definition, string $hook, string $method, int $priority): int { + protected static function addTagToDefinition(Definition $definition, string|int $hook, string $method, int $priority): int { $definition->addTag('kernel.event_listener', [ 'event' => "drupal_hook.$hook", 'method' => $method, -- GitLab From f45f31499f8ef01e167c76aeb4f940b323ab07f1 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 09:37:21 -0500 Subject: [PATCH 077/268] Convert content_translation --- .../content_translation.module | 22 ------------------- .../src/Hook/ContentTranslationHooks.php | 5 +++-- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 97bdc664e259..49b15bb022bc 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -10,28 +10,6 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; -/** - * Implements hook_module_implements_alter(). - */ -function content_translation_module_implements_alter(&$implementations, $hook): void { - switch ($hook) { - // Move our hook_entity_type_alter() implementation to the end of the list. - case 'entity_type_alter': - $group = $implementations['content_translation']; - unset($implementations['content_translation']); - $implementations['content_translation'] = $group; - break; - - // Move our hook_entity_bundle_info_alter() implementation to the top of the - // list, so that any other hook implementation can rely on bundles being - // correctly marked as translatable. - case 'entity_bundle_info_alter': - $group = $implementations['content_translation']; - $implementations = ['content_translation' => $group] + $implementations; - break; - } -} - /** * Installs Content Translation's fields for a given entity type. * diff --git a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php index 0f38a7f005db..f878db7feb0b 100644 --- a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php +++ b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php @@ -17,6 +17,7 @@ use Drupal\Core\Url; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Order; /** * Hook implementations for content_translation. @@ -139,7 +140,7 @@ public function languageTypesInfoAlter(array &$language_types): void { * * @see \Drupal\Core\Entity\Annotation\EntityType */ - #[Hook('entity_type_alter')] + #[Hook('entity_type_alter', order: Order::First)] public function entityTypeAlter(array &$entity_types) : void { // Provide defaults for translation info. /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ @@ -189,7 +190,7 @@ public function entityTypeAlter(array &$entity_types) : void { * @see content_translation_entity_bundle_info_alter() * @see \Drupal\content_translation\ContentTranslationManager::isEnabled() */ - #[Hook('language_content_settings_insert')] + #[Hook('language_content_settings_insert', order: Order::Last)] public function languageContentSettingsInsert(ContentLanguageSettingsInterface $settings): void { if ($settings->getThirdPartySetting('content_translation', 'enabled', FALSE)) { _content_translation_install_field_storage_definitions($settings->getTargetEntityTypeId()); -- GitLab From 9a782f5b6f275f63b29469fc8c36f579143555ca Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 09:39:03 -0500 Subject: [PATCH 078/268] Convert layout_builder --- core/modules/layout_builder/layout_builder.module | 14 -------------- .../layout_builder/src/Hook/LayoutBuilderHooks.php | 3 ++- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/core/modules/layout_builder/layout_builder.module b/core/modules/layout_builder/layout_builder.module index 32ce8bee9459..37dc492bf787 100644 --- a/core/modules/layout_builder/layout_builder.module +++ b/core/modules/layout_builder/layout_builder.module @@ -6,20 +6,6 @@ use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage; -/** - * Implements hook_module_implements_alter(). - */ -function layout_builder_module_implements_alter(&$implementations, $hook): void { - if ($hook === 'entity_view_alter') { - // Ensure that this module's implementation of hook_entity_view_alter() runs - // last so that other modules that use this hook to render extra fields will - // run before it. - $group = $implementations['layout_builder']; - unset($implementations['layout_builder']); - $implementations['layout_builder'] = $group; - } -} - /** * Implements hook_preprocess_HOOK() for language-content-settings-table.html.twig. */ diff --git a/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php b/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php index d12f9f0bc684..5be94e5e80ee 100644 --- a/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php +++ b/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php @@ -26,6 +26,7 @@ use Drupal\Core\Url; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Order; /** * Hook implementations for layout_builder. @@ -151,7 +152,7 @@ public function fieldConfigDelete(FieldConfigInterface $field_config): void { * @see \Drupal\layout_builder\Plugin\Block\ExtraFieldBlock::build() * @see layout_builder_module_implements_alter() */ - #[Hook('entity_view_alter')] + #[Hook('entity_view_alter', order: Order::Last)] public function entityViewAlter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display): void { // Only replace extra fields when Layout Builder has been used to alter the // build. See \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::buildMultiple(). -- GitLab From 794dc990f6e5befc8c3c74404fcf05974c069dae Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 09:42:48 -0500 Subject: [PATCH 079/268] layout_builder_test --- .../layout_builder_test/layout_builder_test.module | 14 -------------- .../src/Hook/LayoutBuilderTestHooks.php | 11 ++++++++++- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module index de2e5316d073..d7dda399be39 100644 --- a/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module @@ -49,17 +49,3 @@ function layout_builder_test_preprocess_layout__twocol_section(&$vars): void { ]; } } - -/** - * Implements hook_module_implements_alter(). - */ -function layout_builder_test_module_implements_alter(&$implementations, $hook): void { - if ($hook === 'system_breadcrumb_alter') { - // Move our hook_system_breadcrumb_alter() implementation to run before - // layout_builder_system_breadcrumb_alter(). - $group = $implementations['layout_builder_test']; - $implementations = [ - 'layout_builder_test' => $group, - ] + $implementations; - } -} diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php index 7a0d47979035..d968b97c1e22 100644 --- a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php @@ -11,6 +11,8 @@ use Drupal\Core\Entity\Display\EntityFormDisplayInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\OrderBefore; +use Drupal\layout_builder\Hook\LayoutBuilderHooks; /** * Hook implementations for layout_builder_test. @@ -115,7 +117,14 @@ public function layoutBuilderEntityFormDisplayAlter(EntityFormDisplayInterface $ /** * Implements hook_system_breadcrumb_alter(). */ - #[Hook('system_breadcrumb_alter')] + #[Hook( + 'system_breadcrumb_alter', + order: new OrderBefore( + classesAndMethods: [ + [LayoutBuilderHooks::class, 'systemBreadcrumbAlter'], + ] + ) + )] public function systemBreadcrumbAlter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context): void { $breadcrumb->addLink(Link::fromTextAndUrl('External link', Url::fromUri('http://www.example.com'))); } -- GitLab From fc5cc23b9639fc8d7caaf2919b68ce6ec101d4b4 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 09:46:48 -0500 Subject: [PATCH 080/268] convert part of navigation --- core/modules/navigation/navigation.module | 5 ----- core/modules/navigation/src/Hook/NavigationHooks.php | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/core/modules/navigation/navigation.module b/core/modules/navigation/navigation.module index dbc11f191e64..ecf6a31f934f 100644 --- a/core/modules/navigation/navigation.module +++ b/core/modules/navigation/navigation.module @@ -10,11 +10,6 @@ * Implements hook_module_implements_alter(). */ function navigation_module_implements_alter(&$implementations, $hook): void { - if ($hook == 'page_top') { - $group = $implementations['navigation']; - unset($implementations['navigation']); - $implementations['navigation'] = $group; - } if ($hook == 'help') { // We take over the layout_builder hook_help(). unset($implementations['layout_builder']); diff --git a/core/modules/navigation/src/Hook/NavigationHooks.php b/core/modules/navigation/src/Hook/NavigationHooks.php index 11bf1773dfe3..30c76860885d 100644 --- a/core/modules/navigation/src/Hook/NavigationHooks.php +++ b/core/modules/navigation/src/Hook/NavigationHooks.php @@ -7,6 +7,7 @@ use Drupal\Core\Config\Action\ConfigActionManager; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Order; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -79,7 +80,7 @@ public function help($route_name, RouteMatchInterface $route_match): ?string { /** * Implements hook_page_top(). */ - #[Hook('page_top')] + #[Hook('page_top', order: Order::Last)] public function pageTop(array &$page_top): void { if (!$this->currentUser->hasPermission('access navigation')) { return; -- GitLab From 4b35a935c94d917ed2133cf35bf2a975fe261a0f Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 10:15:03 -0500 Subject: [PATCH 081/268] Address comments in MR --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 5 ++++- core/lib/Drupal/Core/Hook/ComplexOrder.php | 4 ++-- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 5 ++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 129fde02abc5..393bf7cf930b 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -81,7 +81,10 @@ class ModuleHandler implements ModuleHandlerInterface { * @param array $groupIncludes * An array of .inc files to get helpers from. * @param array $orderGroups - * An array of hooks that have been ordered by attributes. + * A multidimensional array of hooks that have been ordered and the group + * of hooks they have been ordered against. This is stored separately from + * $hookImplementationsMap to prevent ordering again since this group has + * already been fully ordered in HookCollectorPass. * * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider diff --git a/core/lib/Drupal/Core/Hook/ComplexOrder.php b/core/lib/Drupal/Core/Hook/ComplexOrder.php index 30d05a3bde06..2d9ae3839306 100644 --- a/core/lib/Drupal/Core/Hook/ComplexOrder.php +++ b/core/lib/Drupal/Core/Hook/ComplexOrder.php @@ -17,8 +17,8 @@ /** * Whether the priority of this hook should be larger than others. * - * The value of this variable is the same as the constant ::VALUE, it only - * exists so ComplexOrder and Order types both have the same value property. + * This is fixed to the constant ::VALUE, it simplifies ordering by ensuring + * ComplexOrder and Order types both have a value property. * * @var bool */ diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 4b45bf841141..04c1ca26473c 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -112,8 +112,7 @@ public function process(ContainerBuilder $container): array { /** * Register hook implementations as event listeners. * - * Passes required include information to module_handler. - * Passes required runtime ordering information to module_handler. + * Passes required include and ordering information to module_handler. * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. @@ -122,7 +121,7 @@ public function process(ContainerBuilder $container): array { * @param array $implementations * All implementations. * @param array $legacyImplementations - * Modules that implement hooks. + * Modules that implement legacy hooks. * @param array $orderGroups * Groups of hooks to reorder. * -- GitLab From 930f3dec4769acf10a528e7ce41e0fcd06409d4f Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 10:49:09 -0500 Subject: [PATCH 082/268] Update correct hooks --- .../src/Hook/ContentTranslationHooks.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php index f878db7feb0b..8c0d0e816f61 100644 --- a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php +++ b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php @@ -140,7 +140,7 @@ public function languageTypesInfoAlter(array &$language_types): void { * * @see \Drupal\Core\Entity\Annotation\EntityType */ - #[Hook('entity_type_alter', order: Order::First)] + #[Hook('entity_type_alter', order: Order::Last)] public function entityTypeAlter(array &$entity_types) : void { // Provide defaults for translation info. /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ @@ -190,8 +190,13 @@ public function entityTypeAlter(array &$entity_types) : void { * @see content_translation_entity_bundle_info_alter() * @see \Drupal\content_translation\ContentTranslationManager::isEnabled() */ +<<<<<<< HEAD #[Hook('language_content_settings_insert', order: Order::Last)] public function languageContentSettingsInsert(ContentLanguageSettingsInterface $settings): void { +======= + #[Hook('language_content_settings_insert')] + public function languageContentSettingsInsert(ContentLanguageSettingsInterface $settings): void { +>>>>>>> beef39243fd (Update correct hooks) if ($settings->getThirdPartySetting('content_translation', 'enabled', FALSE)) { _content_translation_install_field_storage_definitions($settings->getTargetEntityTypeId()); } @@ -222,7 +227,7 @@ public function languageContentSettingsUpdate(ContentLanguageSettingsInterface $ /** * Implements hook_entity_bundle_info_alter(). */ - #[Hook('entity_bundle_info_alter')] + #[Hook('entity_bundle_info_alter', order: Order::First)] public function entityBundleInfoAlter(&$bundles): void { /** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */ $content_translation_manager = \Drupal::service('content_translation.manager'); -- GitLab From 6a02695995a3d9368efb953ea85f123414ac7f57 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 14:48:49 -0500 Subject: [PATCH 083/268] Add replacement functionality and convert navigation --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 3 ++ .../Drupal/Core/Hook/HookCollectorPass.php | 17 +++++++++++ core/modules/navigation/navigation.module | 10 ------- .../navigation/src/Hook/NavigationHooks.php | 2 +- .../hook_test_replacements.info.yml | 7 +++++ .../src/Hook/TestHookReplacements.php | 30 +++++++++++++++++++ .../Core/Hook/HookCollectorPassTest.php | 16 ++++++++++ 7 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 core/modules/system/tests/modules/hook_test_replacements/hook_test_replacements.info.yml create mode 100644 core/modules/system/tests/modules/hook_test_replacements/src/Hook/TestHookReplacements.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 2a4cfc0e9095..debabeda89ba 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -117,12 +117,15 @@ class Hook { * the implementation is in. * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order * (optional) Set the order of the implementation. + * @param array|null $replacements + * (optional) An array keyed by modules of hook implementations to remove. */ public function __construct( public string $hook, public string $method = '', public ?string $module = NULL, public Order|ComplexOrder|NULL $order = NULL, + public array|NULL $replacements = NULL, ) {} /** diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 04c1ca26473c..5ca74d8c1716 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -72,6 +72,7 @@ public function process(ContainerBuilder $container): array { $orderGroups = []; $orderAttributes = []; $moduleFinder = []; + $allReplacements = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { @@ -93,11 +94,27 @@ public function process(ContainerBuilder $container): array { } } } + if ($hook->hook == 'help' && $hook->module == 'navigation') { + $test = 1; + } + if ($hook->replacements) { + foreach ($hook->replacements as $module_replacement => $replacements) { + $allReplacements[$module_replacement] = array_merge($allReplacements[$module_replacement] ?? [], $replacements); + } + } } } } } $orderGroups = array_map('array_unique', $orderGroups); + $allReplacements = array_map('array_unique', $allReplacements); + + foreach ($allReplacements as $module_replacement => $replacements) { + foreach($replacements as $replacement) { + unset($implementations[$replacement][$module_replacement]); + unset($legacyImplementations[$replacement][$module_replacement]); + } + } // @todo investigate whether this if() is needed after ModuleHandler::add() // is removed. diff --git a/core/modules/navigation/navigation.module b/core/modules/navigation/navigation.module index ecf6a31f934f..570f13ba3e4a 100644 --- a/core/modules/navigation/navigation.module +++ b/core/modules/navigation/navigation.module @@ -6,16 +6,6 @@ use Drupal\navigation\TopBarRegion; -/** - * Implements hook_module_implements_alter(). - */ -function navigation_module_implements_alter(&$implementations, $hook): void { - if ($hook == 'help') { - // We take over the layout_builder hook_help(). - unset($implementations['layout_builder']); - } -} - /** * Prepares variables for navigation top bar template. * diff --git a/core/modules/navigation/src/Hook/NavigationHooks.php b/core/modules/navigation/src/Hook/NavigationHooks.php index 30c76860885d..fc20a3298b99 100644 --- a/core/modules/navigation/src/Hook/NavigationHooks.php +++ b/core/modules/navigation/src/Hook/NavigationHooks.php @@ -55,7 +55,7 @@ public function __construct( /** * Implements hook_help(). */ - #[Hook('help')] + #[Hook('help', replacements: ['layout_builder' => ['help']])] public function help($route_name, RouteMatchInterface $route_match): ?string { switch ($route_name) { case 'help.page.navigation': diff --git a/core/modules/system/tests/modules/hook_test_replacements/hook_test_replacements.info.yml b/core/modules/system/tests/modules/hook_test_replacements/hook_test_replacements.info.yml new file mode 100644 index 000000000000..d6baedba7fa5 --- /dev/null +++ b/core/modules/system/tests/modules/hook_test_replacements/hook_test_replacements.info.yml @@ -0,0 +1,7 @@ +name: Hook test replacements +type: module +description: 'Test module used to test hook replacement.' +package: Testing +version: VERSION +core_version_requirement: '*' +hidden: true diff --git a/core/modules/system/tests/modules/hook_test_replacements/src/Hook/TestHookReplacements.php b/core/modules/system/tests/modules/hook_test_replacements/src/Hook/TestHookReplacements.php new file mode 100644 index 000000000000..672f7f6e65ad --- /dev/null +++ b/core/modules/system/tests/modules/hook_test_replacements/src/Hook/TestHookReplacements.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_test_replacements\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +/** + * Add a hook here, then remove it with another attribute. + */ +class TestHookReplacements { + + /** + * This hook should not be run because the next hook replaces it. + */ + #[Hook('custom_hook1')] + public static function hookDoNotRun(): void { + $GLOBALS['HookShouldNotRunTestReplacement'] = 'HookShouldNotRunTestReplacement'; + } + + /** + * This hook should run and prevent custom_hook1. + */ + #[Hook('custom_hook2', replacements: ['hook_test_replacements' => ['custom_hook1']])] + public static function hookDoRun(): void { + $GLOBALS['HookShouldRunTestReplacement'] = 'HookShouldRunTestReplacement'; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 7109df1e9357..a21d5cfa3bb0 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -249,4 +249,20 @@ public function testHookLast(): void { $this->assertTrue(isset($GLOBALS['HookRanTestingHookLast'])); } + /** + * Tests hook replacements. + */ + public function testHookReplacements(): void { + $module_installer = $this->container->get('module_installer'); + $this->assertTrue($module_installer->install(['hook_test_replacements'])); + $this->assertFalse(isset($GLOBALS['HookShouldRunTestReplacement'])); + $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestReplacement'])); + $module_handler = $this->container->get('module_handler'); + $data = ['hi']; + $module_handler->invokeAll('custom_hook1', $data); + $module_handler->invokeAll('custom_hook2', $data); + $this->assertTrue(isset($GLOBALS['HookShouldRunTestReplacement'])); + $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestReplacement'])); + } + } -- GitLab From 67f273fbfea8b20054693ffeaf3fe147ae124ffe Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 14:53:25 -0500 Subject: [PATCH 084/268] CS --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 5ca74d8c1716..63602b709dc1 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -94,9 +94,6 @@ public function process(ContainerBuilder $container): array { } } } - if ($hook->hook == 'help' && $hook->module == 'navigation') { - $test = 1; - } if ($hook->replacements) { foreach ($hook->replacements as $module_replacement => $replacements) { $allReplacements[$module_replacement] = array_merge($allReplacements[$module_replacement] ?? [], $replacements); @@ -110,7 +107,7 @@ public function process(ContainerBuilder $container): array { $allReplacements = array_map('array_unique', $allReplacements); foreach ($allReplacements as $module_replacement => $replacements) { - foreach($replacements as $replacement) { + foreach ($replacements as $replacement) { unset($implementations[$replacement][$module_replacement]); unset($legacyImplementations[$replacement][$module_replacement]); } -- GitLab From 87c7783b29e4baaa839511be031e2f43acc1b0e8 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 16:16:58 -0500 Subject: [PATCH 085/268] Convert Workspaces --- .../workspaces/src/Hook/EntityOperations.php | 6 ++-- core/modules/workspaces/workspaces.module | 35 ------------------- 2 files changed, 4 insertions(+), 37 deletions(-) delete mode 100644 core/modules/workspaces/workspaces.module diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index c459f4d065e3..aac53b237dc6 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -12,6 +12,7 @@ use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Order; use Drupal\workspaces\WorkspaceAssociationInterface; use Drupal\workspaces\WorkspaceInformationInterface; use Drupal\workspaces\WorkspaceManagerInterface; @@ -72,7 +73,8 @@ public function entityPreload(array $ids, string $entity_type_id): array { /** * Implements hook_entity_presave(). */ - #[Hook('entity_presave')] + #[Hook('entity_presave', order: Order::First)] + #[Hook('entity_presave', order: Order::Last)] public function entityPresave(EntityInterface $entity): void { if ($this->shouldSkipOperations($entity)) { return; @@ -129,7 +131,7 @@ public function entityPresave(EntityInterface $entity): void { /** * Implements hook_entity_insert(). */ - #[Hook('entity_insert')] + #[Hook('entity_insert', order: Order::Last)] public function entityInsert(EntityInterface $entity): void { if ($entity->getEntityTypeId() === 'workspace') { $this->workspaceAssociation->workspaceInsert($entity); diff --git a/core/modules/workspaces/workspaces.module b/core/modules/workspaces/workspaces.module deleted file mode 100644 index a053105f20c3..000000000000 --- a/core/modules/workspaces/workspaces.module +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @file - */ - -/** - * Implements hook_module_implements_alter(). - */ -function workspaces_module_implements_alter(&$implementations, $hook): void { - // Move our 'hook_entity_presave' implementation at the beginning to ensure - // that other presave implementations are aware of the changes done in - // \Drupal\workspaces\Hook\EntityOperations::entityPresave(). - if ($hook === 'entity_presave') { - $implementation = $implementations['workspaces']; - $implementations = ['workspaces' => $implementation] + $implementations; - - // Move Content Moderation's implementation before Workspaces, so we can - // alter the publishing status for the default revision. - if (isset($implementations['content_moderation'])) { - $implementation = $implementations['content_moderation']; - $implementations = ['content_moderation' => $implementation] + $implementations; - } - } - - // Move our 'hook_entity_insert' implementation at the end to ensure that - // the second (pending) revision created for published entities is not used - // by other 'hook_entity_insert' implementations. - // @see \Drupal\workspaces\Hook\EntityOperations::entityInsert() - if ($hook === 'entity_insert') { - $group = $implementations['workspaces']; - unset($implementations['workspaces']); - $implementations['workspaces'] = $group; - } -} -- GitLab From bbef6b6c4e3d04ed0a2ba8e91ed916dd5c1d1756 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 31 Dec 2024 17:08:31 -0500 Subject: [PATCH 086/268] Use simpler ordering option --- .../layout_builder_test/src/Hook/LayoutBuilderTestHooks.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php index d968b97c1e22..5a2a09729bcd 100644 --- a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php @@ -12,7 +12,6 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\OrderBefore; -use Drupal\layout_builder\Hook\LayoutBuilderHooks; /** * Hook implementations for layout_builder_test. @@ -120,9 +119,7 @@ public function layoutBuilderEntityFormDisplayAlter(EntityFormDisplayInterface $ #[Hook( 'system_breadcrumb_alter', order: new OrderBefore( - classesAndMethods: [ - [LayoutBuilderHooks::class, 'systemBreadcrumbAlter'], - ] + modules: ['layout_builder'] ) )] public function systemBreadcrumbAlter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context): void { -- GitLab From 5d3ecad777916edf876e6708d2050100a9086e74 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 31 Dec 2024 19:17:57 -0500 Subject: [PATCH 087/268] Rename to remove --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 4 ++-- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 17 ++++++++--------- .../navigation/src/Hook/NavigationHooks.php | 2 +- .../hook_test_remove.info.yml} | 4 ++-- .../src/Hook/TestHookRemove.php} | 10 +++++----- .../Core/Hook/HookCollectorPassTest.php | 10 +++++----- 6 files changed, 23 insertions(+), 24 deletions(-) rename core/modules/system/tests/modules/{hook_test_replacements/hook_test_replacements.info.yml => hook_test_remove/hook_test_remove.info.yml} (50%) rename core/modules/system/tests/modules/{hook_test_replacements/src/Hook/TestHookReplacements.php => hook_test_remove/src/Hook/TestHookRemove.php} (55%) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index debabeda89ba..4c1b9e255bd6 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -117,7 +117,7 @@ class Hook { * the implementation is in. * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order * (optional) Set the order of the implementation. - * @param array|null $replacements + * @param array|null $remove * (optional) An array keyed by modules of hook implementations to remove. */ public function __construct( @@ -125,7 +125,7 @@ public function __construct( public string $method = '', public ?string $module = NULL, public Order|ComplexOrder|NULL $order = NULL, - public array|NULL $replacements = NULL, + public array|NULL $remove = NULL, ) {} /** diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 63602b709dc1..c305ac64e83b 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -72,7 +72,7 @@ public function process(ContainerBuilder $container): array { $orderGroups = []; $orderAttributes = []; $moduleFinder = []; - $allReplacements = []; + $allRemovals = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { @@ -94,9 +94,9 @@ public function process(ContainerBuilder $container): array { } } } - if ($hook->replacements) { - foreach ($hook->replacements as $module_replacement => $replacements) { - $allReplacements[$module_replacement] = array_merge($allReplacements[$module_replacement] ?? [], $replacements); + if ($hook->remove) { + foreach ($hook->remove as $module_remove => $removals) { + $allRemovals[$module_remove] = array_merge($allRemovals[$module_remove] ?? [], $removals); } } } @@ -104,12 +104,11 @@ public function process(ContainerBuilder $container): array { } } $orderGroups = array_map('array_unique', $orderGroups); - $allReplacements = array_map('array_unique', $allReplacements); - foreach ($allReplacements as $module_replacement => $replacements) { - foreach ($replacements as $replacement) { - unset($implementations[$replacement][$module_replacement]); - unset($legacyImplementations[$replacement][$module_replacement]); + foreach ($allRemovals as $module_remove => $removals) { + foreach ($removals as $removal) { + unset($implementations[$removal][$module_remove]); + unset($legacyImplementations[$removal][$module_remove]); } } diff --git a/core/modules/navigation/src/Hook/NavigationHooks.php b/core/modules/navigation/src/Hook/NavigationHooks.php index fc20a3298b99..b5984a5f9512 100644 --- a/core/modules/navigation/src/Hook/NavigationHooks.php +++ b/core/modules/navigation/src/Hook/NavigationHooks.php @@ -55,7 +55,7 @@ public function __construct( /** * Implements hook_help(). */ - #[Hook('help', replacements: ['layout_builder' => ['help']])] + #[Hook('help', remove: ['layout_builder' => ['help']])] public function help($route_name, RouteMatchInterface $route_match): ?string { switch ($route_name) { case 'help.page.navigation': diff --git a/core/modules/system/tests/modules/hook_test_replacements/hook_test_replacements.info.yml b/core/modules/system/tests/modules/hook_test_remove/hook_test_remove.info.yml similarity index 50% rename from core/modules/system/tests/modules/hook_test_replacements/hook_test_replacements.info.yml rename to core/modules/system/tests/modules/hook_test_remove/hook_test_remove.info.yml index d6baedba7fa5..9245dd4208f7 100644 --- a/core/modules/system/tests/modules/hook_test_replacements/hook_test_replacements.info.yml +++ b/core/modules/system/tests/modules/hook_test_remove/hook_test_remove.info.yml @@ -1,6 +1,6 @@ -name: Hook test replacements +name: Hook test removal type: module -description: 'Test module used to test hook replacement.' +description: 'Test module used to test hook removal.' package: Testing version: VERSION core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/hook_test_replacements/src/Hook/TestHookReplacements.php b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php similarity index 55% rename from core/modules/system/tests/modules/hook_test_replacements/src/Hook/TestHookReplacements.php rename to core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php index 672f7f6e65ad..3065cd1a727c 100644 --- a/core/modules/system/tests/modules/hook_test_replacements/src/Hook/TestHookReplacements.php +++ b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php @@ -2,29 +2,29 @@ declare(strict_types=1); -namespace Drupal\hook_test_replacements\Hook; +namespace Drupal\hook_test_remove\Hook; use Drupal\Core\Hook\Attribute\Hook; /** * Add a hook here, then remove it with another attribute. */ -class TestHookReplacements { +class TestHookRemove { /** * This hook should not be run because the next hook replaces it. */ #[Hook('custom_hook1')] public static function hookDoNotRun(): void { - $GLOBALS['HookShouldNotRunTestReplacement'] = 'HookShouldNotRunTestReplacement'; + $GLOBALS['HookShouldNotRunTestRemove'] = 'HookShouldNotRunTestRemove'; } /** * This hook should run and prevent custom_hook1. */ - #[Hook('custom_hook2', replacements: ['hook_test_replacements' => ['custom_hook1']])] + #[Hook('custom_hook2', remove: ['hook_test_remove' => ['custom_hook1']])] public static function hookDoRun(): void { - $GLOBALS['HookShouldRunTestReplacement'] = 'HookShouldRunTestReplacement'; + $GLOBALS['HookShouldRunTestRemove'] = 'HookShouldRunTestRemove'; } } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index a21d5cfa3bb0..29a5984545a7 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -254,15 +254,15 @@ public function testHookLast(): void { */ public function testHookReplacements(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_test_replacements'])); - $this->assertFalse(isset($GLOBALS['HookShouldRunTestReplacement'])); - $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestReplacement'])); + $this->assertTrue($module_installer->install(['hook_test_remove'])); + $this->assertFalse(isset($GLOBALS['HookShouldRunTestRemove'])); + $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestRemove'])); $module_handler = $this->container->get('module_handler'); $data = ['hi']; $module_handler->invokeAll('custom_hook1', $data); $module_handler->invokeAll('custom_hook2', $data); - $this->assertTrue(isset($GLOBALS['HookShouldRunTestReplacement'])); - $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestReplacement'])); + $this->assertTrue(isset($GLOBALS['HookShouldRunTestRemove'])); + $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestRemove'])); } } -- GitLab From 5aa02cc80610471c83117abc36466b248ab3870a Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 1 Jan 2025 10:31:45 -0500 Subject: [PATCH 088/268] Ordering --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 4c1b9e255bd6..74159d2c1205 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -34,8 +34,13 @@ * } * @endcode * - * Ordering hook implementations can be done by implementing - * hook_module_implements_alter. + * Ordering hook implementations can be done by using the order parameter. + * + * @see https://www.drupal.org/node/3493962 + * + * Removing hook implementations can be done by using the remove parameter. + * + * @see https://www.drupal.org/node/3496786 * * Classes that use this annotation on the class or on their methods are * automatically registered as autowired services with the class name as the -- GitLab From 11fcd270b8761e10bda74844af92bd22b595570d Mon Sep 17 00:00:00 2001 From: Andrei Mateescu <andrei@amateescu.me> Date: Thu, 2 Jan 2025 21:04:09 +0200 Subject: [PATCH 089/268] Fix Workspaces presave implementations. --- core/modules/workspaces/src/Hook/EntityOperations.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index aac53b237dc6..1fbe0cc7d446 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -74,7 +74,6 @@ public function entityPreload(array $ids, string $entity_type_id): array { * Implements hook_entity_presave(). */ #[Hook('entity_presave', order: Order::First)] - #[Hook('entity_presave', order: Order::Last)] public function entityPresave(EntityInterface $entity): void { if ($this->shouldSkipOperations($entity)) { return; @@ -115,6 +114,16 @@ public function entityPresave(EntityInterface $entity): void { $field_name = $entity->getEntityType()->getRevisionMetadataKey('workspace'); $entity->{$field_name}->target_id = $this->workspaceManager->getActiveWorkspace()->id(); } + } + + /** + * Implements hook_entity_presave(). + */ + #[Hook('entity_presave', order: Order::Last)] + public function entityPresaveLast(EntityInterface $entity): void { + if ($this->shouldSkipOperations($entity)) { + return; + } // When a new published entity is inserted in a non-default workspace, we // actually want two revisions to be saved: -- GitLab From 248aae8a9a75d181f66dcd770181b928a33b1045 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 3 Jan 2025 11:23:00 -0500 Subject: [PATCH 090/268] Handle reordering other hook implementations --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 15 +++++++-------- .../lib/Drupal/Core/Hook/HookCollectorPass.php | 6 +++--- .../workspaces/src/Hook/EntityOperations.php | 18 ++++++++---------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 74159d2c1205..96ed909f8e00 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -99,18 +99,14 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Hook { - /** - * The class the hook implementation is in. - * - * @var string - */ - public string $class = ''; - /** * Constructs a Hook attribute object. * * @param string $hook * The short hook name, without the 'hook_' prefix. + * @param string $class + * (optional) The class name. This should only be used when ordering on + * behalf of another hook. * @param string $method * (optional) The method name. If this attribute is on a method, this * parameter is not required. If this attribute is on a class and this @@ -127,6 +123,7 @@ class Hook { */ public function __construct( public string $hook, + public string $class = '', public string $method = '', public ?string $module = NULL, public Order|ComplexOrder|NULL $order = NULL, @@ -144,7 +141,9 @@ public function __construct( * The method for the hook. */ public function set(string $class, string $module, string $method): void { - $this->class = $class; + if (!$this->class) { + $this->class = $class; + } if (!$this->module) { $this->module = $module; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index c305ac64e83b..2de66a91ecdd 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -79,12 +79,12 @@ public function process(ContainerBuilder $container): array { foreach ($hooks as $hook) { assert($hook instanceof Hook); $hook->set(class: $class, module: $module, method: $method); - if ($class !== ProceduralCall::class) { + if ($hook->class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hook); } $legacyImplementations[$hook->hook][$hook->module] = ''; - $implementations[$hook->hook][$hook->module][$class][] = $hook->method; - $moduleFinder[$class][$method] = $hook->module; + $implementations[$hook->hook][$hook->module][$hook->class][$hook->method] = $hook->method; + $moduleFinder[$hook->class][$method] = $hook->module; if ($hook->order) { $orderAttributes[] = $hook; if ($hook->order instanceof ComplexOrder && ($group = $hook->order->group)) { diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index 1fbe0cc7d446..69d6eef9a953 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -13,6 +13,8 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\OrderBefore; +use Drupal\content_moderation\Hook\ContentModerationHooks; use Drupal\workspaces\WorkspaceAssociationInterface; use Drupal\workspaces\WorkspaceInformationInterface; use Drupal\workspaces\WorkspaceManagerInterface; @@ -74,6 +76,12 @@ public function entityPreload(array $ids, string $entity_type_id): array { * Implements hook_entity_presave(). */ #[Hook('entity_presave', order: Order::First)] + #[Hook('entity_presave', + module: 'content_moderation', + class: ContentModerationHooks::class, + method: 'entityPresave', + order: new OrderBefore(['workspaces']) + )] public function entityPresave(EntityInterface $entity): void { if ($this->shouldSkipOperations($entity)) { return; @@ -114,16 +122,6 @@ public function entityPresave(EntityInterface $entity): void { $field_name = $entity->getEntityType()->getRevisionMetadataKey('workspace'); $entity->{$field_name}->target_id = $this->workspaceManager->getActiveWorkspace()->id(); } - } - - /** - * Implements hook_entity_presave(). - */ - #[Hook('entity_presave', order: Order::Last)] - public function entityPresaveLast(EntityInterface $entity): void { - if ($this->shouldSkipOperations($entity)) { - return; - } // When a new published entity is inserted in a non-default workspace, we // actually want two revisions to be saved: -- GitLab From 92afa062ecb53e5e81d5723ed1b51680f3a0998e Mon Sep 17 00:00:00 2001 From: Ghost Of Drupal Past <drupal@negyesi.net> Date: Fri, 3 Jan 2025 18:00:05 +0100 Subject: [PATCH 091/268] i really hate this bullshit --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 ---- core/lib/Drupal/Core/Hook/HookPriority.php | 2 -- 2 files changed, 6 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 2de66a91ecdd..6c070b70a81e 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -137,8 +137,6 @@ public function process(ContainerBuilder $container): array { * Modules that implement legacy hooks. * @param array $orderGroups * Groups of hooks to reorder. - * - * @return void */ protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementations, array $orderGroups): void { $container->register(ProceduralCall::class, ProceduralCall::class) @@ -202,8 +200,6 @@ protected static function registerImplementations(ContainerBuilder $container, H * An array keyed by the class and method of a hook implementation, value * is the module. This is not necessarily the same as the module the class * is in because the implementation might be on behalf of another module. - * - * @return void */ protected static function reOrderImplementations(ContainerBuilder $container, array $orderAttributes, array $orderGroups, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 72d931963d47..9a9fe4e6321a 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -104,8 +104,6 @@ public function change(string $event, Hook $hook, ?array $other_specifiers = NUL * hook implementation to be changed. * @param int $priority * The new priority. - * - * @return void */ public function set(string $class, int $key, int $priority): void { $definition = $this->container->findDefinition($class); -- GitLab From 440da0105138cf96157b560d57d48e08793f355f Mon Sep 17 00:00:00 2001 From: Ghost Of Drupal Past <drupal@negyesi.net> Date: Fri, 3 Jan 2025 18:11:42 +0100 Subject: [PATCH 092/268] meh --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 8 ++++---- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 96ed909f8e00..4bc3e4612692 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -104,9 +104,6 @@ class Hook { * * @param string $hook * The short hook name, without the 'hook_' prefix. - * @param string $class - * (optional) The class name. This should only be used when ordering on - * behalf of another hook. * @param string $method * (optional) The method name. If this attribute is on a method, this * parameter is not required. If this attribute is on a class and this @@ -120,14 +117,17 @@ class Hook { * (optional) Set the order of the implementation. * @param array|null $remove * (optional) An array keyed by modules of hook implementations to remove. + * @param string $class + * (optional) The class name. This should only be used when ordering on + * behalf of another hook. */ public function __construct( public string $hook, - public string $class = '', public string $method = '', public ?string $module = NULL, public Order|ComplexOrder|NULL $order = NULL, public array|NULL $remove = NULL, + public string $class = '', ) {} /** diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 6c070b70a81e..7c3323af2b3f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -394,7 +394,7 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv * The name of function implementing the hook. */ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { - $this->moduleHooks[$module][ProceduralCall::class][$function] = [new Hook($hook, $module . '_' . $hook)]; + $this->moduleHooks[$module][ProceduralCall::class][$function] = [new Hook($hook, method: $module . '_' . $hook)]; if ($hook === 'hook_info') { $this->hookInfo[] = $function; } -- GitLab From 7d362b509d9a6c4bf0db8ded2cea34f61e165254 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 3 Jan 2025 13:07:56 -0500 Subject: [PATCH 093/268] Fix optional hook classes --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 7c3323af2b3f..f6a81f92ee90 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -78,6 +78,9 @@ public function process(ContainerBuilder $container): array { foreach ($methods as $method => $hooks) { foreach ($hooks as $hook) { assert($hook instanceof Hook); + if ($hook->class && !class_exists($hook->class, FALSE)) { + continue; + } $hook->set(class: $class, module: $module, method: $method); if ($hook->class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hook); -- GitLab From a25acd0c0f48279091d0651aff0823b27dea5c48 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 3 Jan 2025 17:20:22 -0500 Subject: [PATCH 094/268] Named remove and override --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 14 +++++----- .../Core/Hook/Attribute/OverrideHook.php | 24 +++++++++++++++++ .../Drupal/Core/Hook/Attribute/RemoveHook.php | 20 ++++++++++++++ .../Drupal/Core/Hook/HookCollectorPass.php | 26 +++++++++---------- .../navigation/src/Hook/NavigationHooks.php | 5 +++- .../src/Hook/TestHookRemove.php | 4 ++- .../workspaces/src/Hook/EntityOperations.php | 5 ++-- 7 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php create mode 100644 core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 4bc3e4612692..a48e3a52c289 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -99,6 +99,13 @@ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Hook { + /** + * The class the hook implementation is in. + * + * @var string + */ + public string $class = ''; + /** * Constructs a Hook attribute object. * @@ -115,19 +122,12 @@ class Hook { * the implementation is in. * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order * (optional) Set the order of the implementation. - * @param array|null $remove - * (optional) An array keyed by modules of hook implementations to remove. - * @param string $class - * (optional) The class name. This should only be used when ordering on - * behalf of another hook. */ public function __construct( public string $hook, public string $method = '', public ?string $module = NULL, public Order|ComplexOrder|NULL $order = NULL, - public array|NULL $remove = NULL, - public string $class = '', ) {} /** diff --git a/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php b/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php new file mode 100644 index 000000000000..cb5cab6f09e9 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php @@ -0,0 +1,24 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +use Drupal\Core\Hook\ComplexOrder; +use Drupal\Core\Hook\Order; + +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class OverrideHook extends Hook { + + public function __construct( + string $hook, + string $class, + string $module, + string $method, + Order|ComplexOrder $order, + ) { + parent::__construct($hook, method: $method, module: $module, order: $order); + $this->class = $class; + } + +} diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php new file mode 100644 index 000000000000..ee9fb2dc868f --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -0,0 +1,20 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +class RemoveHook extends Hook { + + public function __construct( + string $hook, + string $class, + string $module, + string $method, + ) { + parent::__construct($hook, module: $module, method: $method); + $this->class = $class; + } + +} diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index f6a81f92ee90..b8f8189196be 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -10,6 +10,8 @@ use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\LegacyHook; +use Drupal\Core\Hook\Attribute\OverrideHook; +use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -69,16 +71,21 @@ class HookCollectorPass implements CompilerPassInterface { public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); $implementations = []; + $legacyImplementations = []; $orderGroups = []; $orderAttributes = []; $moduleFinder = []; - $allRemovals = []; + $removals = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { foreach ($hooks as $hook) { assert($hook instanceof Hook); - if ($hook->class && !class_exists($hook->class, FALSE)) { + if ($hook instanceof OverrideHook && !class_exists($hook->class, FALSE)) { + continue; + } + if ($hook instanceof RemoveHook) { + $removals[] = $hook; continue; } $hook->set(class: $class, module: $module, method: $method); @@ -97,22 +104,15 @@ public function process(ContainerBuilder $container): array { } } } - if ($hook->remove) { - foreach ($hook->remove as $module_remove => $removals) { - $allRemovals[$module_remove] = array_merge($allRemovals[$module_remove] ?? [], $removals); - } - } } } } } $orderGroups = array_map('array_unique', $orderGroups); - foreach ($allRemovals as $module_remove => $removals) { - foreach ($removals as $removal) { - unset($implementations[$removal][$module_remove]); - unset($legacyImplementations[$removal][$module_remove]); - } + foreach ($removals as $hook) { + unset($legacyImplementations[$hook->hook][$hook->module]); + unset($implementations[$hook->hook][$hook->module][$hook->class][$hook->method]); } // @todo investigate whether this if() is needed after ModuleHandler::add() @@ -479,7 +479,7 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class = ' */ protected static function getAttributeInstances(array $attributes, array $reflections): array { foreach ($reflections as $reflection) { - if ($reflection_attributes = $reflection->getAttributes()) { + if ($reflection_attributes = $reflection->getAttributes(Hook::class, \ReflectionAttribute::IS_INSTANCEOF)) { $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; $attributes[$method] = array_map(fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflection_attributes); } diff --git a/core/modules/navigation/src/Hook/NavigationHooks.php b/core/modules/navigation/src/Hook/NavigationHooks.php index b5984a5f9512..ee30220ae773 100644 --- a/core/modules/navigation/src/Hook/NavigationHooks.php +++ b/core/modules/navigation/src/Hook/NavigationHooks.php @@ -7,10 +7,12 @@ use Drupal\Core\Config\Action\ConfigActionManager; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Order; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\layout_builder\Hook\LayoutBuilderHooks; use Drupal\navigation\NavigationContentLinks; use Drupal\navigation\NavigationRenderer; use Drupal\navigation\Plugin\SectionStorage\NavigationSectionStorage; @@ -55,7 +57,8 @@ public function __construct( /** * Implements hook_help(). */ - #[Hook('help', remove: ['layout_builder' => ['help']])] + #[Hook('help')] + #[RemoveHook('help', LayoutBuilderHooks::class, 'layout_builder', 'help')] public function help($route_name, RouteMatchInterface $route_match): ?string { switch ($route_name) { case 'help.page.navigation': diff --git a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php index 3065cd1a727c..1c7137863cd2 100644 --- a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php +++ b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php @@ -5,6 +5,7 @@ namespace Drupal\hook_test_remove\Hook; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\RemoveHook; /** * Add a hook here, then remove it with another attribute. @@ -22,7 +23,8 @@ public static function hookDoNotRun(): void { /** * This hook should run and prevent custom_hook1. */ - #[Hook('custom_hook2', remove: ['hook_test_remove' => ['custom_hook1']])] + #[Hook('custom_hook2')] + #[RemoveHook('custom_hook1', self::class, 'hook_test_remove', 'hookDoNotRun')] public static function hookDoRun(): void { $GLOBALS['HookShouldRunTestRemove'] = 'HookShouldRunTestRemove'; } diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index 69d6eef9a953..e0dfe0a15b0e 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -12,6 +12,7 @@ use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\OverrideHook; use Drupal\Core\Hook\Order; use Drupal\Core\Hook\OrderBefore; use Drupal\content_moderation\Hook\ContentModerationHooks; @@ -76,9 +77,9 @@ public function entityPreload(array $ids, string $entity_type_id): array { * Implements hook_entity_presave(). */ #[Hook('entity_presave', order: Order::First)] - #[Hook('entity_presave', - module: 'content_moderation', + #[OverrideHook('entity_presave', class: ContentModerationHooks::class, + module: 'content_moderation', method: 'entityPresave', order: new OrderBefore(['workspaces']) )] -- GitLab From b9d7d4ef25d030072dd43764b2854b65eee72415 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 3 Jan 2025 17:26:41 -0500 Subject: [PATCH 095/268] PHP stan --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index b8f8189196be..8e1a1fe795fa 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -119,7 +119,7 @@ public function process(ContainerBuilder $container): array { // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $legacyImplementations ?? [], $orderGroups); + static::registerImplementations($container, $collector, $implementations, $legacyImplementations, $orderGroups); static::reOrderImplementations($container, $orderAttributes, $orderGroups, $implementations, $moduleFinder); } return $implementations; -- GitLab From dd3d7c659b0cbda4aab74b775a6fdb4292bec28d Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Fri, 3 Jan 2025 23:44:21 -0500 Subject: [PATCH 096/268] Handle removal and override cleaner --- .../Core/Hook/Attribute/OverrideHook.php | 22 +++++++++++++-- .../Drupal/Core/Hook/Attribute/RemoveHook.php | 16 +++++++++-- .../Drupal/Core/Hook/HookCollectorPass.php | 27 ++++++++++--------- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php b/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php index cb5cab6f09e9..ebce073a46f0 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php @@ -7,17 +7,35 @@ use Drupal\Core\Hook\ComplexOrder; use Drupal\Core\Hook\Order; +/** + * Attribute for overriding the order of another Hook. + * + * When another hook needs to be ordered provide an OverrideHook attribute + * that specifies the new ordering attribute. + */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class OverrideHook extends Hook { + /** + * Constructs a Hook attribute object. + * + * @param string $hook + * The short hook name, without the 'hook_' prefix. + * @param string $class + * The class the implementation to modify is in. This allows one module to + * affect the order of another module's hook. + * @param string $method + * The method name of the implementation to modify. + * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder $order + * Set the order of the implementation. + */ public function __construct( string $hook, string $class, - string $module, string $method, Order|ComplexOrder $order, ) { - parent::__construct($hook, method: $method, module: $module, order: $order); + parent::__construct($hook, method: $method, order: $order); $this->class = $class; } diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index ee9fb2dc868f..1ba132742a87 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -4,16 +4,28 @@ namespace Drupal\Core\Hook\Attribute; +/** + * Attribute for removing another implementation. + */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class RemoveHook extends Hook { + /** + * Constructs a Hook attribute object. + * + * @param string $hook + * The short hook name, without the 'hook_' prefix. + * @param string $class + * The class the implementation to remove is in. + * @param string $method + * The method name of the implementation to remove. + */ public function __construct( string $hook, string $class, - string $module, string $method, ) { - parent::__construct($hook, module: $module, method: $method); + parent::__construct($hook, method: $method); $this->class = $class; } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 8e1a1fe795fa..83912d8fdc74 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -75,26 +75,17 @@ public function process(ContainerBuilder $container): array { $orderGroups = []; $orderAttributes = []; $moduleFinder = []; + /** @var \Drupal\Core\Hook\Attribute\RemoveHook[] $removals */ $removals = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { foreach ($hooks as $hook) { assert($hook instanceof Hook); - if ($hook instanceof OverrideHook && !class_exists($hook->class, FALSE)) { - continue; - } if ($hook instanceof RemoveHook) { $removals[] = $hook; continue; } - $hook->set(class: $class, module: $module, method: $method); - if ($hook->class !== ProceduralCall::class) { - self::checkForProceduralOnlyHooks($hook); - } - $legacyImplementations[$hook->hook][$hook->module] = ''; - $implementations[$hook->hook][$hook->module][$hook->class][$hook->method] = $hook->method; - $moduleFinder[$hook->class][$method] = $hook->module; if ($hook->order) { $orderAttributes[] = $hook; if ($hook->order instanceof ComplexOrder && ($group = $hook->order->group)) { @@ -104,6 +95,16 @@ public function process(ContainerBuilder $container): array { } } } + if ($hook instanceof OverrideHook) { + continue; + } + if ($class !== ProceduralCall::class) { + self::checkForProceduralOnlyHooks($hook); + } + $hook->set(... compact('class', 'method', 'module')); + $legacyImplementations[$hook->hook][$hook->module] = ''; + $implementations[$hook->hook][$hook->module][$class][$hook->method] = $hook->method; + $moduleFinder[$class][$hook->method] = $hook->module; } } } @@ -111,8 +112,10 @@ public function process(ContainerBuilder $container): array { $orderGroups = array_map('array_unique', $orderGroups); foreach ($removals as $hook) { - unset($legacyImplementations[$hook->hook][$hook->module]); - unset($implementations[$hook->hook][$hook->module][$hook->class][$hook->method]); + if ($module = ($moduleFinder[$hook->class][$hook->method] ?? '')) { + unset($legacyImplementations[$hook->hook][$module]); + unset($implementations[$hook->hook][$module][$hook->class][$hook->method]); + } } // @todo investigate whether this if() is needed after ModuleHandler::add() -- GitLab From 78b684909464d4d5cdf94b5cf81a7dd6f7ad14e9 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Fri, 3 Jan 2025 23:59:29 -0500 Subject: [PATCH 097/268] Override last --- .../Drupal/Core/Hook/HookCollectorPass.php | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 83912d8fdc74..9aca95cacebb 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -77,6 +77,8 @@ public function process(ContainerBuilder $container): array { $moduleFinder = []; /** @var \Drupal\Core\Hook\Attribute\RemoveHook[] $removals */ $removals = []; + /** @var \Drupal\Core\Hook\Attribute\OverrideHook[] $overrides */ + $overrides = []; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { @@ -86,18 +88,14 @@ public function process(ContainerBuilder $container): array { $removals[] = $hook; continue; } - if ($hook->order) { - $orderAttributes[] = $hook; - if ($hook->order instanceof ComplexOrder && ($group = $hook->order->group)) { - $group[] = $hook->hook; - foreach ($group as $extraHook) { - $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $group); - } - } - } if ($hook instanceof OverrideHook) { + // Gather overrides to ensure these can run last. + $overrides[] = $hook; continue; } + if ($hook->order) { + $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); + } if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hook); } @@ -118,6 +116,11 @@ public function process(ContainerBuilder $container): array { } } + foreach ($overrides as $hook) { + // Append overrides last. + $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); + } + // @todo investigate whether this if() is needed after ModuleHandler::add() // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 @@ -128,6 +131,26 @@ public function process(ContainerBuilder $container): array { return $implementations; } + /** + * Gather ordering information. + * + * @param \Drupal\Core\Hook\Attributes\Hook $hook + * The hook with ordering information. + * @param array $orderAttributes + * All attributes related to ordering. + * @param array $orderGroups + * Groups to order by. + */ + protected function gatherOrderInformation(Hook $hook, array &$orderAttributes, array &$orderGroups): void { + $orderAttributes[] = $hook; + if ($hook->order instanceof ComplexOrder && ($group = $hook->order->group)) { + $group[] = $hook->hook; + foreach ($group as $extraHook) { + $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $group); + } + } + } + /** * Register hook implementations as event listeners. * -- GitLab From 1eac4a3e212a612d3d429c0de70df0dd56f37d88 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 4 Jan 2025 00:21:18 -0500 Subject: [PATCH 098/268] Test HookOverride --- .../src/Hook/TestHookOverrideHookFirst.php | 45 +++++++++++++++++++ .../src/Hook/TestHookOverrideHookSecond.php | 39 ++++++++++++++++ .../src/Hook/TestHookRemove.php | 6 ++- .../Core/Hook/HookCollectorPassTest.php | 22 ++++++++- 4 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php create mode 100644 core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php new file mode 100644 index 000000000000..5fc5d6ae056c --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php @@ -0,0 +1,45 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_first_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Attribute\OverrideHook; +use Drupal\hook_order_last_alphabetically\Hook\TestHookOverrideHookSecond; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering permutation. + */ +class TestHookOverrideHookFirst { + + /** + * This pair tests OverrideHook. + */ + #[Hook('custom_hook_override')] + #[OverrideHook( + 'custom_hook_override', + class: TestHookOverrideHookSecond::class, + method: 'customHookOverride', + order: new OrderAfter( + classesAndMethods: [[TestHookOverrideHookFirst::class, 'customHookOverride']], + ) + )] + public static function customHookOverride(): void { + // This normally would run first. + // We override that order in hook_order_second_alphabetically. + // We override, that order here with OverrideHook. + $GLOBALS['HookRanTestingOverrideHookFirstAlpha'] = 'HookRanTestingOverrideHookFirstAlpha'; + } + +} diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php new file mode 100644 index 000000000000..bb7d26a6c311 --- /dev/null +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php @@ -0,0 +1,39 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hook_order_last_alphabetically\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Order; + +/** + * Hook implementations for verifying ordering hooks by attributes. + * + * We must ensure that the order of the modules is expected and then change + * the order that the hooks are run in order to verify. This module + * comes in a pair first alphabetically and last alphabetically. + * + * In the normal order a hook implemented by first alphabetically would run + * before the same hook in last alphabetically. + * + * Each method pair tests one hook ordering permutation. + */ +class TestHookOverrideHookSecond { + + /** + * This pair tests OverrideHook. + */ + #[Hook('custom_hook_override', order: Order::First)] + public static function customHookOverride(): void { + // This normally would run second. + // We override that order here with Order::First. + // We override, that order in hook_order_first_alphabetically with + // OverrideHook. + if (!isset($GLOBALS['HookRanTestingOverrideHookFirstAlpha'])) { + $GLOBALS['HookOutOfOrderTestingOverrideHook'] = 'HookOutOfOrderTestingOverrideHook'; + } + $GLOBALS['HookRanTestingOverrideHookSecondAlpha'] = 'HookRanTestingOverrideHookSecondAlpha'; + } + +} diff --git a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php index 1c7137863cd2..3ea922ec36ce 100644 --- a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php +++ b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php @@ -24,7 +24,11 @@ public static function hookDoNotRun(): void { * This hook should run and prevent custom_hook1. */ #[Hook('custom_hook2')] - #[RemoveHook('custom_hook1', self::class, 'hook_test_remove', 'hookDoNotRun')] + #[RemoveHook( + 'custom_hook1', + class: TestHookRemove::class, + method: 'hookDoNotRun' + )] public static function hookDoRun(): void { $GLOBALS['HookShouldRunTestRemove'] = 'HookShouldRunTestRemove'; } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 29a5984545a7..d74c8d8e2aa2 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -250,9 +250,9 @@ public function testHookLast(): void { } /** - * Tests hook replacements. + * Tests hook remove. */ - public function testHookReplacements(): void { + public function testHookRemove(): void { $module_installer = $this->container->get('module_installer'); $this->assertTrue($module_installer->install(['hook_test_remove'])); $this->assertFalse(isset($GLOBALS['HookShouldRunTestRemove'])); @@ -265,4 +265,22 @@ public function testHookReplacements(): void { $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestRemove'])); } + /** + * Tests hook override. + */ + public function testHookOverride(): void { + $module_installer = $this->container->get('module_installer'); + $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); + $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingOverrideHookFirstAlpha'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOverrideHook'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingOverrideHookSecondAlpha'])); + $module_handler = $this->container->get('module_handler'); + $data = ['hi']; + $module_handler->invokeAll('custom_hook_override', $data); + $this->assertTrue(isset($GLOBALS['HookRanTestingOverrideHookFirstAlpha'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOverrideHook'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingOverrideHookSecondAlpha'])); + } + } -- GitLab From 71b0e4ec747a4c14e286be8d89280e13fd66b421 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 4 Jan 2025 00:26:03 -0500 Subject: [PATCH 099/268] Stan --- core/modules/navigation/src/Hook/NavigationHooks.php | 2 +- core/modules/workspaces/src/Hook/EntityOperations.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/modules/navigation/src/Hook/NavigationHooks.php b/core/modules/navigation/src/Hook/NavigationHooks.php index ee30220ae773..da19ec2dc768 100644 --- a/core/modules/navigation/src/Hook/NavigationHooks.php +++ b/core/modules/navigation/src/Hook/NavigationHooks.php @@ -58,7 +58,7 @@ public function __construct( * Implements hook_help(). */ #[Hook('help')] - #[RemoveHook('help', LayoutBuilderHooks::class, 'layout_builder', 'help')] + #[RemoveHook('help', class: LayoutBuilderHooks::class, method: 'help')] public function help($route_name, RouteMatchInterface $route_match): ?string { switch ($route_name) { case 'help.page.navigation': diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index e0dfe0a15b0e..4240a4daea7e 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -79,7 +79,6 @@ public function entityPreload(array $ids, string $entity_type_id): array { #[Hook('entity_presave', order: Order::First)] #[OverrideHook('entity_presave', class: ContentModerationHooks::class, - module: 'content_moderation', method: 'entityPresave', order: new OrderBefore(['workspaces']) )] -- GitLab From 033dba284f3ac7c176f27475ca53df8613e71ac9 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 4 Jan 2025 09:19:01 -0500 Subject: [PATCH 100/268] Rename OverrideHook --- .../{OverrideHook.php => ReOrderHook.php} | 7 ++-- .../Drupal/Core/Hook/Attribute/RemoveHook.php | 2 +- .../Drupal/Core/Hook/HookCollectorPass.php | 33 ++++++++----------- .../src/Hook/TestHookOverrideHookFirst.php | 18 +++++----- .../src/Hook/TestHookOverrideHookSecond.php | 12 +++---- .../workspaces/src/Hook/EntityOperations.php | 4 +-- .../Core/Hook/HookCollectorPassTest.php | 12 +++---- 7 files changed, 40 insertions(+), 48 deletions(-) rename core/lib/Drupal/Core/Hook/Attribute/{OverrideHook.php => ReOrderHook.php} (81%) diff --git a/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php similarity index 81% rename from core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php rename to core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index ebce073a46f0..c7483819b828 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/OverrideHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -8,13 +8,10 @@ use Drupal\Core\Hook\Order; /** - * Attribute for overriding the order of another Hook. - * - * When another hook needs to be ordered provide an OverrideHook attribute - * that specifies the new ordering attribute. + * Set the order of an already existing implementation. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class OverrideHook extends Hook { +class ReOrderHook extends Hook { /** * Constructs a Hook attribute object. diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index 1ba132742a87..a911cc29e5df 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -5,7 +5,7 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for removing another implementation. + * Attribute for removing an implementation. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class RemoveHook extends Hook { diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 9aca95cacebb..ba30aa953b10 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -10,7 +10,7 @@ use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\LegacyHook; -use Drupal\Core\Hook\Attribute\OverrideHook; +use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -75,27 +75,20 @@ public function process(ContainerBuilder $container): array { $orderGroups = []; $orderAttributes = []; $moduleFinder = []; - /** @var \Drupal\Core\Hook\Attribute\RemoveHook[] $removals */ - $removals = []; - /** @var \Drupal\Core\Hook\Attribute\OverrideHook[] $overrides */ - $overrides = []; + // These need to be processed after normal hooks. + $process_after = [ + RemoveHook::class => [], + ReOrderHook::class => [], + ]; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { foreach ($hooks as $hook) { assert($hook instanceof Hook); - if ($hook instanceof RemoveHook) { - $removals[] = $hook; - continue; - } - if ($hook instanceof OverrideHook) { - // Gather overrides to ensure these can run last. - $overrides[] = $hook; + if (isset($process_after[get_class($hook)])) { + $process_after[get_class($hook)][] = $hook; continue; } - if ($hook->order) { - $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); - } if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hook); } @@ -103,23 +96,25 @@ public function process(ContainerBuilder $container): array { $legacyImplementations[$hook->hook][$hook->module] = ''; $implementations[$hook->hook][$hook->module][$class][$hook->method] = $hook->method; $moduleFinder[$class][$hook->method] = $hook->module; + if ($hook->order) { + $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); + } } } } } - $orderGroups = array_map('array_unique', $orderGroups); - foreach ($removals as $hook) { + foreach ($process_after[RemoveHook::class] as $hook) { if ($module = ($moduleFinder[$hook->class][$hook->method] ?? '')) { unset($legacyImplementations[$hook->hook][$module]); unset($implementations[$hook->hook][$module][$hook->class][$hook->method]); } } - foreach ($overrides as $hook) { - // Append overrides last. + foreach ($process_after[ReOrderHook::class] as $hook) { $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); } + $orderGroups = array_map('array_unique', $orderGroups); // @todo investigate whether this if() is needed after ModuleHandler::add() // is removed. diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php index 5fc5d6ae056c..0c73d1e15975 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php @@ -6,8 +6,8 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\OrderAfter; -use Drupal\Core\Hook\Attribute\OverrideHook; -use Drupal\hook_order_last_alphabetically\Hook\TestHookOverrideHookSecond; +use Drupal\Core\Hook\Attribute\ReOrderHook; +use Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookSecond; /** * Hook implementations for verifying ordering hooks by attributes. @@ -21,25 +21,25 @@ * * Each method pair tests one hook ordering permutation. */ -class TestHookOverrideHookFirst { +class TestHookReOrderHookFirst { /** - * This pair tests OverrideHook. + * This pair tests ReOrderHook. */ #[Hook('custom_hook_override')] - #[OverrideHook( + #[ReOrderHook( 'custom_hook_override', - class: TestHookOverrideHookSecond::class, + class: TestHookReOrderHookSecond::class, method: 'customHookOverride', order: new OrderAfter( - classesAndMethods: [[TestHookOverrideHookFirst::class, 'customHookOverride']], + classesAndMethods: [[TestHookReOrderHookFirst::class, 'customHookOverride']], ) )] public static function customHookOverride(): void { // This normally would run first. // We override that order in hook_order_second_alphabetically. - // We override, that order here with OverrideHook. - $GLOBALS['HookRanTestingOverrideHookFirstAlpha'] = 'HookRanTestingOverrideHookFirstAlpha'; + // We override, that order here with ReOrderHook. + $GLOBALS['HookRanTestingReOrderHookFirstAlpha'] = 'HookRanTestingReOrderHookFirstAlpha'; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php index bb7d26a6c311..b649e5408497 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php @@ -19,21 +19,21 @@ * * Each method pair tests one hook ordering permutation. */ -class TestHookOverrideHookSecond { +class TestHookReOrderHookSecond { /** - * This pair tests OverrideHook. + * This pair tests ReOrderHook. */ #[Hook('custom_hook_override', order: Order::First)] public static function customHookOverride(): void { // This normally would run second. // We override that order here with Order::First. // We override, that order in hook_order_first_alphabetically with - // OverrideHook. - if (!isset($GLOBALS['HookRanTestingOverrideHookFirstAlpha'])) { - $GLOBALS['HookOutOfOrderTestingOverrideHook'] = 'HookOutOfOrderTestingOverrideHook'; + // ReOrderHook. + if (!isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])) { + $GLOBALS['HookOutOfOrderTestingReOrderHook'] = 'HookOutOfOrderTestingReOrderHook'; } - $GLOBALS['HookRanTestingOverrideHookSecondAlpha'] = 'HookRanTestingOverrideHookSecondAlpha'; + $GLOBALS['HookRanTestingReOrderHookSecondAlpha'] = 'HookRanTestingReOrderHookSecondAlpha'; } } diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index 4240a4daea7e..915499f685eb 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -12,7 +12,7 @@ use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\OverrideHook; +use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\Core\Hook\Order; use Drupal\Core\Hook\OrderBefore; use Drupal\content_moderation\Hook\ContentModerationHooks; @@ -77,7 +77,7 @@ public function entityPreload(array $ids, string $entity_type_id): array { * Implements hook_entity_presave(). */ #[Hook('entity_presave', order: Order::First)] - #[OverrideHook('entity_presave', + #[ReOrderHook('entity_presave', class: ContentModerationHooks::class, method: 'entityPresave', order: new OrderBefore(['workspaces']) diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index d74c8d8e2aa2..333db179a831 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -272,15 +272,15 @@ public function testHookOverride(): void { $module_installer = $this->container->get('module_installer'); $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingOverrideHookFirstAlpha'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOverrideHook'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingOverrideHookSecondAlpha'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingReOrderHook'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingReOrderHookSecondAlpha'])); $module_handler = $this->container->get('module_handler'); $data = ['hi']; $module_handler->invokeAll('custom_hook_override', $data); - $this->assertTrue(isset($GLOBALS['HookRanTestingOverrideHookFirstAlpha'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOverrideHook'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingOverrideHookSecondAlpha'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingReOrderHook'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingReOrderHookSecondAlpha'])); } } -- GitLab From b2c21c4508cc20d3ad1ef9990cc6204b112388ef Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 4 Jan 2025 09:28:10 -0500 Subject: [PATCH 101/268] Rename classes --- ...HookOverrideHookFirst.php => TestHookReOrderHookFirst.php} | 4 ++-- ...HookOverrideHookSecond.php => TestHookReOrderHookLast.php} | 4 ++-- .../Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/{TestHookOverrideHookFirst.php => TestHookReOrderHookFirst.php} (96%) rename core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/{TestHookOverrideHookSecond.php => TestHookReOrderHookLast.php} (89%) diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php similarity index 96% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php rename to core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php index 0c73d1e15975..8e48435ebae1 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOverrideHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php @@ -7,7 +7,7 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\OrderAfter; use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookSecond; +use Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookLast; /** * Hook implementations for verifying ordering hooks by attributes. @@ -29,7 +29,7 @@ class TestHookReOrderHookFirst { #[Hook('custom_hook_override')] #[ReOrderHook( 'custom_hook_override', - class: TestHookReOrderHookSecond::class, + class: TestHookReOrderHookLast::class, method: 'customHookOverride', order: new OrderAfter( classesAndMethods: [[TestHookReOrderHookFirst::class, 'customHookOverride']], diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php similarity index 89% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php rename to core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php index b649e5408497..a46e3583609e 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOverrideHookSecond.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php @@ -19,7 +19,7 @@ * * Each method pair tests one hook ordering permutation. */ -class TestHookReOrderHookSecond { +class TestHookReOrderHookLast { /** * This pair tests ReOrderHook. @@ -33,7 +33,7 @@ public static function customHookOverride(): void { if (!isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])) { $GLOBALS['HookOutOfOrderTestingReOrderHook'] = 'HookOutOfOrderTestingReOrderHook'; } - $GLOBALS['HookRanTestingReOrderHookSecondAlpha'] = 'HookRanTestingReOrderHookSecondAlpha'; + $GLOBALS['HookRanTestingReOrderHookLastAlpha'] = 'HookRanTestingReOrderHookLastAlpha'; } } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 333db179a831..38740b09d818 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -274,13 +274,13 @@ public function testHookOverride(): void { $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); $this->assertFalse(isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingReOrderHook'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingReOrderHookSecondAlpha'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingReOrderHookLastAlpha'])); $module_handler = $this->container->get('module_handler'); $data = ['hi']; $module_handler->invokeAll('custom_hook_override', $data); $this->assertTrue(isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])); $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingReOrderHook'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingReOrderHookSecondAlpha'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingReOrderHookLastAlpha'])); } } -- GitLab From 386e5050fdfdb1bf065769125c9e1a1372b23e83 Mon Sep 17 00:00:00 2001 From: Ghost Of Drupal Past <drupal@negyesi.net> Date: Mon, 10 Feb 2025 15:19:32 +0100 Subject: [PATCH 102/268] add HookPriority unit tests --- core/lib/Drupal/Core/Hook/HookPriority.php | 4 +- .../Hook/HookPriorityEqualPriorityTest.php | 76 +++++++++ .../Tests/Core/Hook/HookPriorityTest.php | 148 ++++++++++++++++++ .../Tests/Core/Hook/HookPriorityTestBase.php | 84 ++++++++++ 4 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php create mode 100644 core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php create mode 100644 core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 9a9fe4e6321a..1fc4df0abd55 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -22,7 +22,9 @@ public function __construct(protected ContainerBuilder $container) {} * @param string $event * Listeners to this event will be ordered. * @param \Drupal\Core\Hook\Attribute\Hook $hook - * The hook attribute. + * The hook attribute. Most of the order parameter is ignored by this + * class, only $hook->order->value is used. The rest is preprocessed by + * HookCollectorPass and passed in $other_specifiers. * @param array|null $other_specifiers * Other hook implementations to compare to, if any. The array is a list of * strings, each string is a class and method separated by ::. diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php new file mode 100644 index 000000000000..644f53ee8c6b --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php @@ -0,0 +1,76 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\Core\Hook; + +use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\OrderBefore; + +/** + * @coversDefaultClass \Drupal\Core\Hook\HookPriority + * + * @group Hook + */ +class HookPriorityEqualPriorityTest extends HookPriorityTestBase { + + protected function setUp(): void { + parent::setUp(); + // The priority of "a", "b", "c" are the same, the order is undefined. + $this->setUpContainer(FALSE); + $this->assertSame($this->getPriority('a'), $this->getPriority('b')); + $this->assertSame($this->getPriority('b'), $this->getPriority('c')); + } + + /** + * @covers ::first + */ + public function testFirst(): void { + // "c" was first, make "a" the first. + $this->doPriorityChange('a', Order::First); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); + $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); + // Nothing else can be asserted: by setting the same priority, the setup + // had undefined order and so the services not included in the helper call + // can be in any order. + } + + /** + * @covers ::last + */ + public function testLast(): void { + // "c" was first, make it the last. + $this->doPriorityChange('c', Order::Last); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); + // Nothing else can be asserted: by setting the same priority, the setup + // had undefined order and so the services not included in the helper call + // can be in any order. + } + + /** + * @covers ::before + */ + public function testBefore(): void { + // "a" was last, move it before "b". + $this->doPriorityChange('a', OrderBefore::class, 'b'); + $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); + // Nothing else can be asserted: by setting the same priority, the setup + // had undefined order and so the services not included in the helper call + // can be in any order. + } + + /** + * @covers ::after + */ + public function testAfter(): void { + // "c" was first, move it after "b". + $this->doPriorityChange('c', OrderAfter::class, 'b'); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); + // Nothing else can be asserted: by setting the same priority, the setup + // had undefined order and so the services not included in the helper call + // can be in any order. + } + +} diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php new file mode 100644 index 000000000000..d8fb81927be0 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php @@ -0,0 +1,148 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\Core\Hook; + +use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\OrderBefore; + +/** + * @coversDefaultClass \Drupal\Core\Hook\HookPriority + * + * @group Hook + */ +class HookPriorityTest extends HookPriorityTestBase { + + /** + * The original priorities. + * + * @var array + */ + protected array $originalPriorities = []; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + // "c" first, "b" second, "a" last. + $this->setUpContainer(TRUE); + foreach (['a', 'b', 'c'] as $key) { + $this->originalPriorities[$key] = $this->getPriority($key); + } + // The documentation does not clarify the order of arguments, let's do so + // here to make it easier to develop/debug this test. + $this->assertGreaterThan(1, 2); + // According to https://symfony.com/doc/current/event_dispatcher.html + // the higher the number, the earlier a listener is executed. Accordingly + // assert that "a" is last, "c" is first, "b" is in the middle. The + // asserts in methods can be compared to these establishing asserts. + $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('b')); + $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('c')); + // This is unnecessary, but it's easier to compare if this is explicit. + $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); + } + + /** + * @covers ::first + */ + public function testFirst(): void { + // "c" was first, make "a" the first. + $this->doPriorityChange('a', Order::First); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); + $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); + // The other two shouldn't change. + $this->assertNoChange('a'); + } + + /** + * @covers ::last + */ + public function testLast(): void { + // "c" was first, make it the last. + $this->doPriorityChange('c', Order::Last); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); + // The other two shouldn't change. + $this->assertNoChange('c'); + } + + /** + * @covers ::before + */ + public function testBefore(): void { + // "a" was last, move it before "b". + $this->doPriorityChange('a', OrderBefore::class, 'b'); + $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); + // The relation between these should not change. The actual priority + // might. + $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('c')); + $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); + } + + /** + * @covers ::after + */ + public function testAfter(): void { + // "c" was first, move it after "b". + $this->doPriorityChange('c', OrderAfter::class, 'b'); + $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); + // The relation between these should not change. The actual priority + // might. + $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('b')); + $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); + } + + /** + * @covers ::first + */ + public function testFirstNoChange(): void { + // "c" was first, making it first should be a no-op. + $this->doPriorityChange('c', Order::First); + $this->assertNoChange(); + } + + /** + * @covers ::last + */ + public function testLastNoChange(): void { + // "a" was last, making it last should be a no-op. + $this->doPriorityChange('a', Order::Last); + $this->assertNoChange(); + } + + /** + * @covers ::before + */ + public function testBeforeNoChange(): void { + // "b" is already firing before "a", this should be a no-op. + $this->doPriorityChange('b', OrderBefore::class, 'a'); + $this->assertNoChange(); + } + + /** + * @covers ::after + */ + public function testAfterNoChange(): void { + // "b' is already firing after "c", this should be a no-op. + $this->doPriorityChange('b', OrderAfter::class, 'c'); + $this->assertNoChange(); + } + + /** + * Asserts no change has happened. + * + * @param string $changed + * This one did change. Assert the rest did not change. + */ + protected function assertNoChange(string $changed = ''): void { + foreach ($this->originalPriorities as $key => $priority) { + if ($key !== $changed) { + $this->assertSame($priority, $this->getPriority($key)); + } + } + } + +} diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php new file mode 100644 index 000000000000..7e47c4ac8ec0 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php @@ -0,0 +1,84 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\Core\Hook; + +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\HookPriority; +use Drupal\Core\Hook\Order; +use Drupal\Tests\UnitTestCase; + +abstract class HookPriorityTestBase extends UnitTestCase { + + /** + * The container builder. + * + * @var \Drupal\Core\DependencyInjection\ContainerBuilder + */ + protected ContainerBuilder $container; + + /** + * Set up three service listeners, "a", "b" and "c". + * + * The service id, the class name and the method name are all the same. + * + * @param bool $different_priority + * When TRUE, "c" will fire first, "b" second and "a" last. When FALSE, + * the priority will be set to be the same and the order is undefined. + * + * @return void + */ + protected function setUpContainer(bool $different_priority): void { + $this->container = new ContainerBuilder(); + foreach (['a', 'b', 'c'] as $key => $name) { + $definition = $this->container + ->register($name, $name) + ->setAutowired(TRUE); + $definition->addTag('kernel.event_listener', [ + 'event' => 'drupal_hook.test', + 'method' => $name, + // Do not use $key itself to avoid a 0 priority which could potentially + // lead to misleading results. + 'priority' => $different_priority ? $key + 3 : 0, + ]); + } + } + + /** + * Get the priority for a service. + */ + protected function getPriority(string $name): int { + $definition = $this->container->getDefinition($name); + return $definition->getTags()['kernel.event_listener'][0]['priority']; + } + + /** + * Change priority of a class and method. + * + * @param class-string $classBeingChanged + * The class being changed, the method has the same name. + * @param Order|class-string $order + * Either a member of the Order enum or the name of a ComplexOrder class. + * @param class-string $relativeTo + * If the operation is before or after, this is the name of the class + * the operation changes relative to. + */ + protected function doPriorityChange(string $classBeingChanged, Order|string $order, string $relativeTo = ''): void { + if ($relativeTo) { + // The modules / classesAndMethods argument of the order class is + // processed in HookCollectorPass and is ignored by HookPriority, they + // are passed to HookPriority in the $other_specifiers argument. + $hook = new Hook('test', order: new $order(modules: [''])); + $other_specifiers = ["$relativeTo::$relativeTo"]; + } + else { + $hook = new Hook('test', order: $order); + $other_specifiers = NULL; + } + $hook->set(class: $classBeingChanged, module: '', method: $classBeingChanged); + (new HookPriority($this->container))->change('drupal_hook.test', $hook, $other_specifiers); + } + +} -- GitLab From 87a31975c78277513353b675b2a466d7ec2098a6 Mon Sep 17 00:00:00 2001 From: Ghost Of Drupal Past <drupal@negyesi.net> Date: Mon, 10 Feb 2025 16:28:26 +0100 Subject: [PATCH 103/268] ah it doesnt cover anything any more --- .../Hook/HookPriorityEqualPriorityTest.php | 12 ---------- .../Tests/Core/Hook/HookPriorityTest.php | 24 ------------------- .../Tests/Core/Hook/HookPriorityTestBase.php | 4 ++-- 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php index 644f53ee8c6b..985e3d55c997 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php @@ -23,9 +23,6 @@ protected function setUp(): void { $this->assertSame($this->getPriority('b'), $this->getPriority('c')); } - /** - * @covers ::first - */ public function testFirst(): void { // "c" was first, make "a" the first. $this->doPriorityChange('a', Order::First); @@ -36,9 +33,6 @@ public function testFirst(): void { // can be in any order. } - /** - * @covers ::last - */ public function testLast(): void { // "c" was first, make it the last. $this->doPriorityChange('c', Order::Last); @@ -49,9 +43,6 @@ public function testLast(): void { // can be in any order. } - /** - * @covers ::before - */ public function testBefore(): void { // "a" was last, move it before "b". $this->doPriorityChange('a', OrderBefore::class, 'b'); @@ -61,9 +52,6 @@ public function testBefore(): void { // can be in any order. } - /** - * @covers ::after - */ public function testAfter(): void { // "c" was first, move it after "b". $this->doPriorityChange('c', OrderAfter::class, 'b'); diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php index d8fb81927be0..65c8193b8f83 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php @@ -45,9 +45,6 @@ protected function setUp(): void { $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); } - /** - * @covers ::first - */ public function testFirst(): void { // "c" was first, make "a" the first. $this->doPriorityChange('a', Order::First); @@ -57,9 +54,6 @@ public function testFirst(): void { $this->assertNoChange('a'); } - /** - * @covers ::last - */ public function testLast(): void { // "c" was first, make it the last. $this->doPriorityChange('c', Order::Last); @@ -69,9 +63,6 @@ public function testLast(): void { $this->assertNoChange('c'); } - /** - * @covers ::before - */ public function testBefore(): void { // "a" was last, move it before "b". $this->doPriorityChange('a', OrderBefore::class, 'b'); @@ -82,9 +73,6 @@ public function testBefore(): void { $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); } - /** - * @covers ::after - */ public function testAfter(): void { // "c" was first, move it after "b". $this->doPriorityChange('c', OrderAfter::class, 'b'); @@ -95,36 +83,24 @@ public function testAfter(): void { $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); } - /** - * @covers ::first - */ public function testFirstNoChange(): void { // "c" was first, making it first should be a no-op. $this->doPriorityChange('c', Order::First); $this->assertNoChange(); } - /** - * @covers ::last - */ public function testLastNoChange(): void { // "a" was last, making it last should be a no-op. $this->doPriorityChange('a', Order::Last); $this->assertNoChange(); } - /** - * @covers ::before - */ public function testBeforeNoChange(): void { // "b" is already firing before "a", this should be a no-op. $this->doPriorityChange('b', OrderBefore::class, 'a'); $this->assertNoChange(); } - /** - * @covers ::after - */ public function testAfterNoChange(): void { // "b' is already firing after "c", this should be a no-op. $this->doPriorityChange('b', OrderAfter::class, 'c'); diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php index 7e47c4ac8ec0..64468cbf1ae1 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php @@ -59,8 +59,8 @@ protected function getPriority(string $name): int { * * @param class-string $classBeingChanged * The class being changed, the method has the same name. - * @param Order|class-string $order - * Either a member of the Order enum or the name of a ComplexOrder class. + * @param \Drupal\Core\Hook\Order|class-string $order + * Either a member of the Order enum or the name of a ComplexOrder class. * @param class-string $relativeTo * If the operation is before or after, this is the name of the class * the operation changes relative to. -- GitLab From 600b37763b051819d161a89f942ebf56049c7e88 Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Mon, 10 Feb 2025 15:50:53 +0000 Subject: [PATCH 104/268] class-string --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 2 +- core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 2 +- core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index a48e3a52c289..158e99fc7abf 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -133,7 +133,7 @@ public function __construct( /** * Set necessary parameters for the hook attribute. * - * @param string $class + * @param class-string $class * The class for the hook. * @param string $module * The module for the hook. diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index c7483819b828..ec2033dc8718 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -18,7 +18,7 @@ class ReOrderHook extends Hook { * * @param string $hook * The short hook name, without the 'hook_' prefix. - * @param string $class + * @param class-string $class * The class the implementation to modify is in. This allows one module to * affect the order of another module's hook. * @param string $method diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index a911cc29e5df..4e642ac2b356 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -15,7 +15,7 @@ class RemoveHook extends Hook { * * @param string $hook * The short hook name, without the 'hook_' prefix. - * @param string $class + * @param class-string $class * The class the implementation to remove is in. * @param string $method * The method name of the implementation to remove. -- GitLab From 81063c25021d4d92b9acaea8eddaa03e1199a455 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 10 Feb 2025 11:47:48 -0500 Subject: [PATCH 105/268] Comment updates --- .../Drupal/Core/Hook/HookCollectorPass.php | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index ba30aa953b10..cd968d64075a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -29,6 +29,8 @@ * * Finally, a hook_implementations_map container parameter is added. This * contains a mapping from [hook,class,method] to the module name. + * + * @internal */ class HookCollectorPass implements CompilerPassInterface { @@ -70,12 +72,20 @@ class HookCollectorPass implements CompilerPassInterface { */ public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); + // List of modules implementing hooks with the implementation details. $implementations = []; + // List of modules implementing hooks, used for + // hook_module_implementations_alter. $legacyImplementations = []; + // Groups of hooks that should be ordered together. $orderGroups = []; + // Attributes related to ordering hooks. $orderAttributes = []; + // List of modules that the hooks are defined for, keyed by class and + // method. $moduleFinder = []; - // These need to be processed after normal hooks. + // These attributes need to be processed after all hooks have been + // processed. $process_after = [ RemoveHook::class => [], ReOrderHook::class => [], @@ -92,9 +102,14 @@ public function process(ContainerBuilder $container): array { if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hook); } + // Set properties on hook class that are needed for registration. $hook->set(... compact('class', 'method', 'module')); + // Store a list of modules implementing hooks for simplifying + // registration and hook_module_implements_alter execution. $legacyImplementations[$hook->hook][$hook->module] = ''; + // Store the implementation details for registering the hook. $implementations[$hook->hook][$hook->module][$class][$hook->method] = $hook->method; + // Reverse lookup for modules implementing hooks. $moduleFinder[$class][$hook->method] = $hook->module; if ($hook->order) { $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); @@ -104,6 +119,10 @@ public function process(ContainerBuilder $container): array { } } + // Loop over all RemoveHook attributes and remove them from the maps before + // registering the hooks. This must happen after all collection, but before + // registration to ensure the hook it is removing has already been + // discovered. foreach ($process_after[RemoveHook::class] as $hook) { if ($module = ($moduleFinder[$hook->class][$hook->method] ?? '')) { unset($legacyImplementations[$hook->hook][$module]); @@ -111,6 +130,10 @@ public function process(ContainerBuilder $container): array { } } + // Loop over all ReOrderHook attributes and remove them from the maps + // before registering the hooks. This must happen after all collection, + // but before registration to ensure this ordering directive takes + // precedence. foreach ($process_after[ReOrderHook::class] as $hook) { $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); } -- GitLab From e865aeb4949219bacf0cf7dab28c91779f592b6c Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Mon, 10 Feb 2025 17:57:58 +0000 Subject: [PATCH 106/268] Remove internal --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index cd968d64075a..f7bca9dbbd33 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -29,8 +29,6 @@ * * Finally, a hook_implementations_map container parameter is added. This * contains a mapping from [hook,class,method] to the module name. - * - * @internal */ class HookCollectorPass implements CompilerPassInterface { -- GitLab From aab26f2d5168d874c1beb14a54fdd640c4349131 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 10 Feb 2025 14:19:25 -0500 Subject: [PATCH 107/268] Add internal --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 2 ++ core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php | 2 ++ core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 2 ++ core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 2 ++ core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php | 2 ++ 5 files changed, 10 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 158e99fc7abf..19032791b311 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -95,6 +95,8 @@ * the procedural hook implementations. * * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. + * + * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Hook { diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php index ee6501d7b42e..4b2726a635c2 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php @@ -19,6 +19,8 @@ * only the legacy hook implementation is executed. * * For more information, see https://www.drupal.org/node/3442349. + * + * @internal */ #[\Attribute(\Attribute::TARGET_FUNCTION)] class LegacyHook { diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index ec2033dc8718..98ce8aff5602 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -9,6 +9,8 @@ /** * Set the order of an already existing implementation. + * + * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class ReOrderHook extends Hook { diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index 4e642ac2b356..153a5c198522 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -6,6 +6,8 @@ /** * Attribute for removing an implementation. + * + * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class RemoveHook extends Hook { diff --git a/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php b/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php index 73f0ce6915bd..cc41fff51533 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php +++ b/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php @@ -9,6 +9,8 @@ * * This allows contrib and core to mark when a file has no more * procedural hooks. + * + * @internal */ #[\Attribute(\Attribute::TARGET_FUNCTION)] class StopProceduralHookScan { -- GitLab From 0174c30434f05eaf430f6b135602ec8b0b2dbae5 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 12 Feb 2025 10:28:53 -0500 Subject: [PATCH 108/268] Update remove docs --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 19032791b311..3ca1cd13bbeb 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -38,7 +38,8 @@ * * @see https://www.drupal.org/node/3493962 * - * Removing hook implementations can be done by using the remove parameter. + * Removing hook implementations can be done by using the attribute + * \Drupal\Core\Hook\Attribute/RemoveHook. * * @see https://www.drupal.org/node/3496786 * -- GitLab From 1a19d22be1995fcf0d1ad74f44c1ed5afeffca00 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sun, 16 Feb 2025 23:28:53 -0500 Subject: [PATCH 109/268] Content translation --- .../content_translation/src/Hook/ContentTranslationHooks.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php index 8c0d0e816f61..089b0a5ffba3 100644 --- a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php +++ b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php @@ -190,13 +190,8 @@ public function entityTypeAlter(array &$entity_types) : void { * @see content_translation_entity_bundle_info_alter() * @see \Drupal\content_translation\ContentTranslationManager::isEnabled() */ -<<<<<<< HEAD - #[Hook('language_content_settings_insert', order: Order::Last)] - public function languageContentSettingsInsert(ContentLanguageSettingsInterface $settings): void { -======= #[Hook('language_content_settings_insert')] public function languageContentSettingsInsert(ContentLanguageSettingsInterface $settings): void { ->>>>>>> beef39243fd (Update correct hooks) if ($settings->getThirdPartySetting('content_translation', 'enabled', FALSE)) { _content_translation_install_field_storage_definitions($settings->getTargetEntityTypeId()); } -- GitLab From 693f7ad0706b048442a12cd230f2675a30eeb9cc Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sun, 16 Feb 2025 23:33:38 -0500 Subject: [PATCH 110/268] Code sniffing --- core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php index 64468cbf1ae1..ddee181e3f62 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php @@ -27,8 +27,6 @@ abstract class HookPriorityTestBase extends UnitTestCase { * @param bool $different_priority * When TRUE, "c" will fire first, "b" second and "a" last. When FALSE, * the priority will be set to be the same and the order is undefined. - * - * @return void */ protected function setUpContainer(bool $different_priority): void { $this->container = new ContainerBuilder(); -- GitLab From d9c919dbdc15fbabb6a234997c9c2e673e2948a6 Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Mon, 24 Feb 2025 04:49:21 +0000 Subject: [PATCH 111/268] Suggestions --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 6 +++--- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 3ca1cd13bbeb..151f308b5508 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -105,9 +105,9 @@ class Hook { /** * The class the hook implementation is in. * - * @var string + * @var class-string|null */ - public string $class = ''; + public ?string $class = NULL; /** * Constructs a Hook attribute object. @@ -130,7 +130,7 @@ public function __construct( public string $hook, public string $method = '', public ?string $module = NULL, - public Order|ComplexOrder|NULL $order = NULL, + public Order|ComplexOrder|null $order = NULL, ) {} /** diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index f7bca9dbbd33..a0371d502fb4 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -73,7 +73,7 @@ public function process(ContainerBuilder $container): array { // List of modules implementing hooks with the implementation details. $implementations = []; // List of modules implementing hooks, used for - // hook_module_implementations_alter. + // hook_module_implements_alter(). $legacyImplementations = []; // Groups of hooks that should be ordered together. $orderGroups = []; @@ -150,7 +150,7 @@ public function process(ContainerBuilder $container): array { /** * Gather ordering information. * - * @param \Drupal\Core\Hook\Attributes\Hook $hook + * @param \Drupal\Core\Hook\Attribute\Hook $hook * The hook with ordering information. * @param array $orderAttributes * All attributes related to ordering. @@ -159,8 +159,8 @@ public function process(ContainerBuilder $container): array { */ protected function gatherOrderInformation(Hook $hook, array &$orderAttributes, array &$orderGroups): void { $orderAttributes[] = $hook; - if ($hook->order instanceof ComplexOrder && ($group = $hook->order->group)) { - $group[] = $hook->hook; + if ($hook->order instanceof ComplexOrder && $hook->order->group) { + $group = [...$hook->order->group, $hook->hook]; foreach ($group as $extraHook) { $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $group); } @@ -176,9 +176,9 @@ protected function gatherOrderInformation(Hook $hook, array &$orderAttributes, a * The container. * @param \Drupal\Core\Hook\HookCollectorPass $collector * The collector. - * @param array $implementations - * All implementations. - * @param array $legacyImplementations + * @param array<string, array<string, array<class-string, list<string>>>> $implementations + * All implementations, as method names keyed by hook, module and class. + * @param array<string, array<string, ''>> $legacyImplementations * Modules that implement legacy hooks. * @param array $orderGroups * Groups of hooks to reorder. -- GitLab From 0709b4e451bf0e2c711801659c57a50f08e3b840 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sun, 23 Feb 2025 23:59:36 -0500 Subject: [PATCH 112/268] spacing --- .../Drupal/Core/Hook/HookCollectorPass.php | 75 ++++++++++--------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index a0371d502fb4..e46e6200f423 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -70,18 +70,23 @@ class HookCollectorPass implements CompilerPassInterface { */ public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); + // List of modules implementing hooks with the implementation details. $implementations = []; - // List of modules implementing hooks, used for - // hook_module_implements_alter(). - $legacyImplementations = []; + + // List of hooks and modules formatted for hook_module_implements_alter(). + $legacyImplementationMap = []; + // Groups of hooks that should be ordered together. $orderGroups = []; - // Attributes related to ordering hooks. - $orderAttributes = []; + + // Hook attributes that contain ordering information. + $hookAttributesWithOrder = []; + // List of modules that the hooks are defined for, keyed by class and // method. $moduleFinder = []; + // These attributes need to be processed after all hooks have been // processed. $process_after = [ @@ -104,13 +109,13 @@ public function process(ContainerBuilder $container): array { $hook->set(... compact('class', 'method', 'module')); // Store a list of modules implementing hooks for simplifying // registration and hook_module_implements_alter execution. - $legacyImplementations[$hook->hook][$hook->module] = ''; + $legacyImplementationMap[$hook->hook][$hook->module] = ''; // Store the implementation details for registering the hook. $implementations[$hook->hook][$hook->module][$class][$hook->method] = $hook->method; // Reverse lookup for modules implementing hooks. $moduleFinder[$class][$hook->method] = $hook->module; if ($hook->order) { - $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); + $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderGroups); } } } @@ -123,7 +128,7 @@ public function process(ContainerBuilder $container): array { // discovered. foreach ($process_after[RemoveHook::class] as $hook) { if ($module = ($moduleFinder[$hook->class][$hook->method] ?? '')) { - unset($legacyImplementations[$hook->hook][$module]); + unset($legacyImplementationMap[$hook->hook][$module]); unset($implementations[$hook->hook][$module][$hook->class][$hook->method]); } } @@ -133,7 +138,7 @@ public function process(ContainerBuilder $container): array { // but before registration to ensure this ordering directive takes // precedence. foreach ($process_after[ReOrderHook::class] as $hook) { - $this->gatherOrderInformation($hook, $orderAttributes, $orderGroups); + $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderGroups); } $orderGroups = array_map('array_unique', $orderGroups); @@ -141,8 +146,8 @@ public function process(ContainerBuilder $container): array { // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $legacyImplementations, $orderGroups); - static::reOrderImplementations($container, $orderAttributes, $orderGroups, $implementations, $moduleFinder); + static::registerImplementations($container, $collector, $implementations, $legacyImplementationMap, $orderGroups); + static::reOrderImplementations($container, $hookAttributesWithOrder, $orderGroups, $implementations, $moduleFinder); } return $implementations; } @@ -152,13 +157,13 @@ public function process(ContainerBuilder $container): array { * * @param \Drupal\Core\Hook\Attribute\Hook $hook * The hook with ordering information. - * @param array $orderAttributes - * All attributes related to ordering. + * @param array $hookAttributesWithOrder + * All attributes with ordering information. * @param array $orderGroups * Groups to order by. */ - protected function gatherOrderInformation(Hook $hook, array &$orderAttributes, array &$orderGroups): void { - $orderAttributes[] = $hook; + protected function gatherOrderInformation(Hook $hook, array &$hookAttributesWithOrder, array &$orderGroups): void { + $hookAttributesWithOrder[] = $hook; if ($hook->order instanceof ComplexOrder && $hook->order->group) { $group = [...$hook->order->group, $hook->hook]; foreach ($group as $extraHook) { @@ -178,12 +183,12 @@ protected function gatherOrderInformation(Hook $hook, array &$orderAttributes, a * The collector. * @param array<string, array<string, array<class-string, list<string>>>> $implementations * All implementations, as method names keyed by hook, module and class. - * @param array<string, array<string, ''>> $legacyImplementations - * Modules that implement legacy hooks. + * @param array<string, array<string, ''>> $legacyImplementationMap + * List of hooks and modules formatted for hook_module_implements_alter(). * @param array $orderGroups * Groups of hooks to reorder. */ - protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementations, array $orderGroups): void { + protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementationMap, array $orderGroups): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); $groupIncludes = []; @@ -195,15 +200,15 @@ protected static function registerImplementations(ContainerBuilder $container, H } } - foreach ($legacyImplementations as $hook => $moduleImplements) { + foreach ($legacyImplementationMap as $hook => $moduleImplements) { $extraHooks = $orderGroups[$hook] ?? []; foreach ($extraHooks as $extraHook) { - $moduleImplements += $legacyImplementations[$extraHook] ?? []; + $moduleImplements += $legacyImplementationMap[$extraHook] ?? []; } foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } - $legacyImplementations[$hook] = $moduleImplements; + $legacyImplementationMap[$hook] = $moduleImplements; $priority = 0; foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $method_hooks) { @@ -235,8 +240,8 @@ protected static function registerImplementations(ContainerBuilder $container, H * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. - * @param array $orderAttributes - * All attributes related to ordering. + * @param array $hookAttributesWithOrder + * All attributes that contain ordering information. * @param array $orderGroups * Groups to order by. * @param array $implementations @@ -246,23 +251,23 @@ protected static function registerImplementations(ContainerBuilder $container, H * is the module. This is not necessarily the same as the module the class * is in because the implementation might be on behalf of another module. */ - protected static function reOrderImplementations(ContainerBuilder $container, array $orderAttributes, array $orderGroups, array $implementations, array $moduleFinder): void { + protected static function reOrderImplementations(ContainerBuilder $container, array $hookAttributesWithOrder, array $orderGroups, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); - foreach ($orderAttributes as $orderAttribute) { - assert($orderAttribute instanceof Hook); + foreach ($hookAttributesWithOrder as $hookAttributeWithOrder) { + assert($hookAttributeWithOrder instanceof Hook); // ::process() adds the hook serving as key to the order group so it // does not need to be added if there's a group for the hook. - $hooks = $orderGroups[$orderAttribute->hook] ?? [$orderAttribute->hook]; + $hooks = $orderGroups[$hookAttributeWithOrder->hook] ?? [$hookAttributeWithOrder->hook]; $combinedHook = implode(':', $hooks); - if ($orderAttribute->order instanceof ComplexOrder) { + if ($hookAttributeWithOrder->order instanceof ComplexOrder) { // Verify the correct structure of - // $orderAttribute->order->classesAndMethods and create specifiers + // $hookAttributeWithOrder->order->classesAndMethods and create specifiers // for HookPriority::change() while at it. - $otherSpecifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $orderAttribute->order->classesAndMethods); + $otherSpecifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $hookAttributeWithOrder->order->classesAndMethods); // Collect classes and methods for // self::registerComplexHookImplementations(). - $classesAndMethods = $orderAttribute->order->classesAndMethods; - foreach ($orderAttribute->order->modules as $modules) { + $classesAndMethods = $hookAttributeWithOrder->order->classesAndMethods; + foreach ($hookAttributeWithOrder->order->modules as $modules) { foreach ($hooks as $hook) { foreach ($implementations[$hook][$modules] ?? [] as $class => $methods) { foreach ($methods as $method) { @@ -273,19 +278,19 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar } } if (count($hooks) > 1) { - // The hook implementation in $orderAttribute and everything in + // The hook implementation in $hookAttributeWithOrder and everything in // $classesAndMethods will be ordered relative to each other as if // they were implementing a single hook. This needs to be marked on // their service definition and added to the // hook_implementations_map container parameter. - $classesAndMethods[] = [$orderAttribute->class, $orderAttribute->method]; + $classesAndMethods[] = [$hookAttributeWithOrder->class, $hookAttributeWithOrder->method]; self::registerComplexHookImplementations($container, $classesAndMethods, $moduleFinder, $combinedHook); } } else { $otherSpecifiers = NULL; } - $hookPriority->change("drupal_hook.$combinedHook", $orderAttribute, $otherSpecifiers); + $hookPriority->change("drupal_hook.$combinedHook", $hookAttributeWithOrder, $otherSpecifiers); } } -- GitLab From 0cf3fc79d995507844c1b4ee0948f4a4a1bc6dd4 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 24 Feb 2025 00:11:52 -0500 Subject: [PATCH 113/268] Remove unused line --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index e46e6200f423..e2864fa01fc4 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -208,7 +208,6 @@ protected static function registerImplementations(ContainerBuilder $container, H foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } - $legacyImplementationMap[$hook] = $moduleImplements; $priority = 0; foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $method_hooks) { -- GitLab From 3b64a4c8242e08bf6dca2b77e46fa90fd2e98ff9 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 24 Feb 2025 09:32:42 -0500 Subject: [PATCH 114/268] Class required for check and array shapes --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index e2864fa01fc4..a728c057d25c 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -103,7 +103,7 @@ public function process(ContainerBuilder $container): array { continue; } if ($class !== ProceduralCall::class) { - self::checkForProceduralOnlyHooks($hook); + self::checkForProceduralOnlyHooks($hook, $class); } // Set properties on hook class that are needed for registration. $hook->set(... compact('class', 'method', 'module')); @@ -185,7 +185,7 @@ protected function gatherOrderInformation(Hook $hook, array &$hookAttributesWith * All implementations, as method names keyed by hook, module and class. * @param array<string, array<string, ''>> $legacyImplementationMap * List of hooks and modules formatted for hook_module_implements_alter(). - * @param array $orderGroups + * @param array<string, list<string>> $orderGroups * Groups of hooks to reorder. */ protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementationMap, array $orderGroups): void { @@ -241,7 +241,7 @@ protected static function registerImplementations(ContainerBuilder $container, H * The container. * @param array $hookAttributesWithOrder * All attributes that contain ordering information. - * @param array $orderGroups + * @param array<string, list<string>> $orderGroups * Groups to order by. * @param array $implementations * Hook implementations. @@ -488,10 +488,10 @@ public function getImplementations($paths): array { * * @param \Drupal\Core\Hook\Attribute\Hook $hook * The hook to check. - * @param string $class + * @param class-string $class * The class the hook is implemented on. */ - public static function checkForProceduralOnlyHooks(Hook $hook, string $class = ''): void { + public static function checkForProceduralOnlyHooks(Hook $hook, string $class): void { $staticDenyHooks = [ 'hook_info', 'install', @@ -505,9 +505,6 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class = ' ]; if (in_array($hook->hook, $staticDenyHooks) || preg_match('/^(post_update_|preprocess_|update_\d+$)/', $hook->hook)) { - if (!$class) { - $class = $hook->class; - } throw new \LogicException("The hook $hook->hook on class $class does not support attributes and must remain procedural."); } } -- GitLab From 5bddcdd7e63cc770c9c30963ef94c922339931d6 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 24 Feb 2025 09:47:18 -0500 Subject: [PATCH 115/268] Use cached reflection --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index a728c057d25c..408b4057d6ab 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -323,7 +323,7 @@ public static function collectAllHookImplementations(array $module_filenames, ?C if ($container?->hasParameter("$module.hooks_converted")) { $skip_procedural = $container->getParameter("$module.hooks_converted"); } - $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural); + $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural, $container); } return $collector; } @@ -340,8 +340,10 @@ public static function collectAllHookImplementations(array $module_filenames, ?C * matched first. * @param bool $skip_procedural * Skip the procedural check for the current module. + * @param \Symfony\Component\DependencyInjection\ContainerBuilder|null $container + * The container. */ - protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural): void { + protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural, ?ContainerBuilder $container = NULL): void { $hook_file_cache = FileCacheFactory::get('hook_implementations'); $procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $module_preg); @@ -372,7 +374,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $class = str_replace('/', '\\', $class); $attributes = []; if (class_exists($class)) { - $reflectionClass = new \ReflectionClass($class); + $reflectionClass = $container?->getReflectionClass($class) ?? new \ReflectionClass($class); $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); $reflections[] = $reflectionClass; $attributes = self::getAttributeInstances($attributes, $reflections); -- GitLab From 6abdcf9046c13d4e6b5b032d4a078a1ce0f3c976 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 24 Feb 2025 23:13:33 -0500 Subject: [PATCH 116/268] Rename groups --- .../Drupal/Core/Extension/ModuleHandler.php | 26 +++++----- core/lib/Drupal/Core/Hook/ComplexOrder.php | 8 +-- .../Drupal/Core/Hook/HookCollectorPass.php | 50 +++++++++---------- .../ckeditor5/src/Hook/Ckeditor5Hooks.php | 2 +- ...rGroup.php => TestHookOrderExtraTypes.php} | 14 +++--- ...rGroup.php => TestHookOrderExtraTypes.php} | 6 +-- .../user_hooks_test/user_hooks_test.info.yml | 0 .../Core/Hook/HookCollectorPassTest.php | 14 +++--- 8 files changed, 60 insertions(+), 60 deletions(-) rename core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/{TestHookOrderGroup.php => TestHookOrderExtraTypes.php} (65%) rename core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/{TestHookOrderGroup.php => TestHookOrderExtraTypes.php} (83%) mode change 100644 => 100755 core/modules/user/tests/modules/user_hooks_test/user_hooks_test.info.yml diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 393bf7cf930b..be6cfc3217be 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -80,16 +80,16 @@ class ModuleHandler implements ModuleHandlerInterface { * An array keyed by hook, classname, method and the value is the module. * @param array $groupIncludes * An array of .inc files to get helpers from. - * @param array $orderGroups - * A multidimensional array of hooks that have been ordered and the group - * of hooks they have been ordered against. This is stored separately from - * $hookImplementationsMap to prevent ordering again since this group has - * already been fully ordered in HookCollectorPass. + * @param array $orderedExtraTypes + * A multidimensional array of hooks that have been ordered and the + * extra_types they have been ordered against. This is stored separately + * from $hookImplementationsMap to prevent ordering again since this set + * has already been fully ordered in HookCollectorPass. * * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider */ - public function __construct($root, array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], protected array $orderGroups = []) { + public function __construct($root, array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], protected array $orderedExtraTypes = []) { $this->root = $root; $this->moduleList = []; foreach ($module_list as $name => $module) { @@ -449,16 +449,16 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { foreach ($extra_hooks as $extra_hook) { $hook_listeners = $this->findListenersForAlter($extra_hook, $hook_listeners, $extra_modules); } - // Second, gather implementations grouped together. These are only used - // for ordering because the group might contain hooks not included in + // Second, gather implementations ordered together. These are only used + // for ordering because the set might contain hooks not included in // this alter() call. \Drupal\Core\Hook\HookPriority::change() - // registers the implementations of a grouped hook. + // registers the implementations of combined hooks. foreach (array_merge($extra_hooks, [$type . '_alter']) as $extra_hook) { - if (isset($this->orderGroups[$extra_hook])) { - $group = $this->orderGroups[$extra_hook]; - $extra_listeners = $this->findListenersForAlter(implode(':', $group)); + if (isset($this->orderedExtraTypes[$extra_hook])) { + $orderedHooks = $this->orderedExtraTypes[$extra_hook]; + $extra_listeners = $this->findListenersForAlter(implode(':', $orderedHooks)); // Remove already ordered hooks. - $extra_hooks = array_diff($extra_hooks, $group); + $extra_hooks = array_diff($extra_hooks, $orderedHooks); } } } diff --git a/core/lib/Drupal/Core/Hook/ComplexOrder.php b/core/lib/Drupal/Core/Hook/ComplexOrder.php index 2d9ae3839306..766256dca14b 100644 --- a/core/lib/Drupal/Core/Hook/ComplexOrder.php +++ b/core/lib/Drupal/Core/Hook/ComplexOrder.php @@ -37,14 +37,14 @@ * [Bar::class, 'someOtherMethod'], * ] * @endcode - * @param array $group + * @param array $extraTypes * A list of hooks to be ordered together. Ordering by attributes happens * at build time by setting up the order of the listeners of a hook * correctly. However, ModuleHandlerInterface::alter() can be called with * multiple hooks runtime. If the hook defined on this method/class * requires ordering relative to other such hooks then this parameter can - * be used to order relative to implementations of all hooks in the group. - * Include all alter hooks to be ordered against in the group even if no + * be used to order relative to implementations of all hooks in the set. + * Include all alter hooks to be ordered against in the set even if no * single alter() call includes all of them. For example, this can be used * to order a hook_form_BASE_FORM_ID_alter() implementation relative to * multiple hook_form_FORM_ID_alter() implementations as @@ -53,7 +53,7 @@ public function __construct( public array $modules = [], public array $classesAndMethods = [], - public array $group = [], + public array $extraTypes = [], ) { if (!$this->modules && !$this->classesAndMethods) { throw new \LogicException('Order must provide either modules or class-method pairs to order against.'); diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 408b4057d6ab..6df5c9e6d4e2 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -77,8 +77,8 @@ public function process(ContainerBuilder $container): array { // List of hooks and modules formatted for hook_module_implements_alter(). $legacyImplementationMap = []; - // Groups of hooks that should be ordered together. - $orderGroups = []; + // Hooks that should be ordered together when extra types are involved. + $orderExtraTypes = []; // Hook attributes that contain ordering information. $hookAttributesWithOrder = []; @@ -115,7 +115,7 @@ public function process(ContainerBuilder $container): array { // Reverse lookup for modules implementing hooks. $moduleFinder[$class][$hook->method] = $hook->module; if ($hook->order) { - $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderGroups); + $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderExtraTypes); } } } @@ -138,16 +138,16 @@ public function process(ContainerBuilder $container): array { // but before registration to ensure this ordering directive takes // precedence. foreach ($process_after[ReOrderHook::class] as $hook) { - $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderGroups); + $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderExtraTypes); } - $orderGroups = array_map('array_unique', $orderGroups); + $orderExtraTypes = array_map('array_unique', $orderExtraTypes); // @todo investigate whether this if() is needed after ModuleHandler::add() // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $legacyImplementationMap, $orderGroups); - static::reOrderImplementations($container, $hookAttributesWithOrder, $orderGroups, $implementations, $moduleFinder); + static::registerImplementations($container, $collector, $implementations, $legacyImplementationMap, $orderExtraTypes); + static::reOrderImplementations($container, $hookAttributesWithOrder, $orderExtraTypes, $implementations, $moduleFinder); } return $implementations; } @@ -159,15 +159,15 @@ public function process(ContainerBuilder $container): array { * The hook with ordering information. * @param array $hookAttributesWithOrder * All attributes with ordering information. - * @param array $orderGroups - * Groups to order by. + * @param array<string, list<string>> $orderExtraTypes + * Extra types to order together with. */ - protected function gatherOrderInformation(Hook $hook, array &$hookAttributesWithOrder, array &$orderGroups): void { + protected function gatherOrderInformation(Hook $hook, array &$hookAttributesWithOrder, array &$orderExtraTypes): void { $hookAttributesWithOrder[] = $hook; - if ($hook->order instanceof ComplexOrder && $hook->order->group) { - $group = [...$hook->order->group, $hook->hook]; - foreach ($group as $extraHook) { - $orderGroups[$extraHook] = array_merge($orderGroups[$extraHook] ?? [], $group); + if ($hook->order instanceof ComplexOrder && $hook->order->extraTypes) { + $extraTypes = [...$hook->order->extraTypes, $hook->hook]; + foreach ($extraTypes as $extraHook) { + $orderExtraTypes[$extraHook] = array_merge($orderExtraTypes[$extraHook] ?? [], $extraTypes); } } } @@ -185,10 +185,10 @@ protected function gatherOrderInformation(Hook $hook, array &$hookAttributesWith * All implementations, as method names keyed by hook, module and class. * @param array<string, array<string, ''>> $legacyImplementationMap * List of hooks and modules formatted for hook_module_implements_alter(). - * @param array<string, list<string>> $orderGroups - * Groups of hooks to reorder. + * @param array<string, list<string>> $orderExtraTypes + * Extra types to order a hook with. */ - protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementationMap, array $orderGroups): void { + protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementationMap, array $orderExtraTypes): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); $groupIncludes = []; @@ -201,7 +201,7 @@ protected static function registerImplementations(ContainerBuilder $container, H } foreach ($legacyImplementationMap as $hook => $moduleImplements) { - $extraHooks = $orderGroups[$hook] ?? []; + $extraHooks = $orderExtraTypes[$hook] ?? []; foreach ($extraHooks as $extraHook) { $moduleImplements += $legacyImplementationMap[$extraHook] ?? []; } @@ -230,7 +230,7 @@ protected static function registerImplementations(ContainerBuilder $container, H $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$orderGroups', $orderGroups); + $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); $container->setParameter('hook_implementations_map', $map ?? []); } @@ -241,8 +241,8 @@ protected static function registerImplementations(ContainerBuilder $container, H * The container. * @param array $hookAttributesWithOrder * All attributes that contain ordering information. - * @param array<string, list<string>> $orderGroups - * Groups to order by. + * @param array<string, list<string>> $orderExtraTypes + * Extra types to order together with. * @param array $implementations * Hook implementations. * @param array $moduleFinder @@ -250,13 +250,13 @@ protected static function registerImplementations(ContainerBuilder $container, H * is the module. This is not necessarily the same as the module the class * is in because the implementation might be on behalf of another module. */ - protected static function reOrderImplementations(ContainerBuilder $container, array $hookAttributesWithOrder, array $orderGroups, array $implementations, array $moduleFinder): void { + protected static function reOrderImplementations(ContainerBuilder $container, array $hookAttributesWithOrder, array $orderExtraTypes, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); foreach ($hookAttributesWithOrder as $hookAttributeWithOrder) { assert($hookAttributeWithOrder instanceof Hook); - // ::process() adds the hook serving as key to the order group so it - // does not need to be added if there's a group for the hook. - $hooks = $orderGroups[$hookAttributeWithOrder->hook] ?? [$hookAttributeWithOrder->hook]; + // ::process() adds the hook serving as key to the order extraTypes so it + // does not need to be added if there's a extraTypes for the hook. + $hooks = $orderExtraTypes[$hookAttributeWithOrder->hook] ?? [$hookAttributeWithOrder->hook]; $combinedHook = implode(':', $hooks); if ($hookAttributeWithOrder->order instanceof ComplexOrder) { // Verify the correct structure of diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index 1c6128a8c3cd..0f0b937f30b5 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -112,7 +112,7 @@ public function theme() : array { #[Hook('form_filter_format_form_alter', order: new OrderAfter( modules: ['editor', 'media'], - group: ['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'], + extraTypes: ['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'], ) )] public function formFilterFormatFormAlter(array &$form, FormStateInterface $form_state, $form_id) : void { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php similarity index 65% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php rename to core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php index 87b441159a3d..321cc19e5bf1 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php @@ -19,23 +19,23 @@ * * Each method pair tests one hook ordering permutation. */ -class TestHookOrderGroup { +class TestHookOrderExtraTypes { /** - * This pair tests OrderAfter with Group. + * This pair tests OrderAfter with ExtraTypes. */ #[Hook('custom_hook_extra_types1_alter', order: new OrderAfter( modules: ['hook_order_last_alphabetically'], - group: ['custom_hook_extra_types2_alter'], + extraTypes: ['custom_hook_extra_types2_alter'], ) )] public static function customHookExtraTypes(): void { - // This should be run after so HookOrderGroupExtraTypes should not be set. - if (!isset($GLOBALS['HookOrderGroupExtraTypes'])) { - $GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'] = 'HookOutOfOrderTestingOrderGroupsExtraTypes'; + // This should be run after so HookOrderExtraTypes should not be set. + if (!isset($GLOBALS['HookOrderExtraTypes'])) { + $GLOBALS['HookOutOfOrderTestingOrderExtraTypes'] = 'HookOutOfOrderTestingOrderExtraTypes'; } - $GLOBALS['HookRanTestingOrderGroupsExtraTypes'] = 'HookRanTestingOrderGroupsExtraTypes'; + $GLOBALS['HookRanTestingOrderExtraTypes'] = 'HookRanTestingOrderExtraTypes'; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php similarity index 83% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php rename to core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php index faac9868ce90..2ca8a0d93f42 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderGroup.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php @@ -18,14 +18,14 @@ * * Each method pair tests one hook ordering permutation. */ -class TestHookOrderGroup { +class TestHookOrderExtraTypes { /** - * This pair tests OrderAfter with Group. + * This pair tests OrderAfter with ExtraTypes. */ #[Hook('custom_hook_extra_types2_alter')] public static function customHookExtraTypes(): void { - $GLOBALS['HookOrderGroupExtraTypes'] = 'HookOrderGroupExtraTypes'; + $GLOBALS['HookOrderExtraTypes'] = 'HookOrderExtraTypes'; } } diff --git a/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.info.yml b/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.info.yml old mode 100644 new mode 100755 diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 38740b09d818..9c3e6978d3d8 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -211,13 +211,13 @@ public function testHookBefore(): void { /** * Tests hook ordering with attributes. */ - public function testHookOrderGroup(): void { + public function testHookOrderExtraTypes(): void { $module_installer = $this->container->get('module_installer'); $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookOrderGroupExtraTypes'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingOrderGroupsExtraTypes'])); + $this->assertFalse(isset($GLOBALS['HookOrderExtraTypes'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderExtraTypes'])); + $this->assertFalse(isset($GLOBALS['HookRanTestingOrderExtraTypes'])); $module_handler = $this->container->get('module_handler'); $hooks = [ 'custom_hook', @@ -226,9 +226,9 @@ public function testHookOrderGroup(): void { ]; $data = ['hi']; $module_handler->alter($hooks, $data); - $this->assertTrue(isset($GLOBALS['HookOrderGroupExtraTypes'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderGroupsExtraTypes'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingOrderGroupsExtraTypes'])); + $this->assertTrue(isset($GLOBALS['HookOrderExtraTypes'])); + $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderExtraTypes'])); + $this->assertTrue(isset($GLOBALS['HookRanTestingOrderExtraTypes'])); } /** -- GitLab From dffb11134bf6bb3d3005a20c521e87a2e9369ada Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 25 Feb 2025 15:19:57 -0500 Subject: [PATCH 117/268] HookOperation Base --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 22 +++++------ .../Core/Hook/Attribute/ReOrderHook.php | 16 ++++---- .../Drupal/Core/Hook/Attribute/RemoveHook.php | 16 ++++---- .../Drupal/Core/Hook/HookCollectorPass.php | 24 ++++++------ core/lib/Drupal/Core/Hook/HookOperation.php | 39 +++++++++++++++++++ core/lib/Drupal/Core/Hook/HookPriority.php | 5 +-- 6 files changed, 79 insertions(+), 43 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/HookOperation.php diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 151f308b5508..b9b9a9caddff 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -5,6 +5,7 @@ namespace Drupal\Core\Hook\Attribute; use Drupal\Core\Hook\ComplexOrder; +use Drupal\Core\Hook\HookOperation; use Drupal\Core\Hook\Order; /** @@ -100,14 +101,7 @@ * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class Hook { - - /** - * The class the hook implementation is in. - * - * @var class-string|null - */ - public ?string $class = NULL; +class Hook extends HookOperation { /** * Constructs a Hook attribute object. @@ -127,11 +121,13 @@ class Hook { * (optional) Set the order of the implementation. */ public function __construct( - public string $hook, - public string $method = '', - public ?string $module = NULL, - public Order|ComplexOrder|null $order = NULL, - ) {} + string $hook, + ?string $method = '', + ?string $module = NULL, + Order|ComplexOrder|null $order = NULL, + ) { + parent::__construct(... compact('hook', 'method', 'module', 'order')); + } /** * Set necessary parameters for the hook attribute. diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index 98ce8aff5602..398039ab7e7c 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -5,6 +5,7 @@ namespace Drupal\Core\Hook\Attribute; use Drupal\Core\Hook\ComplexOrder; +use Drupal\Core\Hook\HookOperation; use Drupal\Core\Hook\Order; /** @@ -13,18 +14,18 @@ * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class ReOrderHook extends Hook { +class ReOrderHook extends HookOperation { /** - * Constructs a Hook attribute object. + * Constructs a ReOrderHook object. * * @param string $hook - * The short hook name, without the 'hook_' prefix. + * The hook parameter of the #Hook being modified. * @param class-string $class - * The class the implementation to modify is in. This allows one module to - * affect the order of another module's hook. + * The class the implementation to modify is in. * @param string $method - * The method name of the implementation to modify. + * The method name of the #Hook being modified. If the hook attribute is + * on a class and does not have method set, then use __invoke. * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder $order * Set the order of the implementation. */ @@ -34,8 +35,7 @@ public function __construct( string $method, Order|ComplexOrder $order, ) { - parent::__construct($hook, method: $method, order: $order); - $this->class = $class; + parent::__construct(... compact('hook', 'method', 'class', 'order')); } } diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index 153a5c198522..0f9306a301ff 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -4,31 +4,33 @@ namespace Drupal\Core\Hook\Attribute; +use Drupal\Core\Hook\HookOperation; + /** * Attribute for removing an implementation. * * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class RemoveHook extends Hook { +class RemoveHook extends HookOperation { /** - * Constructs a Hook attribute object. + * Constructs a RemoveHook object. * * @param string $hook - * The short hook name, without the 'hook_' prefix. + * The hook parameter of the #Hook being modified. * @param class-string $class - * The class the implementation to remove is in. + * The class the implementation to modify is in. * @param string $method - * The method name of the implementation to remove. + * The method name of the #Hook being modified. If the hook attribute is + * on a class and does not have method set, then use __invoke. */ public function __construct( string $hook, string $class, string $method, ) { - parent::__construct($hook, method: $method); - $this->class = $class; + parent::__construct(... compact('hook', 'method', 'class')); } } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 6df5c9e6d4e2..566fcb7159cb 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -97,7 +97,7 @@ public function process(ContainerBuilder $container): array { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { foreach ($hooks as $hook) { - assert($hook instanceof Hook); + assert($hook instanceof HookOperation); if (isset($process_after[get_class($hook)])) { $process_after[get_class($hook)][] = $hook; continue; @@ -126,10 +126,10 @@ public function process(ContainerBuilder $container): array { // registering the hooks. This must happen after all collection, but before // registration to ensure the hook it is removing has already been // discovered. - foreach ($process_after[RemoveHook::class] as $hook) { - if ($module = ($moduleFinder[$hook->class][$hook->method] ?? '')) { - unset($legacyImplementationMap[$hook->hook][$module]); - unset($implementations[$hook->hook][$module][$hook->class][$hook->method]); + foreach ($process_after[RemoveHook::class] as $removeHook) { + if ($module = ($moduleFinder[$removeHook->class][$removeHook->method] ?? '')) { + unset($legacyImplementationMap[$removeHook->hook][$module]); + unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); } } @@ -137,8 +137,8 @@ public function process(ContainerBuilder $container): array { // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes // precedence. - foreach ($process_after[ReOrderHook::class] as $hook) { - $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderExtraTypes); + foreach ($process_after[ReOrderHook::class] as $reOrderHook) { + $this->gatherOrderInformation($reOrderHook, $hookAttributesWithOrder, $orderExtraTypes); } $orderExtraTypes = array_map('array_unique', $orderExtraTypes); @@ -155,14 +155,14 @@ public function process(ContainerBuilder $container): array { /** * Gather ordering information. * - * @param \Drupal\Core\Hook\Attribute\Hook $hook + * @param \Drupal\Core\Hook\HookOperation $hook * The hook with ordering information. * @param array $hookAttributesWithOrder * All attributes with ordering information. * @param array<string, list<string>> $orderExtraTypes * Extra types to order together with. */ - protected function gatherOrderInformation(Hook $hook, array &$hookAttributesWithOrder, array &$orderExtraTypes): void { + protected function gatherOrderInformation(HookOperation $hook, array &$hookAttributesWithOrder, array &$orderExtraTypes): void { $hookAttributesWithOrder[] = $hook; if ($hook->order instanceof ComplexOrder && $hook->order->extraTypes) { $extraTypes = [...$hook->order->extraTypes, $hook->hook]; @@ -253,7 +253,7 @@ protected static function registerImplementations(ContainerBuilder $container, H protected static function reOrderImplementations(ContainerBuilder $container, array $hookAttributesWithOrder, array $orderExtraTypes, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); foreach ($hookAttributesWithOrder as $hookAttributeWithOrder) { - assert($hookAttributeWithOrder instanceof Hook); + assert($hookAttributeWithOrder instanceof HookOperation); // ::process() adds the hook serving as key to the order extraTypes so it // does not need to be added if there's a extraTypes for the hook. $hooks = $orderExtraTypes[$hookAttributeWithOrder->hook] ?? [$hookAttributeWithOrder->hook]; @@ -488,7 +488,7 @@ public function getImplementations($paths): array { /** * Checks for hooks which can't be supported in classes. * - * @param \Drupal\Core\Hook\Attribute\Hook $hook + * @param \Drupal\Core\Hook\Hook $hook * The hook to check. * @param class-string $class * The class the hook is implemented on. @@ -524,7 +524,7 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v */ protected static function getAttributeInstances(array $attributes, array $reflections): array { foreach ($reflections as $reflection) { - if ($reflection_attributes = $reflection->getAttributes(Hook::class, \ReflectionAttribute::IS_INSTANCEOF)) { + if ($reflection_attributes = $reflection->getAttributes(HookOperation::class, \ReflectionAttribute::IS_INSTANCEOF)) { $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; $attributes[$method] = array_map(fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflection_attributes); } diff --git a/core/lib/Drupal/Core/Hook/HookOperation.php b/core/lib/Drupal/Core/Hook/HookOperation.php new file mode 100644 index 000000000000..42cfa06317c6 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/HookOperation.php @@ -0,0 +1,39 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +/** + * Base class for attributes that affect other hook implementations. + * + * @internal + */ +abstract class HookOperation { + + /** + * Constructs a HookOperation object. + * + * @param string $hook + * The hook parameter of the implementation. + * @param string $method + * The method name of the implementation. If the hook attribute is + * on a class and does not have method set, then use __invoke. + * @param class-string $class + * (optional) The class the implementation to modify is in. + * @param string|null $module + * (optional) The module this implementation is for. This allows one module + * to implement a hook on behalf of another module. Defaults to the module + * the implementation is in. + * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order + * (optional) Set the order of the implementation. + */ + public function __construct( + public string $hook, + public string $method, + public ?string $class = '', + public ?string $module = NULL, + public Order|ComplexOrder|null $order = NULL, + ) {} + +} diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 1fc4df0abd55..36f6a076b6ef 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -4,7 +4,6 @@ namespace Drupal\Core\Hook; -use Drupal\Core\Hook\Attribute\Hook; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -21,7 +20,7 @@ public function __construct(protected ContainerBuilder $container) {} * * @param string $event * Listeners to this event will be ordered. - * @param \Drupal\Core\Hook\Attribute\Hook $hook + * @param \Drupal\Core\Hook\HookOperation $hook * The hook attribute. Most of the order parameter is ignored by this * class, only $hook->order->value is used. The rest is preprocessed by * HookCollectorPass and passed in $other_specifiers. @@ -31,7 +30,7 @@ public function __construct(protected ContainerBuilder $container) {} * * @internal */ - public function change(string $event, Hook $hook, ?array $other_specifiers = NULL): void { + public function change(string $event, HookOperation $hook, ?array $other_specifiers = NULL): void { foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $tags) { foreach ($tags as $key => $tag) { if ($tag['event'] === $event) { -- GitLab From 81bcbc09af603467c7e64ee0df747ece35509f3d Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 25 Feb 2025 15:23:34 -0500 Subject: [PATCH 118/268] Rename hookAttributesWithOrder to hookOrderOperations --- .../Drupal/Core/Hook/HookCollectorPass.php | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 566fcb7159cb..1871591626a4 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -81,7 +81,7 @@ public function process(ContainerBuilder $container): array { $orderExtraTypes = []; // Hook attributes that contain ordering information. - $hookAttributesWithOrder = []; + $hookOrderOperations = []; // List of modules that the hooks are defined for, keyed by class and // method. @@ -115,7 +115,7 @@ public function process(ContainerBuilder $container): array { // Reverse lookup for modules implementing hooks. $moduleFinder[$class][$hook->method] = $hook->module; if ($hook->order) { - $this->gatherOrderInformation($hook, $hookAttributesWithOrder, $orderExtraTypes); + $this->gatherOrderInformation($hook, $hookOrderOperations, $orderExtraTypes); } } } @@ -138,7 +138,7 @@ public function process(ContainerBuilder $container): array { // but before registration to ensure this ordering directive takes // precedence. foreach ($process_after[ReOrderHook::class] as $reOrderHook) { - $this->gatherOrderInformation($reOrderHook, $hookAttributesWithOrder, $orderExtraTypes); + $this->gatherOrderInformation($reOrderHook, $hookOrderOperations, $orderExtraTypes); } $orderExtraTypes = array_map('array_unique', $orderExtraTypes); @@ -147,7 +147,7 @@ public function process(ContainerBuilder $container): array { // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { static::registerImplementations($container, $collector, $implementations, $legacyImplementationMap, $orderExtraTypes); - static::reOrderImplementations($container, $hookAttributesWithOrder, $orderExtraTypes, $implementations, $moduleFinder); + static::reOrderImplementations($container, $hookOrderOperations, $orderExtraTypes, $implementations, $moduleFinder); } return $implementations; } @@ -157,13 +157,13 @@ public function process(ContainerBuilder $container): array { * * @param \Drupal\Core\Hook\HookOperation $hook * The hook with ordering information. - * @param array $hookAttributesWithOrder + * @param array $hookOrderOperations * All attributes with ordering information. * @param array<string, list<string>> $orderExtraTypes * Extra types to order together with. */ - protected function gatherOrderInformation(HookOperation $hook, array &$hookAttributesWithOrder, array &$orderExtraTypes): void { - $hookAttributesWithOrder[] = $hook; + protected function gatherOrderInformation(HookOperation $hook, array &$hookOrderOperations, array &$orderExtraTypes): void { + $hookOrderOperations[] = $hook; if ($hook->order instanceof ComplexOrder && $hook->order->extraTypes) { $extraTypes = [...$hook->order->extraTypes, $hook->hook]; foreach ($extraTypes as $extraHook) { @@ -239,7 +239,7 @@ protected static function registerImplementations(ContainerBuilder $container, H * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. - * @param array $hookAttributesWithOrder + * @param array $hookOrderOperations * All attributes that contain ordering information. * @param array<string, list<string>> $orderExtraTypes * Extra types to order together with. @@ -250,23 +250,23 @@ protected static function registerImplementations(ContainerBuilder $container, H * is the module. This is not necessarily the same as the module the class * is in because the implementation might be on behalf of another module. */ - protected static function reOrderImplementations(ContainerBuilder $container, array $hookAttributesWithOrder, array $orderExtraTypes, array $implementations, array $moduleFinder): void { + protected static function reOrderImplementations(ContainerBuilder $container, array $hookOrderOperations, array $orderExtraTypes, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); - foreach ($hookAttributesWithOrder as $hookAttributeWithOrder) { - assert($hookAttributeWithOrder instanceof HookOperation); + foreach ($hookOrderOperations as $hookOrderOperation) { + assert($hookOrderOperation instanceof HookOperation); // ::process() adds the hook serving as key to the order extraTypes so it // does not need to be added if there's a extraTypes for the hook. - $hooks = $orderExtraTypes[$hookAttributeWithOrder->hook] ?? [$hookAttributeWithOrder->hook]; + $hooks = $orderExtraTypes[$hookOrderOperation->hook] ?? [$hookOrderOperation->hook]; $combinedHook = implode(':', $hooks); - if ($hookAttributeWithOrder->order instanceof ComplexOrder) { + if ($hookOrderOperation->order instanceof ComplexOrder) { // Verify the correct structure of - // $hookAttributeWithOrder->order->classesAndMethods and create specifiers + // $hookOrderOperation->order->classesAndMethods and create specifiers // for HookPriority::change() while at it. - $otherSpecifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $hookAttributeWithOrder->order->classesAndMethods); + $otherSpecifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $hookOrderOperation->order->classesAndMethods); // Collect classes and methods for // self::registerComplexHookImplementations(). - $classesAndMethods = $hookAttributeWithOrder->order->classesAndMethods; - foreach ($hookAttributeWithOrder->order->modules as $modules) { + $classesAndMethods = $hookOrderOperation->order->classesAndMethods; + foreach ($hookOrderOperation->order->modules as $modules) { foreach ($hooks as $hook) { foreach ($implementations[$hook][$modules] ?? [] as $class => $methods) { foreach ($methods as $method) { @@ -277,19 +277,19 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar } } if (count($hooks) > 1) { - // The hook implementation in $hookAttributeWithOrder and everything in + // The hook implementation in $hookOrderOperation and everything in // $classesAndMethods will be ordered relative to each other as if // they were implementing a single hook. This needs to be marked on // their service definition and added to the // hook_implementations_map container parameter. - $classesAndMethods[] = [$hookAttributeWithOrder->class, $hookAttributeWithOrder->method]; + $classesAndMethods[] = [$hookOrderOperation->class, $hookOrderOperation->method]; self::registerComplexHookImplementations($container, $classesAndMethods, $moduleFinder, $combinedHook); } } else { $otherSpecifiers = NULL; } - $hookPriority->change("drupal_hook.$combinedHook", $hookAttributeWithOrder, $otherSpecifiers); + $hookPriority->change("drupal_hook.$combinedHook", $hookOrderOperation, $otherSpecifiers); } } -- GitLab From 6020f0e981390f59451dd55a7938829e9860d1ab Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 25 Feb 2025 16:53:14 -0500 Subject: [PATCH 119/268] Move module to Hook --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 6 +++--- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- core/lib/Drupal/Core/Hook/HookOperation.php | 5 ----- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index b9b9a9caddff..4b77b1e5e301 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -108,7 +108,7 @@ class Hook extends HookOperation { * * @param string $hook * The short hook name, without the 'hook_' prefix. - * @param string $method + * @param ?string $method * (optional) The method name. If this attribute is on a method, this * parameter is not required. If this attribute is on a class and this * parameter is omitted, the class must have an __invoke() method, which is @@ -123,10 +123,10 @@ class Hook extends HookOperation { public function __construct( string $hook, ?string $method = '', - ?string $module = NULL, + public ?string $module = NULL, Order|ComplexOrder|null $order = NULL, ) { - parent::__construct(... compact('hook', 'method', 'module', 'order')); + parent::__construct(... compact('hook', 'method', 'order')); } /** diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 1871591626a4..87c36c7ee263 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -165,7 +165,7 @@ public function process(ContainerBuilder $container): array { protected function gatherOrderInformation(HookOperation $hook, array &$hookOrderOperations, array &$orderExtraTypes): void { $hookOrderOperations[] = $hook; if ($hook->order instanceof ComplexOrder && $hook->order->extraTypes) { - $extraTypes = [...$hook->order->extraTypes, $hook->hook]; + $extraTypes = [... $hook->order->extraTypes, $hook->hook]; foreach ($extraTypes as $extraHook) { $orderExtraTypes[$extraHook] = array_merge($orderExtraTypes[$extraHook] ?? [], $extraTypes); } diff --git a/core/lib/Drupal/Core/Hook/HookOperation.php b/core/lib/Drupal/Core/Hook/HookOperation.php index 42cfa06317c6..ce4932767a23 100644 --- a/core/lib/Drupal/Core/Hook/HookOperation.php +++ b/core/lib/Drupal/Core/Hook/HookOperation.php @@ -21,10 +21,6 @@ abstract class HookOperation { * on a class and does not have method set, then use __invoke. * @param class-string $class * (optional) The class the implementation to modify is in. - * @param string|null $module - * (optional) The module this implementation is for. This allows one module - * to implement a hook on behalf of another module. Defaults to the module - * the implementation is in. * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order * (optional) Set the order of the implementation. */ @@ -32,7 +28,6 @@ public function __construct( public string $hook, public string $method, public ?string $class = '', - public ?string $module = NULL, public Order|ComplexOrder|null $order = NULL, ) {} -- GitLab From 66b91bf98d91a01adb1c0464c6deeae54d624b4a Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 25 Feb 2025 23:07:57 -0500 Subject: [PATCH 120/268] Refactor GatherOrderInformation away --- .../Drupal/Core/Hook/HookCollectorPass.php | 35 +++++++------------ core/lib/Drupal/Core/Hook/HookOperation.php | 14 ++++---- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 87c36c7ee263..7d329a1fb907 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -115,7 +115,7 @@ public function process(ContainerBuilder $container): array { // Reverse lookup for modules implementing hooks. $moduleFinder[$class][$hook->method] = $hook->module; if ($hook->order) { - $this->gatherOrderInformation($hook, $hookOrderOperations, $orderExtraTypes); + $hookOrderOperations[] = $hook; } } } @@ -133,12 +133,21 @@ public function process(ContainerBuilder $container): array { } } - // Loop over all ReOrderHook attributes and remove them from the maps + // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes // precedence. foreach ($process_after[ReOrderHook::class] as $reOrderHook) { - $this->gatherOrderInformation($reOrderHook, $hookOrderOperations, $orderExtraTypes); + $hookOrderOperations[] = $reOrderHook; + } + + foreach ($hookOrderOperations as $hookWithOrder) { + if ($hookWithOrder->order instanceof ComplexOrder && $hookWithOrder->order->extraTypes) { + $extraTypes = [... $hookWithOrder->order->extraTypes, $hookWithOrder->hook]; + foreach ($extraTypes as $extraHook) { + $orderExtraTypes[$extraHook] = array_merge($orderExtraTypes[$extraHook] ?? [], $extraTypes); + } + } } $orderExtraTypes = array_map('array_unique', $orderExtraTypes); @@ -152,26 +161,6 @@ public function process(ContainerBuilder $container): array { return $implementations; } - /** - * Gather ordering information. - * - * @param \Drupal\Core\Hook\HookOperation $hook - * The hook with ordering information. - * @param array $hookOrderOperations - * All attributes with ordering information. - * @param array<string, list<string>> $orderExtraTypes - * Extra types to order together with. - */ - protected function gatherOrderInformation(HookOperation $hook, array &$hookOrderOperations, array &$orderExtraTypes): void { - $hookOrderOperations[] = $hook; - if ($hook->order instanceof ComplexOrder && $hook->order->extraTypes) { - $extraTypes = [... $hook->order->extraTypes, $hook->hook]; - foreach ($extraTypes as $extraHook) { - $orderExtraTypes[$extraHook] = array_merge($orderExtraTypes[$extraHook] ?? [], $extraTypes); - } - } - } - /** * Register hook implementations as event listeners. * diff --git a/core/lib/Drupal/Core/Hook/HookOperation.php b/core/lib/Drupal/Core/Hook/HookOperation.php index ce4932767a23..6794a536552a 100644 --- a/core/lib/Drupal/Core/Hook/HookOperation.php +++ b/core/lib/Drupal/Core/Hook/HookOperation.php @@ -5,7 +5,7 @@ namespace Drupal\Core\Hook; /** - * Base class for attributes that affect other hook implementations. + * Base class for attributes that affect or define hook implementations. * * @internal */ @@ -15,14 +15,16 @@ abstract class HookOperation { * Constructs a HookOperation object. * * @param string $hook - * The hook parameter of the implementation. + * The hook being implemented or modified. * @param string $method - * The method name of the implementation. If the hook attribute is - * on a class and does not have method set, then use __invoke. + * The method for the hook being implemented or modified. + * This is required when modifying existing hook implementations it is + * optional otherwise. See \Drupal\Core\Hook\Attribute\Hook for more + * information. * @param class-string $class - * (optional) The class the implementation to modify is in. + * (optional) The class of the hook being implemented or modified. * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order - * (optional) Set the order of the implementation. + * (optional) Set the order of the hook referenced. */ public function __construct( public string $hook, -- GitLab From ca14c5b2579310a0088b53d3c58ab17be4721101 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 25 Feb 2025 23:33:38 -0500 Subject: [PATCH 121/268] Refactor getAttributeInstances --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 7d329a1fb907..51a18a90ae5b 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -364,9 +364,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $attributes = []; if (class_exists($class)) { $reflectionClass = $container?->getReflectionClass($class) ?? new \ReflectionClass($class); - $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); - $reflections[] = $reflectionClass; - $attributes = self::getAttributeInstances($attributes, $reflections); + $attributes = self::getAttributeInstances($reflectionClass); $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } } @@ -503,15 +501,15 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v /** * Get attribute instances from class and method reflections. * - * @param array $attributes - * The current attributes. - * @param array $reflections - * A list of class and method reflections. + * @param \ReflectionClass $reflectionClass + * A reflected class. * * @return array * A list of Hook attribute instances. */ - protected static function getAttributeInstances(array $attributes, array $reflections): array { + protected static function getAttributeInstances(\ReflectionClass $reflectionClass): array { + $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); + $reflections[] = $reflectionClass; foreach ($reflections as $reflection) { if ($reflection_attributes = $reflection->getAttributes(HookOperation::class, \ReflectionAttribute::IS_INSTANCEOF)) { $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; -- GitLab From d7c6ec6ad86789440f9257f429a0ef002474078d Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 25 Feb 2025 23:38:26 -0500 Subject: [PATCH 122/268] Initialize attributes --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 51a18a90ae5b..efa58463775f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -508,6 +508,7 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v * A list of Hook attribute instances. */ protected static function getAttributeInstances(\ReflectionClass $reflectionClass): array { + $attributes = []; $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); $reflections[] = $reflectionClass; foreach ($reflections as $reflection) { -- GitLab From 956e42a021dd8064900b61f1cf01f95841448f59 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 26 Feb 2025 14:05:41 -0500 Subject: [PATCH 123/268] Deprecate HMIA --- core/core.api.php | 1 + core/lib/Drupal/Core/Extension/module.api.php | 10 +++++ .../Attribute/LegacyModuleImplementsAlter.php | 26 +++++++++++ .../Drupal/Core/Hook/HookCollectorPass.php | 5 ++- .../modules/common_test/common_test.module | 18 -------- ..._implements_alter_test.implementations.inc | 17 ++++++++ .../module_implements_alter_test.info.yml | 6 +++ .../module_implements_alter_test.module | 42 ++++++++++++++++++ .../modules/module_test/module_test.module | 20 --------- .../tests/src/Kernel/Common/AlterTest.php | 3 ++ .../Extension/ModuleImplementsAlterTest.php | 43 +++++++++---------- .../Core/Hook/HookCollectorPassTest.php | 19 ++++++++ .../Core/Extension/ModuleHandlerTest.php | 6 +++ ...dule_implements_alter_test_legacy.info.yml | 6 +++ ...module_implements_alter_test_legacy.module | 20 +++++++++ 15 files changed, 180 insertions(+), 62 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php create mode 100644 core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.implementations.inc create mode 100644 core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.info.yml create mode 100644 core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module create mode 100644 core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.info.yml create mode 100644 core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.module diff --git a/core/core.api.php b/core/core.api.php index 0500a3c58aec..0dbec836754e 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1657,6 +1657,7 @@ * Legacy meta hooks: * - hook_hook_info() * - hook_module_implements_alter() + * @see https://www.drupal.org/node/3496788 * * Install hooks: * - hook_install() diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index 325335615abb..ae10d273d0b3 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -94,6 +94,14 @@ function hook_hook_info(): array { /** * Alter the registry of modules implementing a hook. * + * This hook will be removed in 12.0.0. + * It has been intentionally not deprecated because custom code and contributed + * modules will still need to maintain implementations with the #[LegacyHook] + * attribute in order to support drupal versions older than 11.2.0. + * + * @link https://www.drupal.org/node/3496788 + * + * * Only procedural implementations are supported for this hook. * * This hook is invoked in \Drupal::moduleHandler()->getImplementationInfo(). @@ -115,6 +123,8 @@ function hook_hook_info(): array { * file named $module.$group.inc. * @param string $hook * The name of the module hook being implemented. + * + * @see https://www.drupal.org/node/3496788 */ function hook_module_implements_alter(&$implementations, $hook) { if ($hook == 'form_alter') { diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php new file mode 100644 index 000000000000..8b756973b38e --- /dev/null +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php @@ -0,0 +1,26 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook\Attribute; + +/** + * Defines a LegacyModuleImplementsAlter attribute object. + * + * This allows contrib and core to maintain legacy hook_module_implements_alter + * alongside the new attribute-based ordering. This means that a contrib module + * can simultaneously support Drupal 11.2 and older versions of Drupal. + * + * Marking hook_module_implements_alter as #LegacyModuleImplementsAlter will + * prevent hook_module_implements_alter from running when attribute-based + * ordering is available. + * + * On older versions of Drupal which are not aware of attribute-based ordering, + * only the legacy hook implementation is executed. + * + * For more information, see https://www.drupal.org/node/3496788. + * + * @internal + */ +#[\Attribute(\Attribute::TARGET_FUNCTION)] +class LegacyModuleImplementsAlter {} diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index efa58463775f..34b2dd7b6dd2 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -10,6 +10,7 @@ use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\LegacyHook; +use Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter; use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; @@ -380,7 +381,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, if (StaticReflectionParser::hasAttribute($attributes, StopProceduralHookScan::class)) { break; } - if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches)) { + if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches) && !StaticReflectionParser::hasAttribute($attributes, LegacyModuleImplementsAlter::class)) { $implementations[] = ['function' => $function, 'module' => $matches['module'], 'hook' => $matches['hook']]; } } @@ -437,6 +438,8 @@ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $h $this->hookInfo[] = $function; } if ($hook === 'module_implements_alter') { + $message = "$function without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788"; + @trigger_error($message, E_USER_DEPRECATED); $this->moduleImplementsAlters[] = $function; } if ($fileinfo->getExtension() !== 'module') { diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module index 4e978472953b..5d7fdc3dc6b8 100644 --- a/core/modules/system/tests/modules/common_test/common_test.module +++ b/core/modules/system/tests/modules/common_test/common_test.module @@ -41,24 +41,6 @@ function olivero_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL): void } } -/** - * Implements hook_module_implements_alter(). - * - * @see block_drupal_alter_foo_alter() - */ -function common_test_module_implements_alter(&$implementations, $hook): void { - // For - // \Drupal::moduleHandler()->alter(['drupal_alter', 'drupal_alter_foo'], ...), - // make the block module implementations run after all the other modules. Note - // that when \Drupal::moduleHandler->alter() is called with an array of types, - // the first type is considered primary and controls the module order. - if ($hook == 'drupal_alter_alter' && isset($implementations['block'])) { - $group = $implementations['block']; - unset($implementations['block']); - $implementations['block'] = $group; - } -} - /** * Implements MODULE_preprocess(). * diff --git a/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.implementations.inc b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.implementations.inc new file mode 100644 index 000000000000..7b6bac4ae95d --- /dev/null +++ b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.implementations.inc @@ -0,0 +1,17 @@ +<?php + +/** + * @file + * Include file for test module. + */ + +declare(strict_types=1); + +/** + * Implements hook_altered_test_hook(). + * + * @see module_implements_alter_test_module_implements_alter() + */ +function module_implements_alter_test_altered_test_hook(): string { + return __FUNCTION__; +} diff --git a/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.info.yml b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.info.yml new file mode 100644 index 000000000000..25995b17cd9a --- /dev/null +++ b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.info.yml @@ -0,0 +1,6 @@ +name: 'Test hook_module_implements_alter' +type: module +description: 'Support module for module system testing.' +package: Testing +version: VERSION +core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module new file mode 100644 index 000000000000..6e7452988600 --- /dev/null +++ b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module @@ -0,0 +1,42 @@ +<?php + +/** + * @file + * Module file for test module. + */ + +declare(strict_types=1); + +function test_auto_include(): void {} + +/** + * Implements hook_module_implements_alter(). + * + * @see \Drupal\system\Tests\Module\ModuleImplementsAlterTest::testModuleImplementsAlter() + * @see module_implements_alter_test_module_implements_alter() + */ +function module_implements_alter_test_module_implements_alter(&$implementations, $hook): void { + if ($hook === 'altered_test_hook') { + // Add a hook implementation, that will be found in + // module_implements_alter_test.implementation.inc. + $implementations['module_implements_alter_test'] = 'implementations'; + } + if ($hook === 'unimplemented_test_hook') { + // Add the non-existing function module_implements_alter_test_unimplemented_test_hook(). This + // should cause an exception to be thrown in + // \Drupal\Core\Extension\ModuleHandler::buildImplementationInfo('unimplemented_test_hook'). + $implementations['module_implements_alter_test'] = FALSE; + } + + // For + // \Drupal::moduleHandler()->alter(['drupal_alter', 'drupal_alter_foo'], ...), + // make the block module implementations run after all the other modules. Note + // that when \Drupal::moduleHandler->alter() is called with an array of types, + // the first type is considered primary and controls the module order. + if ($hook == 'drupal_alter_alter' && isset($implementations['block'])) { + $group = $implementations['block']; + unset($implementations['block']); + $implementations['block'] = $group; + } + +} diff --git a/core/modules/system/tests/modules/module_test/module_test.module b/core/modules/system/tests/modules/module_test/module_test.module index b7f320a35e3c..35561f09c8a0 100644 --- a/core/modules/system/tests/modules/module_test/module_test.module +++ b/core/modules/system/tests/modules/module_test/module_test.module @@ -95,23 +95,3 @@ function module_test_modules_uninstalled($modules): void { // can check that the modules were uninstalled in the correct sequence. \Drupal::state()->set('module_test.uninstall_order', $modules); } - -/** - * Implements hook_module_implements_alter(). - * - * @see module_test_altered_test_hook() - * @see \Drupal\system\Tests\Module\ModuleImplementsAlterTest::testModuleImplementsAlter() - */ -function module_test_module_implements_alter(&$implementations, $hook): void { - if ($hook === 'altered_test_hook') { - // Add a hook implementation, that will be found in - // module_test.implementation.inc. - $implementations['module_test'] = 'implementations'; - } - if ($hook === 'unimplemented_test_hook') { - // Add the non-existing function module_test_unimplemented_test_hook(). This - // should cause an exception to be thrown in - // \Drupal\Core\Extension\ModuleHandler::buildImplementationInfo('unimplemented_test_hook'). - $implementations['module_test'] = FALSE; - } -} diff --git a/core/modules/system/tests/src/Kernel/Common/AlterTest.php b/core/modules/system/tests/src/Kernel/Common/AlterTest.php index 18217579ad95..0ff2115214f4 100644 --- a/core/modules/system/tests/src/Kernel/Common/AlterTest.php +++ b/core/modules/system/tests/src/Kernel/Common/AlterTest.php @@ -19,11 +19,14 @@ class AlterTest extends KernelTestBase { protected static $modules = [ 'block', 'common_test', + 'module_implements_alter_test', 'system', ]; /** * Tests if the theme has been altered. + * + * @group legacy */ public function testDrupalAlter(): void { // This test depends on Olivero, so make sure that it is always the current diff --git a/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php b/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php index ef0022b1bfaf..2e9447c49913 100644 --- a/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php +++ b/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php @@ -10,6 +10,8 @@ * Tests hook_module_implements_alter(). * * @group Module + * + * @group legacy */ class ModuleImplementsAlterTest extends KernelTestBase { @@ -22,7 +24,7 @@ class ModuleImplementsAlterTest extends KernelTestBase { * Tests hook_module_implements_alter() adding an implementation. * * @see \Drupal\Core\Extension\ModuleHandler::buildImplementationInfo() - * @see module_test_module_implements_alter() + * @see module_implements_alter_test_module_implements_alter() */ public function testModuleImplementsAlter(): void { @@ -32,38 +34,33 @@ public function testModuleImplementsAlter(): void { $this->assertSame(\Drupal::moduleHandler(), $module_handler, 'Module handler instance is still the same.'); - // Install the module_test module. - \Drupal::service('module_installer')->install(['module_test']); + // Install the module_implements_alter_test module. + \Drupal::service('module_installer')->install(['module_implements_alter_test']); // Assert that the \Drupal::moduleHandler() instance has been replaced. $this->assertNotSame(\Drupal::moduleHandler(), $module_handler, 'The \Drupal::moduleHandler() instance has been replaced during \Drupal::moduleHandler()->install().'); - // Assert that module_test.module is now included. - $this->assertTrue(function_exists('module_test_modules_installed'), - 'The file module_test.module was successfully included.'); - - $this->assertArrayHasKey('module_test', \Drupal::moduleHandler()->getModuleList()); + // Assert that module_implements_alter_test.module is now included. + $this->assertTrue(function_exists('test_auto_include'), + 'The file module_implements_alter_test.module was successfully included.'); - $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('modules_installed', 'module_test'), - 'module_test implements hook_modules_installed().'); + $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('module_implements_alter', 'module_implements_alter_test'), + 'module_implements_alter_test implements hook_module_implements_alter().'); - $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('module_implements_alter', 'module_test'), - 'module_test implements hook_module_implements_alter().'); - - // Assert that module_test.implementations.inc is not included yet. - $this->assertFalse(function_exists('module_test_altered_test_hook'), - 'The file module_test.implementations.inc is not included yet.'); + // Assert that module_implements_alter_test.implementations.inc is not included yet. + $this->assertFalse(function_exists('module_implements_alter_test_altered_test_hook'), + 'The file module_implements_alter_test.implementations.inc is not included yet.'); // Trigger hook discovery for hook_altered_test_hook(). - // Assert that module_test_module_implements_alter(*, 'altered_test_hook') + // Assert that module_implements_alter_test_module_implements_alter(*, 'altered_test_hook') // has added an implementation. - $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('altered_test_hook', 'module_test'), - 'module_test implements hook_altered_test_hook().'); + $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('altered_test_hook', 'module_implements_alter_test'), + 'module_implements_alter_test implements hook_altered_test_hook().'); + + // Assert that module_implements_alter_test.implementations.inc was included as part of the process. + $this->assertTrue(function_exists('module_implements_alter_test_altered_test_hook'), + 'The file module_implements_alter_test.implementations.inc was included.'); - // Assert that module_test.implementations.inc was included as part of the - // process. - $this->assertTrue(function_exists('module_test_altered_test_hook'), - 'The file module_test.implementations.inc was included.'); } } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 9c3e6978d3d8..10765f3a084b 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -52,6 +52,8 @@ public function testSymlink(): void { /** * Test that ordering works. + * + * @group legacy */ public function testOrdering(): void { $container = new ContainerBuilder(); @@ -82,6 +84,23 @@ public function testOrdering(): void { $this->assertLessThan($priorities['drupal_hook.order2']['order'], $priorities['drupal_hook.order2']['module_handler_test_all2_order2']); } + /** + * Test LegacyModuleImplementsAlter. + */ + public function testLegacyModuleImplementsAlter(): void { + $container = new ContainerBuilder(); + $module_filenames = [ + 'module_implements_alter_test_legacy' => ['pathname' => "core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.info.yml"], + ]; + include_once 'core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.module'; + $container->setParameter('container.modules', $module_filenames); + $container->setDefinition('module_handler', new Definition()); + (new HookCollectorPass())->process($container); + + // This test will also fail if the deprecation notice shows up. + $this->assertFalse(isset($GLOBALS['ShouldNotRunLegacyModuleImplementsAlter'])); + } + /** * Test hooks implemented on behalf of an uninstalled module. * diff --git a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php index 3b36018806f7..7e688874bf33 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php @@ -102,8 +102,11 @@ public function testLoadModule(): void { * Tests loading all modules. * * @covers ::loadAll + * + * @group legacy */ public function testLoadAllModules(): void { + $this->expectDeprecation('module_handler_test_all1_module_implements_alter without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788'); $moduleList = [ 'module_handler_test_all1' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test_all1', 'module_handler_test_all2' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test_all2', @@ -352,8 +355,11 @@ public function testImplementsHookModuleEnabled(): void { * Tests invoke all. * * @covers ::invokeAll + * + * @group legacy */ public function testInvokeAll(): void { + $this->expectDeprecation('module_handler_test_all1_module_implements_alter without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788'); $implementations = [ 'module_handler_test_hook' => 'module_handler_test', 'module_handler_test_all1_hook' => 'module_handler_test_all1', diff --git a/core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.info.yml b/core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.info.yml new file mode 100644 index 000000000000..e286a4ffac38 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.info.yml @@ -0,0 +1,6 @@ +name: 'Module for testing LegacyModuleImplementsAlter' +type: module +description: 'Support module for module system testing.' +package: Testing +version: VERSION +core_version_requirement: '*' diff --git a/core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.module b/core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.module new file mode 100644 index 000000000000..355b56169497 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.module @@ -0,0 +1,20 @@ +<?php + +/** + * @file + * Module file for test module. + */ + +declare(strict_types=1); + +use Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter; + +/** + * Implements hook_module_implements_alter(). + * + * @see \Drupal\system\Tests\Module\ModuleImplementsAlterTest::testModuleImplementsAlter() + */ +#[LegacyModuleImplementsAlter] +function module_implements_alter_test_legacy_module_implements_alter(&$implementations, $hook): void { + $GLOBALS['ShouldNotRunLegacyModuleImplementsAlter'] = TRUE; +} -- GitLab From 0653821c76bfc66fbb2a19029878a35316e9c0fe Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 26 Feb 2025 16:32:06 -0500 Subject: [PATCH 124/268] Static --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 +- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index be6cfc3217be..8e6bbdb052e8 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -444,7 +444,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $extra_modules = FALSE; $extra_listeners = []; if (isset($extra_types)) { - $extra_hooks = array_map(fn ($x) => $x . '_alter', $extra_types); + $extra_hooks = array_map(static fn ($x) => $x . '_alter', $extra_types); // First get the listeners implementing extra hooks. foreach ($extra_hooks as $extra_hook) { $hook_listeners = $this->findListenersForAlter($extra_hook, $hook_listeners, $extra_modules); diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 34b2dd7b6dd2..aa2f382311f8 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -252,7 +252,7 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar // Verify the correct structure of // $hookOrderOperation->order->classesAndMethods and create specifiers // for HookPriority::change() while at it. - $otherSpecifiers = array_map(fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $hookOrderOperation->order->classesAndMethods); + $otherSpecifiers = array_map(static fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $hookOrderOperation->order->classesAndMethods); // Collect classes and methods for // self::registerComplexHookImplementations(). $classesAndMethods = $hookOrderOperation->order->classesAndMethods; @@ -303,7 +303,7 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar * @see https://www.drupal.org/project/drupal/issues/3481778 */ public static function collectAllHookImplementations(array $module_filenames, ?ContainerBuilder $container = NULL): static { - $modules = array_map(fn ($x) => preg_quote($x, '/'), array_keys($module_filenames)); + $modules = array_map(static fn ($x) => preg_quote($x, '/'), array_keys($module_filenames)); // Longer modules first. usort($modules, fn($a, $b) => strlen($b) - strlen($a)); $module_preg = '/^(?<function>(?<module>' . implode('|', $modules) . ')_(?!preprocess_)(?!update_\d)(?<hook>[a-zA-Z0-9_\x80-\xff]+$))/'; @@ -517,7 +517,7 @@ protected static function getAttributeInstances(\ReflectionClass $reflectionClas foreach ($reflections as $reflection) { if ($reflection_attributes = $reflection->getAttributes(HookOperation::class, \ReflectionAttribute::IS_INSTANCEOF)) { $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; - $attributes[$method] = array_map(fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflection_attributes); + $attributes[$method] = array_map(static fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflection_attributes); } } return $attributes; -- GitLab From f9cbc9c676db7c6b0954d9ce39f9ab98d4e35d42 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 26 Feb 2025 17:04:00 -0500 Subject: [PATCH 125/268] Remove compact --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 2 +- core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 2 +- core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 2 +- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 4b77b1e5e301..47af2290434c 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -126,7 +126,7 @@ public function __construct( public ?string $module = NULL, Order|ComplexOrder|null $order = NULL, ) { - parent::__construct(... compact('hook', 'method', 'order')); + parent::__construct($hook, $method, order: $order); } /** diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index 398039ab7e7c..1f88da580f4b 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -35,7 +35,7 @@ public function __construct( string $method, Order|ComplexOrder $order, ) { - parent::__construct(... compact('hook', 'method', 'class', 'order')); + parent::__construct($hook, $method, $class, $order); } } diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index 0f9306a301ff..5b4cca3b132a 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -30,7 +30,7 @@ public function __construct( string $class, string $method, ) { - parent::__construct(... compact('hook', 'method', 'class')); + parent::__construct($hook, $method, $class); } } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index aa2f382311f8..66142f5df0c5 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -107,7 +107,7 @@ public function process(ContainerBuilder $container): array { self::checkForProceduralOnlyHooks($hook, $class); } // Set properties on hook class that are needed for registration. - $hook->set(... compact('class', 'method', 'module')); + $hook->set($class, $module, $method); // Store a list of modules implementing hooks for simplifying // registration and hook_module_implements_alter execution. $legacyImplementationMap[$hook->hook][$hook->module] = ''; -- GitLab From f88a8ed4946ce07ea250afe6fc5acc84a852d037 Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Fri, 28 Feb 2025 03:44:32 +0000 Subject: [PATCH 126/268] Correct comments --- core/lib/Drupal/Core/Extension/module.api.php | 5 +++-- .../Core/Hook/Attribute/LegacyModuleImplementsAlter.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index ae10d273d0b3..de01dd91844a 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -96,8 +96,9 @@ function hook_hook_info(): array { * * This hook will be removed in 12.0.0. * It has been intentionally not deprecated because custom code and contributed - * modules will still need to maintain implementations with the #[LegacyHook] - * attribute in order to support drupal versions older than 11.2.0. + * modules will still need to maintain implementations with the + * #[LegacyModuleImplementsAlter] attribute in order to support drupal versions + * older than 11.2.0. * * @link https://www.drupal.org/node/3496788 * diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php index 8b756973b38e..0940fdc012bd 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php @@ -5,7 +5,7 @@ namespace Drupal\Core\Hook\Attribute; /** - * Defines a LegacyModuleImplementsAlter attribute object. + * Defines a LegacyModuleImplementsAlter object. * * This allows contrib and core to maintain legacy hook_module_implements_alter * alongside the new attribute-based ordering. This means that a contrib module -- GitLab From 52c99eb0939d4e5d94dafc0d4e8b9a168dcd605c Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Fri, 28 Feb 2025 14:31:43 +0000 Subject: [PATCH 127/268] Typing --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 66142f5df0c5..e50d64ddea40 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -478,7 +478,7 @@ public function getImplementations($paths): array { /** * Checks for hooks which can't be supported in classes. * - * @param \Drupal\Core\Hook\Hook $hook + * @param \Drupal\Core\Hook\Attribute\Hook $hook * The hook to check. * @param class-string $class * The class the hook is implemented on. @@ -507,8 +507,8 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v * @param \ReflectionClass $reflectionClass * A reflected class. * - * @return array - * A list of Hook attribute instances. + * @return array<string, list<\Drupal\Core\Hook\HookOperation>> + * Lists of Hook attribute instances by method name. */ protected static function getAttributeInstances(\ReflectionClass $reflectionClass): array { $attributes = []; -- GitLab From 123961bfc87bdf7a50d0d28c75325106bd31b818 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Fri, 28 Feb 2025 22:26:06 -0500 Subject: [PATCH 128/268] Rename hook to hook attribute and fix several variables --- .../Drupal/Core/Hook/HookCollectorPass.php | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index e50d64ddea40..7f06624cc649 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -90,33 +90,33 @@ public function process(ContainerBuilder $container): array { // These attributes need to be processed after all hooks have been // processed. - $process_after = [ + $processAfter = [ RemoveHook::class => [], ReOrderHook::class => [], ]; foreach (array_keys($container->getParameter('container.modules')) as $module) { foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { foreach ($methods as $method => $hooks) { - foreach ($hooks as $hook) { - assert($hook instanceof HookOperation); - if (isset($process_after[get_class($hook)])) { - $process_after[get_class($hook)][] = $hook; + foreach ($hooks as $hookAttribute) { + assert($hookAttribute instanceof HookOperation); + if (isset($processAfter[get_class($hookAttribute)])) { + $processAfter[get_class($hookAttribute)][] = $hookAttribute; continue; } if ($class !== ProceduralCall::class) { - self::checkForProceduralOnlyHooks($hook, $class); + self::checkForProceduralOnlyHooks($hookAttribute, $class); } // Set properties on hook class that are needed for registration. - $hook->set($class, $module, $method); + $hookAttribute->set($class, $module, $method); // Store a list of modules implementing hooks for simplifying // registration and hook_module_implements_alter execution. - $legacyImplementationMap[$hook->hook][$hook->module] = ''; + $legacyImplementationMap[$hookAttribute->hook][$hookAttribute->module] = ''; // Store the implementation details for registering the hook. - $implementations[$hook->hook][$hook->module][$class][$hook->method] = $hook->method; + $implementations[$hookAttribute->hook][$hookAttribute->module][$class][$hookAttribute->method] = $hookAttribute->method; // Reverse lookup for modules implementing hooks. - $moduleFinder[$class][$hook->method] = $hook->module; - if ($hook->order) { - $hookOrderOperations[] = $hook; + $moduleFinder[$class][$hookAttribute->method] = $hookAttribute->module; + if ($hookAttribute->order) { + $hookOrderOperations[] = $hookAttribute; } } } @@ -127,7 +127,7 @@ public function process(ContainerBuilder $container): array { // registering the hooks. This must happen after all collection, but before // registration to ensure the hook it is removing has already been // discovered. - foreach ($process_after[RemoveHook::class] as $removeHook) { + foreach ($processAfter[RemoveHook::class] as $removeHook) { if ($module = ($moduleFinder[$removeHook->class][$removeHook->method] ?? '')) { unset($legacyImplementationMap[$removeHook->hook][$module]); unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); @@ -138,7 +138,7 @@ public function process(ContainerBuilder $container): array { // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes // precedence. - foreach ($process_after[ReOrderHook::class] as $reOrderHook) { + foreach ($processAfter[ReOrderHook::class] as $reOrderHook) { $hookOrderOperations[] = $reOrderHook; } @@ -256,9 +256,9 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar // Collect classes and methods for // self::registerComplexHookImplementations(). $classesAndMethods = $hookOrderOperation->order->classesAndMethods; - foreach ($hookOrderOperation->order->modules as $modules) { + foreach ($hookOrderOperation->order->modules as $module) { foreach ($hooks as $hook) { - foreach ($implementations[$hook][$modules] ?? [] as $class => $methods) { + foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { $classesAndMethods[] = [$class, $method]; $otherSpecifiers[] = "$class::$method"; @@ -478,12 +478,12 @@ public function getImplementations($paths): array { /** * Checks for hooks which can't be supported in classes. * - * @param \Drupal\Core\Hook\Attribute\Hook $hook + * @param \Drupal\Core\Hook\Attribute\Hook $hookAttribute * The hook to check. * @param class-string $class * The class the hook is implemented on. */ - public static function checkForProceduralOnlyHooks(Hook $hook, string $class): void { + public static function checkForProceduralOnlyHooks(Hook $hookAttribute, string $class): void { $staticDenyHooks = [ 'hook_info', 'install', @@ -496,8 +496,8 @@ public static function checkForProceduralOnlyHooks(Hook $hook, string $class): v 'install_tasks_alter', ]; - if (in_array($hook->hook, $staticDenyHooks) || preg_match('/^(post_update_|preprocess_|update_\d+$)/', $hook->hook)) { - throw new \LogicException("The hook $hook->hook on class $class does not support attributes and must remain procedural."); + if (in_array($hookAttribute->hook, $staticDenyHooks) || preg_match('/^(post_update_|preprocess_|update_\d+$)/', $hookAttribute->hook)) { + throw new \LogicException("The hook $hookAttribute->hook on class $class does not support attributes and must remain procedural."); } } @@ -515,9 +515,9 @@ protected static function getAttributeInstances(\ReflectionClass $reflectionClas $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); $reflections[] = $reflectionClass; foreach ($reflections as $reflection) { - if ($reflection_attributes = $reflection->getAttributes(HookOperation::class, \ReflectionAttribute::IS_INSTANCEOF)) { + if ($reflectionAttributes = $reflection->getAttributes(HookOperation::class, \ReflectionAttribute::IS_INSTANCEOF)) { $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; - $attributes[$method] = array_map(static fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflection_attributes); + $attributes[$method] = array_map(static fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflectionAttributes); } } return $attributes; -- GitLab From bab479cbfde038b79abd08da11e01b41496f2a13 Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Sat, 1 Mar 2025 03:30:16 +0000 Subject: [PATCH 129/268] Typing --- core/lib/Drupal/Core/Hook/ComplexOrder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/ComplexOrder.php b/core/lib/Drupal/Core/Hook/ComplexOrder.php index 766256dca14b..bf05cc79f344 100644 --- a/core/lib/Drupal/Core/Hook/ComplexOrder.php +++ b/core/lib/Drupal/Core/Hook/ComplexOrder.php @@ -27,9 +27,9 @@ /** * Constructs a ComplexOrder object. * - * @param array $modules + * @param list<string> $modules * A list of modules. - * @param array $classesAndMethods + * @param list<array{class-string, string}> $classesAndMethods * A list of classes and methods, for example: * @code * [ -- GitLab From ea0735ae9f52f77664c679ff9718e76463bc36e7 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sun, 2 Mar 2025 11:22:53 -0500 Subject: [PATCH 130/268] Handle removeHook legacyimplentationsmap --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 7f06624cc649..889674995642 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -129,8 +129,13 @@ public function process(ContainerBuilder $container): array { // discovered. foreach ($processAfter[RemoveHook::class] as $removeHook) { if ($module = ($moduleFinder[$removeHook->class][$removeHook->method] ?? '')) { - unset($legacyImplementationMap[$removeHook->hook][$module]); unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); + // A module can implement a hook more than one time so confirm no + // more implementations before removing from the + // $legacyImplementationMap. + if (!isset($implementations[$removeHook->hook][$module])) { + unset($legacyImplementationMap[$removeHook->hook][$module]); + } } } -- GitLab From 89e1632d559790e4df9de9774c0ba6b040c1f18d Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 3 Mar 2025 09:36:30 -0500 Subject: [PATCH 131/268] Apply in scope typing and add hook check --- .../Drupal/Core/Extension/ModuleHandler.php | 17 +++++--- core/lib/Drupal/Core/Hook/ComplexOrder.php | 2 +- .../Drupal/Core/Hook/HookCollectorPass.php | 42 +++++++++++++------ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 8e6bbdb052e8..e8d658bb71b8 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -80,7 +80,7 @@ class ModuleHandler implements ModuleHandlerInterface { * An array keyed by hook, classname, method and the value is the module. * @param array $groupIncludes * An array of .inc files to get helpers from. - * @param array $orderedExtraTypes + * @param array<string, list<string>> $orderedExtraTypes * A multidimensional array of hooks that have been ordered and the * extra_types they have been ordered against. This is stored separately * from $hookImplementationsMap to prevent ordering again since this set @@ -89,7 +89,14 @@ class ModuleHandler implements ModuleHandlerInterface { * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider */ - public function __construct($root, array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], protected array $orderedExtraTypes = []) { + public function __construct( + $root, + array $module_list, + protected EventDispatcherInterface $eventDispatcher, + protected array $hookImplementationsMap, + protected array $groupIncludes = [], + protected array $orderedExtraTypes = [], + ) { $this->root = $root; $this->moduleList = []; foreach ($module_list as $name => $module) { @@ -606,12 +613,12 @@ protected function getHookListeners(string $hook): array { * * @param string $hook * The extra hook or combination hook to check for. - * @param array $hook_listeners + * @param array<string, list<callable>> $hook_listeners * Hook listeners for the current hook_alter. - * @param bool $extra_modules + * @param bool|null $extra_modules * Whether there are extra modules to order. * - * @return array + * @return array<string, list<callable>> * The hook listeners. */ public function findListenersForAlter(string $hook, array $hook_listeners = [], ?bool &$extra_modules = NULL): array { diff --git a/core/lib/Drupal/Core/Hook/ComplexOrder.php b/core/lib/Drupal/Core/Hook/ComplexOrder.php index bf05cc79f344..a44e0bd2e815 100644 --- a/core/lib/Drupal/Core/Hook/ComplexOrder.php +++ b/core/lib/Drupal/Core/Hook/ComplexOrder.php @@ -37,7 +37,7 @@ * [Bar::class, 'someOtherMethod'], * ] * @endcode - * @param array $extraTypes + * @param list<string> $extraTypes * A list of hooks to be ordered together. Ordering by attributes happens * at build time by setting up the order of the listeners of a hook * correctly. However, ModuleHandlerInterface::alter() can be called with diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 889674995642..e9a5950f92b0 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -63,11 +63,17 @@ class HookCollectorPass implements CompilerPassInterface { * A list of attributes for hook implementations. * * Keys are module, class and method. Values are Hook attributes. + * + * @var array<string, array<class-string, array<string, list<\Drupal\Core\Hook\HookOperation>>>> */ protected array $moduleHooks = []; /** * {@inheritdoc} + * + * @return array<string, array<string, array<class-string, array<string, string>>>> + * Hook implementation method names + * keyed by hook, module, class and method. */ public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); @@ -103,6 +109,11 @@ public function process(ContainerBuilder $container): array { $processAfter[get_class($hookAttribute)][] = $hookAttribute; continue; } + if (!($hookAttribute instanceof Hook)) { + // This is an unsupported attribute class, the code below would + // not work. + continue; + } if ($class !== ProceduralCall::class) { self::checkForProceduralOnlyHooks($hookAttribute, $class); } @@ -234,16 +245,18 @@ protected static function registerImplementations(ContainerBuilder $container, H * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. - * @param array $hookOrderOperations + * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations * All attributes that contain ordering information. * @param array<string, list<string>> $orderExtraTypes - * Extra types to order together with. - * @param array $implementations - * Hook implementations. - * @param array $moduleFinder - * An array keyed by the class and method of a hook implementation, value - * is the module. This is not necessarily the same as the module the class - * is in because the implementation might be on behalf of another module. + * Lists of extra hooks to order together with, keyed by hook name. + * @param array<string, array<string, array<class-string, list<string>>>> $implementations + * Hook implementations, as method names by hook, module and class. + * @param array<class-string, array<string, string>> $moduleFinder + * Lookup map to find the module for each hook implementation. + * Array keys are the class and method of the hook implementation, array + * values are module names. + * The module name can be different from the module the class is in, + * because an implementation can be on behalf of another module. */ protected static function reOrderImplementations(ContainerBuilder $container, array $hookOrderOperations, array $orderExtraTypes, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); @@ -257,7 +270,12 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar // Verify the correct structure of // $hookOrderOperation->order->classesAndMethods and create specifiers // for HookPriority::change() while at it. - $otherSpecifiers = array_map(static fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $hookOrderOperation->order->classesAndMethods); + $otherSpecifiers = array_map( + static fn ($pair) => is_array($pair) + ? $pair[0] . '::' . $pair[1] + : throw new \LogicException('classesAndMethods needs to be an array of arrays'), + $hookOrderOperation->order->classesAndMethods + ); // Collect classes and methods for // self::registerComplexHookImplementations(). $classesAndMethods = $hookOrderOperation->order->classesAndMethods; @@ -557,10 +575,10 @@ protected static function addTagToDefinition(Definition $definition, string|int * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container. - * @param array $classesAndMethods + * @param list<array{class-string, string}> $classesAndMethods * A list of class-and-method pairs. - * @param array $moduleFinder - * A module finder array, see ::reOrderImplementations() for explanation. + * @param array<class-string, array<string, string>> $moduleFinder + * Module names by class and method of hook implementations. * @param string $combinedHook * A string made form list of hooks separated by : */ -- GitLab From 3aff14bbb7b54cc13ea85351da0e9573f7c258ab Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 3 Mar 2025 09:55:25 -0500 Subject: [PATCH 132/268] Address comments on moduleFinder --- .../lib/Drupal/Core/Hook/HookCollectorPass.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index e9a5950f92b0..34cca6abb09d 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -125,7 +125,7 @@ public function process(ContainerBuilder $container): array { // Store the implementation details for registering the hook. $implementations[$hookAttribute->hook][$hookAttribute->module][$class][$hookAttribute->method] = $hookAttribute->method; // Reverse lookup for modules implementing hooks. - $moduleFinder[$class][$hookAttribute->method] = $hookAttribute->module; + $moduleFinder[$class][$hookAttribute->method][$hookAttribute->hook] = $hookAttribute->module; if ($hookAttribute->order) { $hookOrderOperations[] = $hookAttribute; } @@ -139,7 +139,7 @@ public function process(ContainerBuilder $container): array { // registration to ensure the hook it is removing has already been // discovered. foreach ($processAfter[RemoveHook::class] as $removeHook) { - if ($module = ($moduleFinder[$removeHook->class][$removeHook->method] ?? '')) { + if ($module = ($moduleFinder[$removeHook->class][$removeHook->method][$removeHook->hook] ?? '')) { unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); // A module can implement a hook more than one time so confirm no // more implementations before removing from the @@ -251,10 +251,10 @@ protected static function registerImplementations(ContainerBuilder $container, H * Lists of extra hooks to order together with, keyed by hook name. * @param array<string, array<string, array<class-string, list<string>>>> $implementations * Hook implementations, as method names by hook, module and class. - * @param array<class-string, array<string, string>> $moduleFinder + * @param array<class-string, array<array<string, string>>> $moduleFinder * Lookup map to find the module for each hook implementation. - * Array keys are the class and method of the hook implementation, array - * values are module names. + * Array keys are the class, method, and hook, array values are module + * names. * The module name can be different from the module the class is in, * because an implementation can be on behalf of another module. */ @@ -577,8 +577,9 @@ protected static function addTagToDefinition(Definition $definition, string|int * The container. * @param list<array{class-string, string}> $classesAndMethods * A list of class-and-method pairs. - * @param array<class-string, array<string, string>> $moduleFinder - * Module names by class and method of hook implementations. + * @param array<class-string, array<array<string, string>>> $moduleFinder + * Array keys are the class, method, and hook, array values are module + * names. * @param string $combinedHook * A string made form list of hooks separated by : */ @@ -588,6 +589,9 @@ protected static function registerComplexHookImplementations(ContainerBuilder $c foreach ($classesAndMethods as [$class, $method]) { // Ordering against not installed modules is possible. if (isset($moduleFinder[$class][$method])) { + if (count(array_unique($moduleFinder[$class][$method])) > 1) { + throw new \LogicException('Complex ordering can only work when all implementations on a single method are for the same module.'); + } $map[$combinedHook][$class][$method] = $moduleFinder[$class][$method]; $priority = self::addTagToDefinition($container->findDefinition($class), $combinedHook, $method, $priority); } -- GitLab From 151f75b0bc252a6824c4ff1064edbe0d7eeac81d Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 3 Mar 2025 12:12:20 -0500 Subject: [PATCH 133/268] Reset module --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 34cca6abb09d..b27ac5603acd 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -271,9 +271,7 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar // $hookOrderOperation->order->classesAndMethods and create specifiers // for HookPriority::change() while at it. $otherSpecifiers = array_map( - static fn ($pair) => is_array($pair) - ? $pair[0] . '::' . $pair[1] - : throw new \LogicException('classesAndMethods needs to be an array of arrays'), + static fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), $hookOrderOperation->order->classesAndMethods ); // Collect classes and methods for @@ -592,7 +590,7 @@ protected static function registerComplexHookImplementations(ContainerBuilder $c if (count(array_unique($moduleFinder[$class][$method])) > 1) { throw new \LogicException('Complex ordering can only work when all implementations on a single method are for the same module.'); } - $map[$combinedHook][$class][$method] = $moduleFinder[$class][$method]; + $map[$combinedHook][$class][$method] = reset($moduleFinder[$class][$method]); $priority = self::addTagToDefinition($container->findDefinition($class), $combinedHook, $method, $priority); } } -- GitLab From 4fb466d7a60af9d628a412dd66b0adc2944d1ca9 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 3 Mar 2025 12:37:20 -0500 Subject: [PATCH 134/268] PHPSTAN and CS --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 4 ++-- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 ++-- core/lib/Drupal/Core/Hook/HookOperation.php | 2 +- core/lib/Drupal/Core/Hook/Order.php | 8 ++------ .../tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php | 3 +++ 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 47af2290434c..0a4c5b5cdec9 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -108,7 +108,7 @@ class Hook extends HookOperation { * * @param string $hook * The short hook name, without the 'hook_' prefix. - * @param ?string $method + * @param string $method * (optional) The method name. If this attribute is on a method, this * parameter is not required. If this attribute is on a class and this * parameter is omitted, the class must have an __invoke() method, which is @@ -122,7 +122,7 @@ class Hook extends HookOperation { */ public function __construct( string $hook, - ?string $method = '', + string $method = '', public ?string $module = NULL, Order|ComplexOrder|null $order = NULL, ) { diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index b27ac5603acd..5047da9307c1 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -550,7 +550,7 @@ protected static function getAttributeInstances(\ReflectionClass $reflectionClas * @param \Symfony\Component\DependencyInjection\Definition $definition * The service definition. * @param string|int $hook - * The name of the hook + * The name of the hook. * @param string $method * The method. * @param int $priority @@ -579,7 +579,7 @@ protected static function addTagToDefinition(Definition $definition, string|int * Array keys are the class, method, and hook, array values are module * names. * @param string $combinedHook - * A string made form list of hooks separated by : + * A string made form list of hooks separated by :. */ protected static function registerComplexHookImplementations(ContainerBuilder $container, array $classesAndMethods, array $moduleFinder, string $combinedHook): void { $map = $container->getParameter('hook_implementations_map'); diff --git a/core/lib/Drupal/Core/Hook/HookOperation.php b/core/lib/Drupal/Core/Hook/HookOperation.php index 6794a536552a..f212588a42c6 100644 --- a/core/lib/Drupal/Core/Hook/HookOperation.php +++ b/core/lib/Drupal/Core/Hook/HookOperation.php @@ -29,7 +29,7 @@ abstract class HookOperation { public function __construct( public string $hook, public string $method, - public ?string $class = '', + public ?string $class = NULL, public Order|ComplexOrder|null $order = NULL, ) {} diff --git a/core/lib/Drupal/Core/Hook/Order.php b/core/lib/Drupal/Core/Hook/Order.php index e8c8898d2a03..f26ef257db72 100644 --- a/core/lib/Drupal/Core/Hook/Order.php +++ b/core/lib/Drupal/Core/Hook/Order.php @@ -9,14 +9,10 @@ */ enum Order: int { - /** - * This implementation should fire first. - */ + // This implementation should fire first. case First = 1; - /** - * This implementation should fire last. - */ + // This implementation should fire last. case Last = 0; } diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php index ddee181e3f62..36cbd21055f0 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php @@ -10,6 +10,9 @@ use Drupal\Core\Hook\Order; use Drupal\Tests\UnitTestCase; +/** + * Base class for testing HookPriority. + */ abstract class HookPriorityTestBase extends UnitTestCase { /** -- GitLab From 5e8ebff4d4d0b4ef6f87419ad032cbe27a2fa237 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 3 Mar 2025 23:03:54 -0500 Subject: [PATCH 135/268] Correct array shape --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 5047da9307c1..859754bd9239 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -251,7 +251,7 @@ protected static function registerImplementations(ContainerBuilder $container, H * Lists of extra hooks to order together with, keyed by hook name. * @param array<string, array<string, array<class-string, list<string>>>> $implementations * Hook implementations, as method names by hook, module and class. - * @param array<class-string, array<array<string, string>>> $moduleFinder + * @param array<class-string, array<string, array<string, string>>> $moduleFinder * Lookup map to find the module for each hook implementation. * Array keys are the class, method, and hook, array values are module * names. @@ -575,7 +575,7 @@ protected static function addTagToDefinition(Definition $definition, string|int * The container. * @param list<array{class-string, string}> $classesAndMethods * A list of class-and-method pairs. - * @param array<class-string, array<array<string, string>>> $moduleFinder + * @param array<class-string, array<string, array<string, string>>> $moduleFinder * Array keys are the class, method, and hook, array values are module * names. * @param string $combinedHook -- GitLab From 6ebb40c008d38c918fa55091585f22e6e1d1b0a5 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Mon, 3 Mar 2025 23:25:40 -0500 Subject: [PATCH 136/268] Add comments --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 859754bd9239..b11e8e73e3ef 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -197,6 +197,10 @@ public function process(ContainerBuilder $container): array { protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementationMap, array $orderExtraTypes): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); + + // Gather includes for each hook_hook_info group. + // We store this in $groupIncludes so moduleHandler can ensure the files + // are included runtime when the hooks are invoked. $groupIncludes = []; foreach ($collector->hookInfo as $function) { foreach ($function() as $hook => $info) { @@ -206,14 +210,19 @@ protected static function registerImplementations(ContainerBuilder $container, H } } + // Register all implementations. foreach ($legacyImplementationMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; foreach ($extraHooks as $extraHook) { $moduleImplements += $legacyImplementationMap[$extraHook] ?? []; } + // Process all hook_module_implements_alter() for build time ordering. foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } + // Start at 0 for the first hook. We decrease the priority after each + // hook that is registered. Symfony priorities run higher priorities + // first. $priority = 0; foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $method_hooks) { @@ -234,6 +243,7 @@ protected static function registerImplementations(ContainerBuilder $container, H } } + // Pass necessary parameters to moduleHandler. $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); -- GitLab From 1bda6bf91fd04b4d2a6fc47244b749e643c3cf73 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 4 Mar 2025 23:53:56 -0500 Subject: [PATCH 137/268] Modify removal check --- .../Drupal/Core/Hook/HookCollectorPass.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index b11e8e73e3ef..1b7fbf8381ee 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -140,11 +140,24 @@ public function process(ContainerBuilder $container): array { // discovered. foreach ($processAfter[RemoveHook::class] as $removeHook) { if ($module = ($moduleFinder[$removeHook->class][$removeHook->method][$removeHook->hook] ?? '')) { + // Remove the hook implementation for the defined class, method, and + // hook. unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); - // A module can implement a hook more than one time so confirm no - // more implementations before removing from the + // Check if the given module has implemented the hook on more than one + // method for the class. + // Hook removal is very rare so it is more efficient to do the check + // here. + if ($implementations[$removeHook->hook][$module][$removeHook->class]) { + // Remove the class from the implementation map if the hook has no + // more implementations. + unset($implementations[$removeHook->hook][$module][$removeHook->class]); + } + // A module can implement a hook on more than one class so we confirm + // there are no more implementations before removing from the // $legacyImplementationMap. - if (!isset($implementations[$removeHook->hook][$module])) { + // We do not need to clear further empty arrays since we handle this + // state before registering the hooks. + if (empty($implementations[$removeHook->hook][$module])) { unset($legacyImplementationMap[$removeHook->hook][$module]); } } -- GitLab From d87f0cdb9d4634c22ff0dbb54a7c6afb4e49a28a Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Wed, 5 Mar 2025 16:48:11 -0500 Subject: [PATCH 138/268] Add comment to moduleImplements loop --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 1b7fbf8381ee..69d60d119400 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -226,6 +226,8 @@ protected static function registerImplementations(ContainerBuilder $container, H // Register all implementations. foreach ($legacyImplementationMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; + // Add implementations to the array we pass to HMIA when the definition + // specifies that they should be ordered together. foreach ($extraHooks as $extraHook) { $moduleImplements += $legacyImplementationMap[$extraHook] ?? []; } @@ -239,7 +241,7 @@ protected static function registerImplementations(ContainerBuilder $container, H $priority = 0; foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $method_hooks) { - if ($container->has($class)) { + if ($container->hasDefinition($class)) { $definition = $container->findDefinition($class); } else { -- GitLab From bd6585c4b70b1177b3092b17391a848116168893 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Mon, 10 Mar 2025 11:19:03 -0400 Subject: [PATCH 139/268] Reorder check that implementation exists --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 13b6e1ea07a5..f6cc24ac197d 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -166,9 +166,13 @@ public function process(ContainerBuilder $container): array { // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes - // precedence. + // precedence. We only add this directive if the implementation exists. foreach ($processAfter[ReOrderHook::class] as $reOrderHook) { - $hookOrderOperations[] = $reOrderHook; + if ($module = ($moduleFinder[$reOrderHook->class][$reOrderHook->method][$reOrderHook->hook] ?? '')) { + if (isset($implementations[$reOrderHook->hook][$module][$reOrderHook->class][$reOrderHook->method])) { + $hookOrderOperations[] = $reOrderHook; + } + } } foreach ($hookOrderOperations as $hookWithOrder) { -- GitLab From 596960ed51cde1e64e2cc9a92e0eccdecb67fc9a Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Wed, 5 Mar 2025 22:35:38 +0000 Subject: [PATCH 140/268] Spelling --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 69d60d119400..13b6e1ea07a5 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -226,8 +226,8 @@ protected static function registerImplementations(ContainerBuilder $container, H // Register all implementations. foreach ($legacyImplementationMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; - // Add implementations to the array we pass to HMIA when the definition - // specifies that they should be ordered together. + // Add implementations to the array we pass to legacy ordering + // when the definition specifies that they should be ordered together. foreach ($extraHooks as $extraHook) { $moduleImplements += $legacyImplementationMap[$extraHook] ?? []; } -- GitLab From 78717963bf56b2a454795b8356fc380678941589 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 8 Mar 2025 19:00:58 +0100 Subject: [PATCH 141/268] Add HookOrderTest kernel test. --- .../HookOrder/hk_a_test/hk_a_test.info.yml | 6 + .../HookOrder/hk_a_test/hk_a_test.module | 26 ++ .../src/Hook/ModuleImplementsAlter.php | 47 +++ .../HookOrder/hk_b_test/hk_b_test.info.yml | 6 + .../HookOrder/hk_b_test/hk_b_test.module | 17 + .../HookOrder/hk_c_test/hk_c_test.info.yml | 6 + .../HookOrder/hk_c_test/hk_c_test.module | 17 + .../KernelTests/Core/Hook/HookOrderTest.php | 313 ++++++++++++++++++ 8 files changed, 438 insertions(+) create mode 100644 core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.info.yml create mode 100644 core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module create mode 100644 core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/ModuleImplementsAlter.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.info.yml create mode 100644 core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module create mode 100644 core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.info.yml create mode 100644 core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module create mode 100644 core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.info.yml b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.info.yml new file mode 100644 index 000000000000..a85c8b96fee7 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.info.yml @@ -0,0 +1,6 @@ +name: Hook A Test +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module new file mode 100644 index 000000000000..43e42c8868fc --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module @@ -0,0 +1,26 @@ +<?php + +declare(strict_types=1); + +use Drupal\hk_a_test\Hook\ModuleImplementsAlter; + +/** + * Implements hook_procedural_alter(). + */ +function hk_a_test_procedural_alter(array &$calls): void { + $calls[] = __FUNCTION__; +} + +/** + * Implements hook_procedural_subtype_alter(). + */ +function hk_a_test_procedural_subtype_alter(array &$calls): void { + $calls[] = __FUNCTION__; +} + +/** + * Implements hook_module_implements_alter(). + */ +function hk_a_test_module_implements_alter(array &$implementations, string $hook): void { + ModuleImplementsAlter::call($implementations, $hook); +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/ModuleImplementsAlter.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/ModuleImplementsAlter.php new file mode 100644 index 000000000000..082950e7aa5a --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/ModuleImplementsAlter.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_a_test\Hook; + +/** + * Contains a replaceable callback for hook_module_implements_alter(). + */ +class ModuleImplementsAlter { + + /** + * Callback for hook_module_implements_alter(). + * + * @var ?\Closure + * @phpstan-var (\Closure(array<string, string|false>&, string): void)|null + */ + private static ?\Closure $callback = NULL; + + /** + * Sets a callback for hook_module_implements_alter(). + * + * @param ?\Closure $callback + * Callback to set, or NULL to unset. + * + * @phpstan-param (\Closure(array<string, string|false>&, string): void)|null $callback + */ + public static function set(?\Closure $callback): void { + self::$callback = $callback; + } + + /** + * Invokes the registered callback. + * + * @param array<string, string|false> $implementations + * The implementations, as "group" by module name. + * @param string $hook + * The hook. + */ + public static function call(array &$implementations, string $hook): void { + if (self::$callback === NULL) { + return; + } + (self::$callback)($implementations, $hook); + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.info.yml b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.info.yml new file mode 100644 index 000000000000..c286f011bc7b --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.info.yml @@ -0,0 +1,6 @@ +name: Hook B Test +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module new file mode 100644 index 000000000000..cc1598917ea3 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +/** + * Implements hook_procedural_alter(). + */ +function hk_b_test_procedural_alter(array &$calls): void { + $calls[] = __FUNCTION__; +} + +/** + * Implements hook_procedural_subtype_alter(). + */ +function hk_b_test_procedural_subtype_alter(array &$calls): void { + $calls[] = __FUNCTION__; +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.info.yml b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.info.yml new file mode 100644 index 000000000000..02f09d78faea --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.info.yml @@ -0,0 +1,6 @@ +name: Hook C Test +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module new file mode 100644 index 000000000000..c02938af3b0d --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +/** + * Implements hook_procedural_alter(). + */ +function hk_c_test_procedural_alter(array &$calls): void { + $calls[] = __FUNCTION__; +} + +/** + * Implements hook_procedural_subtype_alter(). + */ +function hk_c_test_procedural_subtype_alter(array &$calls): void { + $calls[] = __FUNCTION__; +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php new file mode 100644 index 000000000000..dc6b615f26f6 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -0,0 +1,313 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\KernelTests\Core\Hook; + +use Drupal\hk_a_test\Hook\ModuleImplementsAlter; +use Drupal\KernelTests\KernelTestBase; + +/** + * @group Hook + */ +class HookOrderTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'hk_a_test', + 'hk_b_test', + 'hk_c_test', + ]; + + public function testProceduralAlterOrder(): void { + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_b_test_procedural_alter', + 'hk_c_test_procedural_alter', + ], 'procedural'); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_subtype_alter', + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_subtype_alter', + ], 'procedural_subtype'); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_a_test_procedural_subtype_alter', + 'hk_b_test_procedural_alter', + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_alter', + 'hk_c_test_procedural_subtype_alter', + ], ['procedural', 'procedural_subtype']); + + // Test with module B moved to the end. + ModuleImplementsAlter::set( + function (array &$implementations, string $hook): void { + if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { + return; + } + $this->assertSameCallList([ + 'hk_a_test', + 'hk_b_test', + 'hk_c_test', + ], array_keys($implementations)); + // Move B to the end, no matter which hook. + $group = $implementations['hk_b_test']; + unset($implementations['hk_b_test']); + $implementations['hk_b_test'] = $group; + }, + ); + \Drupal::service('kernel')->rebuildContainer(); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_c_test_procedural_alter', + // The implementation of B has been moved. + 'hk_b_test_procedural_alter', + ], 'procedural', prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + // The implementation of B is back to its original position. + 'hk_b_test_procedural_alter', + 'hk_c_test_procedural_alter', + ], ['x', 'procedural']); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_subtype_alter', + 'hk_c_test_procedural_subtype_alter', + // The implementation of B has been moved. + 'hk_b_test_procedural_subtype_alter', + ], 'procedural_subtype', prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_subtype_alter', + // The implementation of B is back to its original position. + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_subtype_alter', + ], ['x', 'procedural_subtype'], prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_a_test_procedural_subtype_alter', + 'hk_c_test_procedural_alter', + 'hk_c_test_procedural_subtype_alter', + 'hk_b_test_procedural_alter', + 'hk_b_test_procedural_subtype_alter', + ], ['procedural', 'procedural_subtype'], prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_a_test_procedural_subtype_alter', + // The implementations of B are back to their original position. + 'hk_b_test_procedural_alter', + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_alter', + 'hk_c_test_procedural_subtype_alter', + ], ['x', 'procedural', 'procedural_subtype'], prepend_unknown_type: FALSE); + + // Test with module B moved to the end for the main hook. + ModuleImplementsAlter::set( + function (array &$implementations, string $hook): void { + if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { + return; + } + $this->assertSameCallList([ + 'hk_a_test', + 'hk_b_test', + 'hk_c_test', + ], array_keys($implementations)); + if ($hook !== 'procedural_alter') { + return; + } + // Move B to the end, no matter which hook. + $group = $implementations['hk_b_test']; + unset($implementations['hk_b_test']); + $implementations['hk_b_test'] = $group; + }, + ); + \Drupal::service('kernel')->rebuildContainer(); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_c_test_procedural_alter', + // The main hook has B last. + 'hk_b_test_procedural_alter', + ], 'procedural', prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + // The main hook has B in its original position. + 'hk_b_test_procedural_alter', + 'hk_c_test_procedural_alter', + ], ['x', 'procedural'], prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_subtype_alter', + // The subtype hook has B in its original place. + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_subtype_alter', + ], 'procedural_subtype'); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_a_test_procedural_subtype_alter', + 'hk_c_test_procedural_alter', + 'hk_c_test_procedural_subtype_alter', + // The mixed hook has B last. + 'hk_b_test_procedural_alter', + 'hk_b_test_procedural_subtype_alter', + ], ['procedural', 'procedural_subtype'], prepend_unknown_type: FALSE); + + // Test with module B moved to the end for the subtype hook. + ModuleImplementsAlter::set( + function (array &$implementations, string $hook): void { + if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { + return; + } + $this->assertSameCallList([ + 'hk_a_test', + 'hk_b_test', + 'hk_c_test', + ], array_keys($implementations)); + if ($hook !== 'procedural_subtype_alter') { + return; + } + // Move B to the end, no matter which hook. + $group = $implementations['hk_b_test']; + unset($implementations['hk_b_test']); + $implementations['hk_b_test'] = $group; + }, + ); + \Drupal::service('kernel')->rebuildContainer(); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + // The main hook has B in its original place. + 'hk_b_test_procedural_alter', + 'hk_c_test_procedural_alter', + ], 'procedural'); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_subtype_alter', + 'hk_c_test_procedural_subtype_alter', + // The subtype hook has B last. + 'hk_b_test_procedural_subtype_alter', + ], 'procedural_subtype', prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + 'hk_a_test_procedural_alter', + 'hk_a_test_procedural_subtype_alter', + // The mixed hook has B in its original place. + 'hk_b_test_procedural_alter', + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_alter', + 'hk_c_test_procedural_subtype_alter', + ], ['procedural', 'procedural_subtype']); + } + + /** + * Asserts the call order from an alter call. + * + * Also asserts additional $type argument values that are meant to produce the + * same result. + * + * @param list<string> $expected + * Expected call list, as strings from __METHOD__ or __FUNCTION__. + * @param string|list<string> $type + * First argument to pass to ->alter(). + * @param list<string|list<string>>|null $equivalent_types + * Alternative values for $type that are meant to produce the same result. + * If NULL, alternative value will be generated by appending and/ + * prepending "unknown" types, that is, types with no implementations. + * @param bool $prepend_unknown_type + * If TRUE, or if NULL and $equivalent_types is NULL, additional equivalent + * types will be generated where an unknown type is prepended. + */ + protected function assertAlterCallOrder(array $expected, string|array $type, array|null $equivalent_types = NULL, ?bool $prepend_unknown_type = NULL): void { + if ($equivalent_types === NULL) { + $equivalent_type = []; + foreach ((array) $type as $i => $type_i) { + $equivalent_type[] = $type_i; + $equivalent_type[] = 'x_' . $i; + } + $equivalent_types = [$equivalent_type]; + $prepend_unknown_type ??= TRUE; + } + if ($prepend_unknown_type ?? FALSE) { + foreach ([(array) $type, ...$equivalent_types] as $type_i) { + $equivalent_types[] = ['x', ...$type_i]; + } + } + foreach ([$type, ...$equivalent_types] as $i => $type_i) { + $this->assertSameCallList( + $expected, + $this->alter($type_i), + $i . ': ' . json_encode($type_i), + ); + } + } + + /** + * Invokes ModuleHandler->alter() and returns the altered array. + * + * @param string|list<string> $type + * Alter type or list of alter types. + * + * @return array + * The altered array. + */ + protected function alter(string|array $type): array { + $data = []; + \Drupal::moduleHandler()->alter($type, $data); + return $data; + } + + /** + * Asserts that two lists of call strings are the same. + * + * It is meant for strings produced with __FUNCTION__ or __METHOD__. + * + * The assertion fails exactly when a regular ->assertSame() would fail, but + * it provides a more useful output on failure. + * + * @param list<string> $expected + * Expected list of strings. + * @param list<string> $actual + * Actual list of strings. + * @param string $message + * Message to pass to ->assertSame(). + */ + protected function assertSameCallList(array $expected, array $actual, string $message = ''): void { + // Format without the numeric array keys, but in a way that can be easily + // copied into the test. + $format = function (array $strings): string { + if (!$strings) { + return '[]'; + } + $parts = array_map( + function (string $call_string) { + if (preg_match('@^(\w+\\\\)*(\w+)::(\w+)@', $call_string, $matches)) { + [,, $class_shortname, $method] = $matches; + return $class_shortname . '::class . ' . var_export('::' . $method, TRUE); + } + return var_export($call_string, TRUE); + }, + $strings, + ); + return "[\n " . implode(",\n ", $parts) . ",\n]"; + }; + $this->assertSame( + $format($expected), + $format($actual), + $message, + ); + // Finally, assert that array keys and the full class names are really the + // same, in a way that provides useful output on failure. + $this->assertSame($expected, $actual, $message); + } + +} -- GitLab From faca28ea7cb21aee7fbe3ac0c65df35414f61b8e Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 2 Mar 2025 18:02:58 +0100 Subject: [PATCH 142/268] Remove empty arrays from $implementations. --- .../Drupal/Core/Hook/HookCollectorPass.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 79397f271b4d..57a2ec3e1727 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -161,6 +161,7 @@ public function process(ContainerBuilder $container): array { } } } + $implementations = static::removeEmptyArraysRecursively($implementations); // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, @@ -621,4 +622,32 @@ protected static function registerComplexHookImplementations(ContainerBuilder $c $container->setParameter('hook_implementations_map', $map); } + /** + * Removes empty sub-arrays recursively, preserving keys. + * + * This is different from NestedArray::filter() or regular array_filter(): + * - It only removes empty arrays, not other empty-ish values. + * - It reliably removes empty parent arrays, after all children have been + * removed. See https://www.drupal.org/project/drupal/issues/3381640. + * + * @param array $array + * Array which may or may not contain other arrays as values. + * + * @return array + * Filtered array, with empty arrays removed. + * + * @see \Drupal\Component\Utility\NestedArray::filter() + */ + protected static function removeEmptyArraysRecursively(array $array): array { + foreach ($array as $key => $value) { + if (is_array($value)) { + $value = self::removeEmptyArraysRecursively($value); + if ($value === []) { + unset($array[$key]); + } + } + } + return $array; + } + } -- GitLab From 59f0b8ed331458238c1d64b5aa3822db5f2afeaa Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 2 Mar 2025 17:39:50 +0100 Subject: [PATCH 143/268] Calculate $legacyImplementationsMap after the hook removal. --- .../Drupal/Core/Hook/HookCollectorPass.php | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 57a2ec3e1727..864e356f5b70 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -80,9 +80,6 @@ public function process(ContainerBuilder $container): array { // List of modules implementing hooks with the implementation details. $implementations = []; - // List of hooks and modules formatted for hook_module_implements_alter(). - $legacyImplementationMap = []; - // Hooks that should be ordered together when extra types are involved. $orderExtraTypes = []; @@ -118,9 +115,6 @@ public function process(ContainerBuilder $container): array { } // Set properties on hook class that are needed for registration. $hookAttribute->set($class, $module, $method); - // Store a list of modules implementing hooks for simplifying - // registration and hook_module_implements_alter execution. - $legacyImplementationMap[$hookAttribute->hook][$hookAttribute->module] = ''; // Store the implementation details for registering the hook. $implementations[$hookAttribute->hook][$hookAttribute->module][$class][$hookAttribute->method] = $hookAttribute->method; // Reverse lookup for modules implementing hooks. @@ -142,27 +136,18 @@ public function process(ContainerBuilder $container): array { // Remove the hook implementation for the defined class, method, and // hook. unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); - // Check if the given module has implemented the hook on more than one - // method for the class. - // Hook removal is very rare so it is more efficient to do the check - // here. - if ($implementations[$removeHook->hook][$module][$removeHook->class]) { - // Remove the class from the implementation map if the hook has no - // more implementations. - unset($implementations[$removeHook->hook][$module][$removeHook->class]); - } - // A module can implement a hook on more than one class so we confirm - // there are no more implementations before removing from the - // $legacyImplementationMap. - // We do not need to clear further empty arrays since we handle this - // state before registering the hooks. - if (empty($implementations[$removeHook->hook][$module])) { - unset($legacyImplementationMap[$removeHook->hook][$module]); - } } } $implementations = static::removeEmptyArraysRecursively($implementations); + // List of hooks and modules formatted for hook_module_implements_alter(). + $legacyImplementationMap = []; + foreach ($implementations as $hook => $implementationsByModule) { + foreach ($implementationsByModule as $module => $implementationsByClass) { + $legacyImplementationMap[$hook][$module] = ''; + } + } + // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes -- GitLab From a64fc9c70ad001010490aa9c2b5dd63bc14868d0 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 2 Mar 2025 21:59:55 +0100 Subject: [PATCH 144/268] Calculate $legacyImplementationMap later, in registerImplementations(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 864e356f5b70..451323b98830 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -140,14 +140,6 @@ public function process(ContainerBuilder $container): array { } $implementations = static::removeEmptyArraysRecursively($implementations); - // List of hooks and modules formatted for hook_module_implements_alter(). - $legacyImplementationMap = []; - foreach ($implementations as $hook => $implementationsByModule) { - foreach ($implementationsByModule as $module => $implementationsByClass) { - $legacyImplementationMap[$hook][$module] = ''; - } - } - // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes @@ -170,7 +162,7 @@ public function process(ContainerBuilder $container): array { // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $legacyImplementationMap, $orderExtraTypes); + static::registerImplementations($container, $collector, $implementations, $orderExtraTypes); static::reOrderImplementations($container, $hookOrderOperations, $orderExtraTypes, $implementations, $moduleFinder); } return $implementations; @@ -187,12 +179,10 @@ public function process(ContainerBuilder $container): array { * The collector. * @param array<string, array<string, array<class-string, list<string>>>> $implementations * All implementations, as method names keyed by hook, module and class. - * @param array<string, array<string, ''>> $legacyImplementationMap - * List of hooks and modules formatted for hook_module_implements_alter(). * @param array<string, list<string>> $orderExtraTypes * Extra types to order a hook with. */ - protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $legacyImplementationMap, array $orderExtraTypes): void { + protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $orderExtraTypes): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); @@ -208,7 +198,14 @@ protected static function registerImplementations(ContainerBuilder $container, H } } - // Register all implementations. + // List of hooks and modules formatted for hook_module_implements_alter(). + $legacyImplementationMap = []; + foreach ($implementations as $hook => $implementationsByModule) { + foreach ($implementationsByModule as $module => $implementationsByClass) { + $legacyImplementationMap[$hook][$module] = ''; + } + } + foreach ($legacyImplementationMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; // Add implementations to the array we pass to legacy ordering -- GitLab From f4a4c8b83862ba23da5317e7e593adba421970ba Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 2 Mar 2025 12:42:34 +0100 Subject: [PATCH 145/268] Rename some variables in foreach(). --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 451323b98830..6cbe92256089 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -97,9 +97,9 @@ public function process(ContainerBuilder $container): array { ReOrderHook::class => [], ]; foreach (array_keys($container->getParameter('container.modules')) as $module) { - foreach ($collector->moduleHooks[$module] ?? [] as $class => $methods) { - foreach ($methods as $method => $hooks) { - foreach ($hooks as $hookAttribute) { + foreach ($collector->moduleHooks[$module] ?? [] as $class => $attributesByMethod) { + foreach ($attributesByMethod as $method => $attributes) { + foreach ($attributes as $hookAttribute) { assert($hookAttribute instanceof HookOperation); if (isset($processAfter[get_class($hookAttribute)])) { $processAfter[get_class($hookAttribute)][] = $hookAttribute; -- GitLab From e97c9f5827d3bfd49a0f2216e67dc37ff337ef67 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 3 Mar 2025 00:40:38 +0100 Subject: [PATCH 146/268] Change how empty arrays are removed. --- .../Drupal/Core/Hook/HookCollectorPass.php | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 6cbe92256089..03c470b53de3 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -136,9 +136,21 @@ public function process(ContainerBuilder $container): array { // Remove the hook implementation for the defined class, method, and // hook. unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); + // Remove empty arrays, after the entry was removed. + // Hook implementation removal is expected to be rare, therefore it will + // be faster to do it like this than cleaning the entire tree + // afterwards. + if (empty($implementations[$removeHook->hook][$module][$removeHook->class])) { + unset($implementations[$removeHook->hook][$module][$removeHook->class]); + if (empty($implementations[$removeHook->hook][$module])) { + unset($implementations[$removeHook->hook][$module]); + if (empty($implementations[$removeHook->hook])) { + unset($implementations[$removeHook->hook]); + } + } + } } } - $implementations = static::removeEmptyArraysRecursively($implementations); // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, @@ -604,32 +616,4 @@ protected static function registerComplexHookImplementations(ContainerBuilder $c $container->setParameter('hook_implementations_map', $map); } - /** - * Removes empty sub-arrays recursively, preserving keys. - * - * This is different from NestedArray::filter() or regular array_filter(): - * - It only removes empty arrays, not other empty-ish values. - * - It reliably removes empty parent arrays, after all children have been - * removed. See https://www.drupal.org/project/drupal/issues/3381640. - * - * @param array $array - * Array which may or may not contain other arrays as values. - * - * @return array - * Filtered array, with empty arrays removed. - * - * @see \Drupal\Component\Utility\NestedArray::filter() - */ - protected static function removeEmptyArraysRecursively(array $array): array { - foreach ($array as $key => $value) { - if (is_array($value)) { - $value = self::removeEmptyArraysRecursively($value); - if ($value === []) { - unset($array[$key]); - } - } - } - return $array; - } - } -- GitLab From 9d9698fdfa0f608594676ad4a1862a3f2d7f1123 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 3 Mar 2025 00:42:28 +0100 Subject: [PATCH 147/268] Rename $legacyImplementationMap -> $moduleImplementsMap. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 03c470b53de3..4078619c3aea 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -211,19 +211,19 @@ protected static function registerImplementations(ContainerBuilder $container, H } // List of hooks and modules formatted for hook_module_implements_alter(). - $legacyImplementationMap = []; + $moduleImplementsMap = []; foreach ($implementations as $hook => $implementationsByModule) { foreach ($implementationsByModule as $module => $implementationsByClass) { - $legacyImplementationMap[$hook][$module] = ''; + $moduleImplementsMap[$hook][$module] = ''; } } - foreach ($legacyImplementationMap as $hook => $moduleImplements) { + foreach ($moduleImplementsMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; // Add implementations to the array we pass to legacy ordering // when the definition specifies that they should be ordered together. foreach ($extraHooks as $extraHook) { - $moduleImplements += $legacyImplementationMap[$extraHook] ?? []; + $moduleImplements += $moduleImplementsMap[$extraHook] ?? []; } // Process all hook_module_implements_alter() for build time ordering. foreach ($collector->moduleImplementsAlters as $alter) { -- GitLab From 9fb327f03f5c287ece69566f789448f98076d1e5 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 3 Mar 2025 00:44:59 +0100 Subject: [PATCH 148/268] Rename $method_hooks -> $methods. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 4078619c3aea..c0595249580c 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -234,7 +234,7 @@ protected static function registerImplementations(ContainerBuilder $container, H // first. $priority = 0; foreach ($moduleImplements as $module => $v) { - foreach ($implementations[$hook][$module] ?? [] as $class => $method_hooks) { + foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { if ($container->hasDefinition($class)) { $definition = $container->findDefinition($class); } @@ -243,7 +243,7 @@ protected static function registerImplementations(ContainerBuilder $container, H ->register($class, $class) ->setAutowired(TRUE); } - foreach ($method_hooks as $method) { + foreach ($methods as $method) { $map[$hook][$class][$method] = $module; $priority = self::addTagToDefinition($definition, $hook, $method, $priority); } -- GitLab From 940b97c343b6ed6a0ec0c257ffbbdb8d1cb91f7f Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 00:04:04 +0100 Subject: [PATCH 149/268] OPTIONAL: Doc return comment back to one line. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 13b6e1ea07a5..3d2ac093de85 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -72,8 +72,7 @@ class HookCollectorPass implements CompilerPassInterface { * {@inheritdoc} * * @return array<string, array<string, array<class-string, array<string, string>>>> - * Hook implementation method names - * keyed by hook, module, class and method. + * Hook implementation method names keyed by hook, module, class and method. */ public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); -- GitLab From f460323d196ed8022daac412fee83a2d6b082863 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 3 Mar 2025 01:08:54 +0100 Subject: [PATCH 150/268] Calculate $tagsInfoByClass before writing to the container. --- .../Drupal/Core/Hook/HookCollectorPass.php | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index c0595249580c..a1c63688cbc9 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -218,6 +218,7 @@ protected static function registerImplementations(ContainerBuilder $container, H } } + $tagsInfoByClass = []; foreach ($moduleImplementsMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; // Add implementations to the array we pass to legacy ordering @@ -235,23 +236,34 @@ protected static function registerImplementations(ContainerBuilder $container, H $priority = 0; foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { - if ($container->hasDefinition($class)) { - $definition = $container->findDefinition($class); - } - else { - $definition = $container - ->register($class, $class) - ->setAutowired(TRUE); - } foreach ($methods as $method) { + $tagsInfoByClass[$class][] = [ + 'event' => "drupal_hook.$hook", + 'method' => $method, + 'priority' => $priority, + ]; + --$priority; $map[$hook][$class][$method] = $module; - $priority = self::addTagToDefinition($definition, $hook, $method, $priority); } } unset($implementations[$hook][$module]); } } + foreach ($tagsInfoByClass as $class => $tagsInfo) { + if ($container->hasDefinition($class)) { + $definition = $container->findDefinition($class); + } + else { + $definition = $container + ->register($class, $class) + ->setAutowired(TRUE); + } + foreach ($tagsInfo as $tag_info) { + $definition->addTag('kernel.event_listener', $tag_info); + } + } + // Pass necessary parameters to moduleHandler. $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); -- GitLab From 180598525ebda5734190453f08bb30fc9e548157 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 00:32:35 +0100 Subject: [PATCH 151/268] Drop parens around instanceof. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 3d2ac093de85..c62f12cb5d57 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -108,7 +108,7 @@ public function process(ContainerBuilder $container): array { $processAfter[get_class($hookAttribute)][] = $hookAttribute; continue; } - if (!($hookAttribute instanceof Hook)) { + if (!$hookAttribute instanceof Hook) { // This is an unsupported attribute class, the code below would // not work. continue; -- GitLab From 75de5b65997f99708b2d50eb2d136de10369b0be Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 3 Mar 2025 02:09:49 +0100 Subject: [PATCH 152/268] Add line breaks, convert arrow function to regular closure. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index a1c63688cbc9..37cd038f2ac4 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -302,7 +302,12 @@ protected static function reOrderImplementations(ContainerBuilder $container, ar // $hookOrderOperation->order->classesAndMethods and create specifiers // for HookPriority::change() while at it. $otherSpecifiers = array_map( - static fn ($pair) => is_array($pair) ? $pair[0] . '::' . $pair[1] : throw new \LogicException('classesAndMethods needs to be an array of arrays'), + static function ($pair) { + if (!is_array($pair)) { + return throw new \LogicException('classesAndMethods needs to be an array of arrays'); + } + return $pair[0] . '::' . $pair[1]; + }, $hookOrderOperation->order->classesAndMethods ); // Collect classes and methods for -- GitLab From ff236b8f7c076a4dadfd22663a767e28fbc7bdc0 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 2 Mar 2025 01:05:27 +0100 Subject: [PATCH 153/268] Add param type for getImplementations(). --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index c62f12cb5d57..89bff8fdaf14 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -514,7 +514,7 @@ public function loadAllIncludes(): void { * * @internal */ - public function getImplementations($paths): array { + public function getImplementations(array $paths): array { $container = new ContainerBuilder(); $container->setParameter('container.modules', $paths); return $this->process($container); -- GitLab From 21b85fe23743586ca8954d655d5d59290557cf07 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 12:34:38 +0100 Subject: [PATCH 154/268] Collapse reOrderImplementations() into registerImplementations(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 37cd038f2ac4..86a005467fe1 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -174,8 +174,7 @@ public function process(ContainerBuilder $container): array { // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $orderExtraTypes); - static::reOrderImplementations($container, $hookOrderOperations, $orderExtraTypes, $implementations, $moduleFinder); + static::registerImplementations($container, $collector, $implementations, $orderExtraTypes, $hookOrderOperations, $moduleFinder); } return $implementations; } @@ -193,8 +192,23 @@ public function process(ContainerBuilder $container): array { * All implementations, as method names keyed by hook, module and class. * @param array<string, list<string>> $orderExtraTypes * Extra types to order a hook with. + * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations + * All attributes that contain ordering information. + * @param array<class-string, array<string, array<string, string>>> $moduleFinder + * Lookup map to find the module for each hook implementation. + * Array keys are the class, method, and hook, array values are module + * names. + * The module name can be different from the module the class is in, + * because an implementation can be on behalf of another module. */ - protected static function registerImplementations(ContainerBuilder $container, HookCollectorPass $collector, array $implementations, array $orderExtraTypes): void { + protected static function registerImplementations( + ContainerBuilder $container, + HookCollectorPass $collector, + array $implementations, + array $orderExtraTypes, + array $hookOrderOperations, + array $moduleFinder, + ): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); @@ -269,27 +283,7 @@ protected static function registerImplementations(ContainerBuilder $container, H $definition->setArgument('$groupIncludes', $groupIncludes); $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); $container->setParameter('hook_implementations_map', $map ?? []); - } - /** - * Reorder hook implementations specifying an order. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * The container. - * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations - * All attributes that contain ordering information. - * @param array<string, list<string>> $orderExtraTypes - * Lists of extra hooks to order together with, keyed by hook name. - * @param array<string, array<string, array<class-string, list<string>>>> $implementations - * Hook implementations, as method names by hook, module and class. - * @param array<class-string, array<string, array<string, string>>> $moduleFinder - * Lookup map to find the module for each hook implementation. - * Array keys are the class, method, and hook, array values are module - * names. - * The module name can be different from the module the class is in, - * because an implementation can be on behalf of another module. - */ - protected static function reOrderImplementations(ContainerBuilder $container, array $hookOrderOperations, array $orderExtraTypes, array $implementations, array $moduleFinder): void { $hookPriority = new HookPriority($container); foreach ($hookOrderOperations as $hookOrderOperation) { assert($hookOrderOperation instanceof HookOperation); -- GitLab From ff1970ee7ac28931cb33e8e12c7f93ff47856bb4 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 9 Mar 2025 15:52:37 +0100 Subject: [PATCH 155/268] Add tests to cover the new order attributes. --- .../HookOrder/hk_a_test/hk_a_test.module | 7 + .../hk_a_test/src/Hook/AAlterHooks.php | 22 ++++ .../hk_a_test/src/Hook/AFormAlterHooks.php | 42 ++++++ .../HookOrder/hk_a_test/src/Hook/AHooks.php | 33 +++++ .../HookOrder/hk_b_test/hk_b_test.module | 7 + .../hk_b_test/src/Hook/BAlterHooks.php | 22 ++++ .../hk_b_test/src/Hook/BFormAlterHooks.php | 21 +++ .../HookOrder/hk_b_test/src/Hook/BHooks.php | 16 +++ .../HookOrder/hk_c_test/hk_c_test.module | 7 + .../hk_c_test/src/Hook/CAlterHooks.php | 21 +++ .../hk_c_test/src/Hook/CFormAlterHooks.php | 21 +++ .../HookOrder/hk_c_test/src/Hook/CHooks.php | 42 ++++++ .../HookOrder/hk_d_test/hk_d_test.info.yml | 6 + .../HookOrder/hk_d_test/hk_d_test.module | 10 ++ .../hk_d_test/src/Hook/DAlterHooks.php | 21 +++ .../HookOrder/hk_d_test/src/Hook/DHooks.php | 22 ++++ .../KernelTests/Core/Hook/HookOrderTest.php | 124 ++++++++++++++++++ 17 files changed, 444 insertions(+) create mode 100644 core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.info.yml create mode 100644 core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module create mode 100644 core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php create mode 100644 core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module index 43e42c8868fc..c02369025037 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module @@ -4,6 +4,13 @@ use Drupal\hk_a_test\Hook\ModuleImplementsAlter; +/** + * Implements hook_testhook(). + */ +function hk_a_test_testhook(): string { + return __FUNCTION__; +} + /** * Implements hook_procedural_alter(). */ diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php new file mode 100644 index 000000000000..e548a6d48b77 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php @@ -0,0 +1,22 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_a_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\OrderAfter; + +class AAlterHooks { + + #[Hook('test_alter', order: new OrderAfter(modules: ['hk_c_test']))] + public function testAlterAfterC(array &$calls): void { + $calls[] = __METHOD__; + } + + #[Hook('test_subtype_alter')] + public function testSubtypeAlter(array &$calls): void { + $calls[] = __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php new file mode 100644 index 000000000000..d4e08fe1f953 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php @@ -0,0 +1,42 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_a_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\OrderAfter; + +class AFormAlterHooks { + + #[Hook('form_alter')] + public function formAlter(array &$form): void { + $form['#calls'][] = __METHOD__; + } + + #[Hook('form_myform_alter')] + public function myFormAlter(array &$form): void { + $form['#calls'][] = __METHOD__; + } + + #[Hook('form_alter', order: new OrderAfter(modules: ['hk_b_test']))] + public function formAlterAfterB(array &$form): void { + $form['#calls'][] = __METHOD__; + } + + #[Hook('form_myform_alter', order: new OrderAfter(modules: ['hk_b_test']))] + public function myFormAlterAfterB(array &$form): void { + $form['#calls'][] = __METHOD__; + } + + #[Hook('form_alter', order: new OrderAfter(modules: ['hk_b_test'], extraTypes: ['form_myform_alter']))] + public function formAlterAfterBExtra(array &$form): void { + $form['#calls'][] = __METHOD__; + } + + #[Hook('form_myform_alter', order: new OrderAfter(modules: ['hk_b_test'], extraTypes: ['form_alter']))] + public function myFormAlterAfterBExtra(array &$form): void { + $form['#calls'][] = __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php new file mode 100644 index 000000000000..009a940e0a85 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_a_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\OrderAfter; + +class AHooks { + + #[Hook('testhook')] + public function testHook(): string { + return __METHOD__; + } + + #[Hook('testhook', order: Order::First)] + public function testHookFirst(): string { + return __METHOD__; + } + + #[Hook('testhook', order: Order::Last)] + public function testHookLast(): string { + return __METHOD__; + } + + #[Hook('testhook', order: new OrderAfter(modules: ['hk_b_test']))] + public function testHookAfterB(): string { + return __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module index cc1598917ea3..ef2949485daa 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module @@ -2,6 +2,13 @@ declare(strict_types=1); +/** + * Implements hook_testhook(). + */ +function hk_b_test_testhook(): string { + return __FUNCTION__; +} + /** * Implements hook_procedural_alter(). */ diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php new file mode 100644 index 000000000000..790a71b3adc9 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php @@ -0,0 +1,22 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_b_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\OrderAfter; + +class BAlterHooks { + + #[Hook('test_alter', order: new OrderAfter(modules: ['hk_c_test'], extraTypes: ['test_subtype_alter']))] + public function testAlterAfterCExtra(array &$calls): void { + $calls[] = __METHOD__; + } + + #[Hook('test_subtype_alter')] + public function testSubtypeAlter(array &$calls): void { + $calls[] = __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php new file mode 100644 index 000000000000..4c02511647de --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_b_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +class BFormAlterHooks { + + #[Hook('form_alter')] + public function formAlter(array &$form): void { + $form['#calls'][] = __METHOD__; + } + + #[Hook('form_myform_alter')] + public function myFormAlter(array &$form): void { + $form['#calls'][] = __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php new file mode 100644 index 000000000000..3b3953c5a2cb --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php @@ -0,0 +1,16 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_b_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +class BHooks { + + #[Hook('testhook')] + public function testHook(): string { + return __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module index c02938af3b0d..2856847c2305 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module @@ -2,6 +2,13 @@ declare(strict_types=1); +/** + * Implements hook_testhook(). + */ +function hk_c_test_testhook(): string { + return __FUNCTION__; +} + /** * Implements hook_procedural_alter(). */ diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php new file mode 100644 index 000000000000..447814a9fbbd --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_c_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +class CAlterHooks { + + #[Hook('test_alter')] + public function testAlter(array &$calls): void { + $calls[] = __METHOD__; + } + + #[Hook('test_subtype_alter')] + public function testSubtypeAlter(array &$calls): void { + $calls[] = __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php new file mode 100644 index 000000000000..ce4c276c6d11 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_c_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +class CFormAlterHooks { + + #[Hook('form_alter')] + public function formAlter(array &$form): void { + $form['#calls'][] = __METHOD__; + } + + #[Hook('form_myform_alter')] + public function myFormAlter(array &$form): void { + $form['#calls'][] = __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php new file mode 100644 index 000000000000..99d924e5d343 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php @@ -0,0 +1,42 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_c_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Order; + +class CHooks { + + #[Hook('testhook')] + public function testHook(): string { + return __METHOD__; + } + + #[Hook('testhook', order: Order::First)] + public function testHookFirst(): string { + return __METHOD__; + } + + /** + * This implementation is reordered from elsewhere. + * + * @see \Drupal\hk_d_test\Hook\DHooks + */ + #[Hook('testhook')] + public function testHookReOrderFirst(): string { + return __METHOD__; + } + + /** + * This implementation is removed from elsewhere. + * + * @see \Drupal\hk_d_test\Hook\DHooks + */ + #[Hook('testhook')] + public function testHookRemoved(): string { + return __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.info.yml b/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.info.yml new file mode 100644 index 000000000000..1d8c2e86d63d --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.info.yml @@ -0,0 +1,6 @@ +name: Hook D Test +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module b/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module new file mode 100644 index 000000000000..f3e9c5b48091 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module @@ -0,0 +1,10 @@ +<?php + +declare(strict_types=1); + +/** + * Implements hook_testhook(). + */ +function hk_d_test_testhook(): string { + return __FUNCTION__; +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php new file mode 100644 index 000000000000..817e1ba60f28 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_d_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; + +class DAlterHooks { + + #[Hook('test_alter')] + public function testAlter(array &$calls): void { + $calls[] = __METHOD__; + } + + #[Hook('test_subtype_alter')] + public function testSubtypeAlter(array &$calls): void { + $calls[] = __METHOD__; + } + +} diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php new file mode 100644 index 000000000000..a6b413c3f2a7 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php @@ -0,0 +1,22 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_d_test\Hook; + +use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\RemoveHook; +use Drupal\Core\Hook\Attribute\ReOrderHook; +use Drupal\Core\Hook\Order; +use Drupal\hk_c_test\Hook\CHooks; + +#[ReOrderHook('testhook', CHooks::class, 'testHookReOrderFirst', Order::First)] +#[RemoveHook('testhook', CHooks::class, 'testHookRemoved')] +class DHooks { + + #[Hook('testhook')] + public function testHook(): string { + return __METHOD__; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index dc6b615f26f6..9b87ae92b00a 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -4,7 +4,17 @@ namespace Drupal\KernelTests\Core\Hook; +use Drupal\hk_a_test\Hook\AAlterHooks; +use Drupal\hk_a_test\Hook\AFormAlterHooks; +use Drupal\hk_a_test\Hook\AHooks; use Drupal\hk_a_test\Hook\ModuleImplementsAlter; +use Drupal\hk_b_test\Hook\BAlterHooks; +use Drupal\hk_b_test\Hook\BFormAlterHooks; +use Drupal\hk_b_test\Hook\BHooks; +use Drupal\hk_c_test\Hook\CAlterHooks; +use Drupal\hk_c_test\Hook\CFormAlterHooks; +use Drupal\hk_d_test\Hook\DAlterHooks; +use Drupal\hk_d_test\Hook\DHooks; use Drupal\KernelTests\KernelTestBase; /** @@ -19,6 +29,7 @@ class HookOrderTest extends KernelTestBase { 'hk_a_test', 'hk_b_test', 'hk_c_test', + 'hk_d_test', ]; public function testProceduralAlterOrder(): void { @@ -209,6 +220,119 @@ function (array &$implementations, string $hook): void { ], ['procedural', 'procedural_subtype']); } + public function testAlterOrder(): void { + $this->assertAlterCallOrder([ + AAlterHooks::class . '::testAlterAfterC', + BAlterHooks::class . '::testAlterAfterCExtra', + CAlterHooks::class . '::testAlter', + DAlterHooks::class . '::testAlter', + ], 'test', [ + ['test', 'test_unknown'], + ['test_unknown', 'test', 'test_unknown'], + ['test_unknown_1', 'test_unknown_2', 'test'], + ]); + + // Prepending a type changes the order and loses the implementation for 'D'. + // @todo This is probably bad. + $this->assertAlterCallOrder([ + CAlterHooks::class . '::testAlter', + AAlterHooks::class . '::testAlterAfterC', + BAlterHooks::class . '::testAlterAfterCExtra', + ], ['test_other_unknown', 'test'], []); + + $this->assertAlterCallOrder([ + AAlterHooks::class . '::testSubtypeAlter', + BAlterHooks::class . '::testSubtypeAlter', + CAlterHooks::class . '::testSubtypeAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], 'test_subtype', prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + // The implementation from 'D' is gone. + CAlterHooks::class . '::testAlter', + CAlterHooks::class . '::testSubtypeAlter', + AAlterHooks::class . '::testAlterAfterC', + AAlterHooks::class . '::testSubtypeAlter', + BAlterHooks::class . '::testAlterAfterCExtra', + BAlterHooks::class . '::testSubtypeAlter', + ], ['test', 'test_subtype'], []); + + $this->assertAlterCallOrder([ + AAlterHooks::class . '::testAlterAfterC', + AAlterHooks::class . '::testSubtypeAlter', + BAlterHooks::class . '::testAlterAfterCExtra', + BAlterHooks::class . '::testSubtypeAlter', + CAlterHooks::class . '::testAlter', + CAlterHooks::class . '::testSubtypeAlter', + DAlterHooks::class . '::testAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], ['test', 'test_subtype', 'test_other'], []); + + $this->disableModules(['hk_b_test']); + + $this->assertAlterCallOrder([ + CAlterHooks::class . '::testAlter', + AAlterHooks::class . '::testAlterAfterC', + DAlterHooks::class . '::testAlter', + ], 'test', prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + AAlterHooks::class . '::testSubtypeAlter', + CAlterHooks::class . '::testSubtypeAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], 'test_subtype', prepend_unknown_type: FALSE); + + $this->assertAlterCallOrder([ + CAlterHooks::class . '::testAlter', + CAlterHooks::class . '::testSubtypeAlter', + AAlterHooks::class . '::testAlterAfterC', + AAlterHooks::class . '::testSubtypeAlter', + DAlterHooks::class . '::testAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], ['test', 'test_subtype'], prepend_unknown_type: FALSE); + } + + public function testFormAlterOrder(): void { + $this->assertSameCallList([ + AFormAlterHooks::class . '::formAlter', + AFormAlterHooks::class . '::formAlterAfterB', + AFormAlterHooks::class . '::formAlterAfterBExtra', + BFormAlterHooks::class . '::formAlter', + CFormAlterHooks::class . '::formAlter', + ], $this->alter('form')['#calls'] ?? NULL); + + $this->assertSameCallList([ + BFormAlterHooks::class . '::formAlter', + BFormAlterHooks::class . '::myFormAlter', + AFormAlterHooks::class . '::formAlter', + AFormAlterHooks::class . '::formAlterAfterB', + AFormAlterHooks::class . '::formAlterAfterBExtra', + AFormAlterHooks::class . '::myFormAlter', + AFormAlterHooks::class . '::myFormAlterAfterB', + AFormAlterHooks::class . '::myFormAlterAfterBExtra', + ], $this->alter(['form', 'form_myform'])['#calls'] ?? NULL); + } + + public function testHookOrder(): void { + $this->assertSameCallList( + [ + // All the implementations from CHooks are gone. + // @todo This is probably bad. + AHooks::class . '::testHookFirst', + 'hk_a_test_testhook', + AHooks::class . '::testHook', + AHooks::class . '::testHookAfterB', + AHooks::class . '::testHookLast', + 'hk_b_test_testhook', + BHooks::class . '::testHook', + 'hk_c_test_testhook', + 'hk_d_test_testhook', + DHooks::class . '::testHook', + ], + \Drupal::moduleHandler()->invokeAll('testhook'), + ); + } + /** * Asserts the call order from an alter call. * -- GitLab From 88b202dea8f3814bdc6156eec4c15ed33ae64b94 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 2 Mar 2025 01:34:05 +0100 Subject: [PATCH 156/268] Use regular 'See' for link in doc. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 89bff8fdaf14..79397f271b4d 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -510,7 +510,7 @@ public function loadAllIncludes(): void { * This method is only to be used by ModuleHandler. * * @todo remove when ModuleHandler::add() is removed. - * @see https://www.drupal.org/project/drupal/issues/3481778 + * See https://www.drupal.org/project/drupal/issues/3481778 * * @internal */ -- GitLab From 37c8a7845a414db7c45816cfce536f6323cd1873 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 14:13:40 +0100 Subject: [PATCH 157/268] Calculate $otherSpecifiers later. --- .../Drupal/Core/Hook/HookCollectorPass.php | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 86a005467fe1..1c1925c0c886 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -292,18 +292,6 @@ protected static function registerImplementations( $hooks = $orderExtraTypes[$hookOrderOperation->hook] ?? [$hookOrderOperation->hook]; $combinedHook = implode(':', $hooks); if ($hookOrderOperation->order instanceof ComplexOrder) { - // Verify the correct structure of - // $hookOrderOperation->order->classesAndMethods and create specifiers - // for HookPriority::change() while at it. - $otherSpecifiers = array_map( - static function ($pair) { - if (!is_array($pair)) { - return throw new \LogicException('classesAndMethods needs to be an array of arrays'); - } - return $pair[0] . '::' . $pair[1]; - }, - $hookOrderOperation->order->classesAndMethods - ); // Collect classes and methods for // self::registerComplexHookImplementations(). $classesAndMethods = $hookOrderOperation->order->classesAndMethods; @@ -312,11 +300,22 @@ static function ($pair) { foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { $classesAndMethods[] = [$class, $method]; - $otherSpecifiers[] = "$class::$method"; } } } } + // Verify the correct structure of + // $hookOrderOperation->order->classesAndMethods and create specifiers + // for HookPriority::change() while at it. + $otherSpecifiers = array_map( + static function ($pair) { + if (!is_array($pair)) { + return throw new \LogicException('classesAndMethods needs to be an array of arrays'); + } + return $pair[0] . '::' . $pair[1]; + }, + $classesAndMethods, + ); if (count($hooks) > 1) { // The hook implementation in $hookOrderOperation and everything in // $classesAndMethods will be ordered relative to each other as if -- GitLab From 28a8c6ac2de9e96e87593bd997805e9eca930fc3 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 14:24:50 +0100 Subject: [PATCH 158/268] Collapse HookPriority::change() into HookCollectorPass::registerImplementations(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 64 ++++++++- core/lib/Drupal/Core/Hook/HookPriority.php | 80 ----------- .../Hook/HookPriorityEqualPriorityTest.php | 64 --------- .../Tests/Core/Hook/HookPriorityTest.php | 124 ------------------ .../Tests/Core/Hook/HookPriorityTestBase.php | 85 ------------ 5 files changed, 63 insertions(+), 354 deletions(-) delete mode 100644 core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php delete mode 100644 core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php delete mode 100644 core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 1c1925c0c886..7fd94d6a8a61 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -329,7 +329,69 @@ static function ($pair) { else { $otherSpecifiers = NULL; } - $hookPriority->change("drupal_hook.$combinedHook", $hookOrderOperation, $otherSpecifiers); + + foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $tags) { + foreach ($tags as $key => $tag) { + if ($tag['event'] === "drupal_hook.$combinedHook") { + $index = "$id.$key"; + $priority = $tag['priority']; + // Symfony documents event listener priorities to be integers, + // HookCollectorPass sets them to be integers, ::set() only + // accepts integers. + assert(is_int($priority)); + $priorities[$index] = $priority; + $specifier = "$id::" . $tag['method']; + if ($specifier === "$hookOrderOperation->class::$hookOrderOperation->method") { + $index_this = $index; + } + // $other_specifiers is defined for before and after, for these + // compare only the priority of those. For first and last the + // priority of every other hook matters. + elseif (!isset($otherSpecifiers) || in_array($specifier, $otherSpecifiers)) { + $priorities_other[$specifier] = $priority; + } + } + } + } + if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { + return; + } + + $shouldBeLarger = (bool) $hookOrderOperation->order->value; + // The priority of the hook being changed. + $priority_this = $priorities[$index_this]; + // The priority of the hook being compared to. + $priority_other = $shouldBeLarger ? max($priorities_other) : min($priorities_other); + // If the order is correct there is nothing to do. If the two priorities + // are the same then the order is undefined and so it can't be correct. + // If they are not the same and $priority_this is already larger exactly + // when $shouldBeLarger says then it's the correct order. + if ($priority_this !== $priority_other && ($shouldBeLarger === ($priority_this > $priority_other))) { + return; + } + $priority_new = $priority_other + ($shouldBeLarger ? 1 : -1); + // For first and last this new priority is already larger/smaller + // than all existing priorities but for before / after it might belong to + // an already existing hook. In this case set the new priority temporarily + // to be halfway between $priority_other and $priority_new then give all + // hook implementations new, integer priorities keeping this new order. + // This ensures the hook implementation being changed is in the right order + // relative to both $priority_other and the hook whose priority was + // $priority_new. + if (in_array($priority_new, $priorities)) { + $priorities[$index_this] = $priority_other + ($shouldBeLarger ? 0.5 : -0.5); + asort($priorities); + $changed_indexes = array_keys($priorities); + $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); + } + else { + $priorities[$index_this] = $priority_new; + $changed_indexes = [$index_this]; + } + foreach ($changed_indexes as $index) { + [$id, $key] = explode('.', $index); + $hookPriority->set($id, (int) $key, $priorities[$index]); + } } } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php index 36f6a076b6ef..fa271cd07df3 100644 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ b/core/lib/Drupal/Core/Hook/HookPriority.php @@ -15,86 +15,6 @@ class HookPriority { public function __construct(protected ContainerBuilder $container) {} - /** - * Change the priority of a hook implementation. - * - * @param string $event - * Listeners to this event will be ordered. - * @param \Drupal\Core\Hook\HookOperation $hook - * The hook attribute. Most of the order parameter is ignored by this - * class, only $hook->order->value is used. The rest is preprocessed by - * HookCollectorPass and passed in $other_specifiers. - * @param array|null $other_specifiers - * Other hook implementations to compare to, if any. The array is a list of - * strings, each string is a class and method separated by ::. - * - * @internal - */ - public function change(string $event, HookOperation $hook, ?array $other_specifiers = NULL): void { - foreach ($this->container->findTaggedServiceIds('kernel.event_listener') as $id => $tags) { - foreach ($tags as $key => $tag) { - if ($tag['event'] === $event) { - $index = "$id.$key"; - $priority = $tag['priority']; - // Symfony documents event listener priorities to be integers, - // HookCollectorPass sets them to be integers, ::set() only - // accepts integers. - assert(is_int($priority)); - $priorities[$index] = $priority; - $specifier = "$id::" . $tag['method']; - if ($specifier === "$hook->class::$hook->method") { - $index_this = $index; - } - // $other_specifiers is defined for before and after, for these - // compare only the priority of those. For first and last the - // priority of every other hook matters. - elseif (!isset($other_specifiers) || in_array($specifier, $other_specifiers)) { - $priorities_other[$specifier] = $priority; - } - } - } - } - if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { - return; - } - - $shouldBeLarger = (bool) $hook->order->value; - // The priority of the hook being changed. - $priority_this = $priorities[$index_this]; - // The priority of the hook being compared to. - $priority_other = $shouldBeLarger ? max($priorities_other) : min($priorities_other); - // If the order is correct there is nothing to do. If the two priorities - // are the same then the order is undefined and so it can't be correct. - // If they are not the same and $priority_this is already larger exactly - // when $shouldBeLarger says then it's the correct order. - if ($priority_this !== $priority_other && ($shouldBeLarger === ($priority_this > $priority_other))) { - return; - } - $priority_new = $priority_other + ($shouldBeLarger ? 1 : -1); - // For first and last this new priority is already larger/smaller - // than all existing priorities but for before / after it might belong to - // an already existing hook. In this case set the new priority temporarily - // to be halfway between $priority_other and $priority_new then give all - // hook implementations new, integer priorities keeping this new order. - // This ensures the hook implementation being changed is in the right order - // relative to both $priority_other and the hook whose priority was - // $priority_new. - if (in_array($priority_new, $priorities)) { - $priorities[$index_this] = $priority_other + ($shouldBeLarger ? 0.5 : -0.5); - asort($priorities); - $changed_indexes = array_keys($priorities); - $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); - } - else { - $priorities[$index_this] = $priority_new; - $changed_indexes = [$index_this]; - } - foreach ($changed_indexes as $index) { - [$id, $key] = explode('.', $index); - $this->set($id, (int) $key, $priorities[$index]); - } - } - /** * Set the priority of a listener. * diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php deleted file mode 100644 index 985e3d55c997..000000000000 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityEqualPriorityTest.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Core\Hook; - -use Drupal\Core\Hook\Order; -use Drupal\Core\Hook\OrderAfter; -use Drupal\Core\Hook\OrderBefore; - -/** - * @coversDefaultClass \Drupal\Core\Hook\HookPriority - * - * @group Hook - */ -class HookPriorityEqualPriorityTest extends HookPriorityTestBase { - - protected function setUp(): void { - parent::setUp(); - // The priority of "a", "b", "c" are the same, the order is undefined. - $this->setUpContainer(FALSE); - $this->assertSame($this->getPriority('a'), $this->getPriority('b')); - $this->assertSame($this->getPriority('b'), $this->getPriority('c')); - } - - public function testFirst(): void { - // "c" was first, make "a" the first. - $this->doPriorityChange('a', Order::First); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); - $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); - // Nothing else can be asserted: by setting the same priority, the setup - // had undefined order and so the services not included in the helper call - // can be in any order. - } - - public function testLast(): void { - // "c" was first, make it the last. - $this->doPriorityChange('c', Order::Last); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); - // Nothing else can be asserted: by setting the same priority, the setup - // had undefined order and so the services not included in the helper call - // can be in any order. - } - - public function testBefore(): void { - // "a" was last, move it before "b". - $this->doPriorityChange('a', OrderBefore::class, 'b'); - $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); - // Nothing else can be asserted: by setting the same priority, the setup - // had undefined order and so the services not included in the helper call - // can be in any order. - } - - public function testAfter(): void { - // "c" was first, move it after "b". - $this->doPriorityChange('c', OrderAfter::class, 'b'); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); - // Nothing else can be asserted: by setting the same priority, the setup - // had undefined order and so the services not included in the helper call - // can be in any order. - } - -} diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php deleted file mode 100644 index 65c8193b8f83..000000000000 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTest.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Core\Hook; - -use Drupal\Core\Hook\Order; -use Drupal\Core\Hook\OrderAfter; -use Drupal\Core\Hook\OrderBefore; - -/** - * @coversDefaultClass \Drupal\Core\Hook\HookPriority - * - * @group Hook - */ -class HookPriorityTest extends HookPriorityTestBase { - - /** - * The original priorities. - * - * @var array - */ - protected array $originalPriorities = []; - - /** - * {@inheritdoc} - */ - protected function setUp(): void { - parent::setUp(); - // "c" first, "b" second, "a" last. - $this->setUpContainer(TRUE); - foreach (['a', 'b', 'c'] as $key) { - $this->originalPriorities[$key] = $this->getPriority($key); - } - // The documentation does not clarify the order of arguments, let's do so - // here to make it easier to develop/debug this test. - $this->assertGreaterThan(1, 2); - // According to https://symfony.com/doc/current/event_dispatcher.html - // the higher the number, the earlier a listener is executed. Accordingly - // assert that "a" is last, "c" is first, "b" is in the middle. The - // asserts in methods can be compared to these establishing asserts. - $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('b')); - $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('c')); - // This is unnecessary, but it's easier to compare if this is explicit. - $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); - } - - public function testFirst(): void { - // "c" was first, make "a" the first. - $this->doPriorityChange('a', Order::First); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); - $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); - // The other two shouldn't change. - $this->assertNoChange('a'); - } - - public function testLast(): void { - // "c" was first, make it the last. - $this->doPriorityChange('c', Order::Last); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('a')); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); - // The other two shouldn't change. - $this->assertNoChange('c'); - } - - public function testBefore(): void { - // "a" was last, move it before "b". - $this->doPriorityChange('a', OrderBefore::class, 'b'); - $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('a')); - // The relation between these should not change. The actual priority - // might. - $this->assertGreaterThan($this->getPriority('b'), $this->getPriority('c')); - $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); - } - - public function testAfter(): void { - // "c" was first, move it after "b". - $this->doPriorityChange('c', OrderAfter::class, 'b'); - $this->assertGreaterThan($this->getPriority('c'), $this->getPriority('b')); - // The relation between these should not change. The actual priority - // might. - $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('b')); - $this->assertGreaterThan($this->getPriority('a'), $this->getPriority('c')); - } - - public function testFirstNoChange(): void { - // "c" was first, making it first should be a no-op. - $this->doPriorityChange('c', Order::First); - $this->assertNoChange(); - } - - public function testLastNoChange(): void { - // "a" was last, making it last should be a no-op. - $this->doPriorityChange('a', Order::Last); - $this->assertNoChange(); - } - - public function testBeforeNoChange(): void { - // "b" is already firing before "a", this should be a no-op. - $this->doPriorityChange('b', OrderBefore::class, 'a'); - $this->assertNoChange(); - } - - public function testAfterNoChange(): void { - // "b' is already firing after "c", this should be a no-op. - $this->doPriorityChange('b', OrderAfter::class, 'c'); - $this->assertNoChange(); - } - - /** - * Asserts no change has happened. - * - * @param string $changed - * This one did change. Assert the rest did not change. - */ - protected function assertNoChange(string $changed = ''): void { - foreach ($this->originalPriorities as $key => $priority) { - if ($key !== $changed) { - $this->assertSame($priority, $this->getPriority($key)); - } - } - } - -} diff --git a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php b/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php deleted file mode 100644 index 36cbd21055f0..000000000000 --- a/core/tests/Drupal/Tests/Core/Hook/HookPriorityTestBase.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Tests\Core\Hook; - -use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\HookPriority; -use Drupal\Core\Hook\Order; -use Drupal\Tests\UnitTestCase; - -/** - * Base class for testing HookPriority. - */ -abstract class HookPriorityTestBase extends UnitTestCase { - - /** - * The container builder. - * - * @var \Drupal\Core\DependencyInjection\ContainerBuilder - */ - protected ContainerBuilder $container; - - /** - * Set up three service listeners, "a", "b" and "c". - * - * The service id, the class name and the method name are all the same. - * - * @param bool $different_priority - * When TRUE, "c" will fire first, "b" second and "a" last. When FALSE, - * the priority will be set to be the same and the order is undefined. - */ - protected function setUpContainer(bool $different_priority): void { - $this->container = new ContainerBuilder(); - foreach (['a', 'b', 'c'] as $key => $name) { - $definition = $this->container - ->register($name, $name) - ->setAutowired(TRUE); - $definition->addTag('kernel.event_listener', [ - 'event' => 'drupal_hook.test', - 'method' => $name, - // Do not use $key itself to avoid a 0 priority which could potentially - // lead to misleading results. - 'priority' => $different_priority ? $key + 3 : 0, - ]); - } - } - - /** - * Get the priority for a service. - */ - protected function getPriority(string $name): int { - $definition = $this->container->getDefinition($name); - return $definition->getTags()['kernel.event_listener'][0]['priority']; - } - - /** - * Change priority of a class and method. - * - * @param class-string $classBeingChanged - * The class being changed, the method has the same name. - * @param \Drupal\Core\Hook\Order|class-string $order - * Either a member of the Order enum or the name of a ComplexOrder class. - * @param class-string $relativeTo - * If the operation is before or after, this is the name of the class - * the operation changes relative to. - */ - protected function doPriorityChange(string $classBeingChanged, Order|string $order, string $relativeTo = ''): void { - if ($relativeTo) { - // The modules / classesAndMethods argument of the order class is - // processed in HookCollectorPass and is ignored by HookPriority, they - // are passed to HookPriority in the $other_specifiers argument. - $hook = new Hook('test', order: new $order(modules: [''])); - $other_specifiers = ["$relativeTo::$relativeTo"]; - } - else { - $hook = new Hook('test', order: $order); - $other_specifiers = NULL; - } - $hook->set(class: $classBeingChanged, module: '', method: $classBeingChanged); - (new HookPriority($this->container))->change('drupal_hook.test', $hook, $other_specifiers); - } - -} -- GitLab From ad9d631d04df18689c7cad925e74e096b5e51319 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 14:30:42 +0100 Subject: [PATCH 159/268] Collapse HookPriority::set() into HookCollectorPass::registerImplementations(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 8 +++-- core/lib/Drupal/Core/Hook/HookPriority.php | 36 ------------------- 2 files changed, 5 insertions(+), 39 deletions(-) delete mode 100644 core/lib/Drupal/Core/Hook/HookPriority.php diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 7fd94d6a8a61..727469820c4a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -284,7 +284,6 @@ protected static function registerImplementations( $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); $container->setParameter('hook_implementations_map', $map ?? []); - $hookPriority = new HookPriority($container); foreach ($hookOrderOperations as $hookOrderOperation) { assert($hookOrderOperation instanceof HookOperation); // ::process() adds the hook serving as key to the order extraTypes so it @@ -389,8 +388,11 @@ static function ($pair) { $changed_indexes = [$index_this]; } foreach ($changed_indexes as $index) { - [$id, $key] = explode('.', $index); - $hookPriority->set($id, (int) $key, $priorities[$index]); + [$id1, $key1] = explode('.', $index); + $definition = $container->findDefinition($id1); + $tags = $definition->getTags(); + $tags['kernel.event_listener'][$key1]['priority'] = $priorities[$index]; + $definition->setTags($tags); } } } diff --git a/core/lib/Drupal/Core/Hook/HookPriority.php b/core/lib/Drupal/Core/Hook/HookPriority.php deleted file mode 100644 index fa271cd07df3..000000000000 --- a/core/lib/Drupal/Core/Hook/HookPriority.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook; - -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * Helper class for HookCollectorPass to change the priority of listeners. - * - * @internal - */ -class HookPriority { - - public function __construct(protected ContainerBuilder $container) {} - - /** - * Set the priority of a listener. - * - * @param string $class - * The name of the class, this is the same as the service id. - * @param int $key - * The key within the tags array of the 'kernel.event_listener' tag for the - * hook implementation to be changed. - * @param int $priority - * The new priority. - */ - public function set(string $class, int $key, int $priority): void { - $definition = $this->container->findDefinition($class); - $tags = $definition->getTags(); - $tags['kernel.event_listener'][$key]['priority'] = $priority; - $definition->setTags($tags); - } - -} -- GitLab From 5827264831709c0e8ffd4447af69ae7417e3fff8 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 15:57:00 +0100 Subject: [PATCH 160/268] Do not unset the entry in $implementations, because no reason to. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 727469820c4a..6e45be932c3b 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -260,7 +260,6 @@ protected static function registerImplementations( $map[$hook][$class][$method] = $module; } } - unset($implementations[$hook][$module]); } } -- GitLab From 729a66bd2684c572dc06e2d769898e3357289ec0 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 15:58:56 +0100 Subject: [PATCH 161/268] Initialize $map before foreach(). --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 6e45be932c3b..3e4924d99426 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -233,6 +233,7 @@ protected static function registerImplementations( } $tagsInfoByClass = []; + $map = []; foreach ($moduleImplementsMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; // Add implementations to the array we pass to legacy ordering @@ -281,7 +282,7 @@ protected static function registerImplementations( $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); - $container->setParameter('hook_implementations_map', $map ?? []); + $container->setParameter('hook_implementations_map', $map); foreach ($hookOrderOperations as $hookOrderOperation) { assert($hookOrderOperation instanceof HookOperation); -- GitLab From 86e32f3223fe30ce4312d7ac66801e28937ea54d Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 16:00:43 +0100 Subject: [PATCH 162/268] Calculate $map and $tagsInfoByClass in separate foreach(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 3e4924d99426..febafe7d6a7f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -232,8 +232,7 @@ protected static function registerImplementations( } } - $tagsInfoByClass = []; - $map = []; + $alteredImplementations = []; foreach ($moduleImplementsMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; // Add implementations to the array we pass to legacy ordering @@ -245,25 +244,31 @@ protected static function registerImplementations( foreach ($collector->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } - // Start at 0 for the first hook. We decrease the priority after each - // hook that is registered. Symfony priorities run higher priorities - // first. - $priority = 0; foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { - $tagsInfoByClass[$class][] = [ - 'event' => "drupal_hook.$hook", - 'method' => $method, - 'priority' => $priority, - ]; - --$priority; - $map[$hook][$class][$method] = $module; + $alteredImplementations[$hook]["$class::$method"] = $module; } } } } + $map = []; + $tagsInfoByClass = []; + foreach ($alteredImplementations as $hook => $hookImplementations) { + $priority = 0; + foreach ($hookImplementations as $class_and_method => $module) { + [$class, $method] = explode('::', $class_and_method); + $tagsInfoByClass[$class][] = [ + 'event' => "drupal_hook.$hook", + 'method' => $method, + 'priority' => $priority, + ]; + --$priority; + $map[$hook][$class][$method] = $module; + } + } + foreach ($tagsInfoByClass as $class => $tagsInfo) { if ($container->hasDefinition($class)) { $definition = $container->findDefinition($class); -- GitLab From 0e5bfb5551edef28236487b37e4667d5c557b868 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 16:16:51 +0100 Subject: [PATCH 163/268] Collapse registerComplexHookImplementations() into registerImplementations(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 44 ++++++------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index febafe7d6a7f..546f92ae8a95 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -327,7 +327,20 @@ static function ($pair) { // their service definition and added to the // hook_implementations_map container parameter. $classesAndMethods[] = [$hookOrderOperation->class, $hookOrderOperation->method]; - self::registerComplexHookImplementations($container, $classesAndMethods, $moduleFinder, $combinedHook); + + $map1 = $container->getParameter('hook_implementations_map'); + $priority1 = 0; + foreach ($classesAndMethods as [$class1, $method1]) { + // Ordering against not installed modules is possible. + if (isset($moduleFinder[$class1][$method1])) { + if (count(array_unique($moduleFinder[$class1][$method1])) > 1) { + throw new \LogicException('Complex ordering can only work when all implementations on a single method are for the same module.'); + } + $map1[$combinedHook][$class1][$method1] = reset($moduleFinder[$class1][$method1]); + $priority1 = self::addTagToDefinition($container->findDefinition($class1), $combinedHook, $method1, $priority1); + } + } + $container->setParameter('hook_implementations_map', $map1); } } else { @@ -666,33 +679,4 @@ protected static function addTagToDefinition(Definition $definition, string|int return $priority; } - /** - * Register complex hook implementations. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * The container. - * @param list<array{class-string, string}> $classesAndMethods - * A list of class-and-method pairs. - * @param array<class-string, array<string, array<string, string>>> $moduleFinder - * Array keys are the class, method, and hook, array values are module - * names. - * @param string $combinedHook - * A string made form list of hooks separated by :. - */ - protected static function registerComplexHookImplementations(ContainerBuilder $container, array $classesAndMethods, array $moduleFinder, string $combinedHook): void { - $map = $container->getParameter('hook_implementations_map'); - $priority = 0; - foreach ($classesAndMethods as [$class, $method]) { - // Ordering against not installed modules is possible. - if (isset($moduleFinder[$class][$method])) { - if (count(array_unique($moduleFinder[$class][$method])) > 1) { - throw new \LogicException('Complex ordering can only work when all implementations on a single method are for the same module.'); - } - $map[$combinedHook][$class][$method] = reset($moduleFinder[$class][$method]); - $priority = self::addTagToDefinition($container->findDefinition($class), $combinedHook, $method, $priority); - } - } - $container->setParameter('hook_implementations_map', $map); - } - } -- GitLab From 7bbce2fa14f94152ef7b18efe01011d2ef9667ab Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 6 Mar 2025 18:40:16 +0100 Subject: [PATCH 164/268] Collapse addTagToDefinition() into registerImplementations(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 546f92ae8a95..8cc67e7e53ce 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -337,7 +337,11 @@ static function ($pair) { throw new \LogicException('Complex ordering can only work when all implementations on a single method are for the same module.'); } $map1[$combinedHook][$class1][$method1] = reset($moduleFinder[$class1][$method1]); - $priority1 = self::addTagToDefinition($container->findDefinition($class1), $combinedHook, $method1, $priority1); + $container->findDefinition($class1)->addTag('kernel.event_listener', [ + 'event' => "drupal_hook.$combinedHook", + 'method' => $method1, + 'priority' => $priority1--, + ]); } } $container->setParameter('hook_implementations_map', $map1); @@ -655,28 +659,4 @@ protected static function getAttributeInstances(\ReflectionClass $reflectionClas return $attributes; } - /** - * Adds an event listener tag to a service definition. - * - * @param \Symfony\Component\DependencyInjection\Definition $definition - * The service definition. - * @param string|int $hook - * The name of the hook. - * @param string $method - * The method. - * @param int $priority - * The priority. - * - * @return int - * A new priority, guaranteed to be lower than $priority. - */ - protected static function addTagToDefinition(Definition $definition, string|int $hook, string $method, int $priority): int { - $definition->addTag('kernel.event_listener', [ - 'event' => "drupal_hook.$hook", - 'method' => $method, - 'priority' => $priority--, - ]); - return $priority; - } - } -- GitLab From 37df741b51afdac06892b32a60ca98c3b8c79599 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 01:19:41 +0100 Subject: [PATCH 165/268] Rewrite parts of HookCollectorPass to calculate first, then write to container. --- .../Drupal/Core/Hook/HookCollectorPass.php | 275 +++++++++--------- 1 file changed, 138 insertions(+), 137 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 8cc67e7e53ce..96806ded7509 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -16,7 +16,6 @@ use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; /** * Collects and registers hook implementations. @@ -224,6 +223,43 @@ protected static function registerImplementations( } } + $implementationsByHook = static::calculateImplementations( + $implementations, + $collector, + $orderExtraTypes, + $hookOrderOperations, + ); + + static::writeImplementationsToContainer($container, $implementationsByHook); + + // Update the module handler definition. + $definition = $container->getDefinition('module_handler'); + $definition->setArgument('$groupIncludes', $groupIncludes); + $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); + } + + /** + * Calculates the ordered implementations. + * + * @param array<string, array<string, array<class-string, list<string>>>> $implementations + * All implementations, as method names keyed by hook, module and class. + * @param \Drupal\Core\Hook\HookCollectorPass $collector + * The collector. + * @param array<string, list<string>> $orderExtraTypes + * Extra types to order a hook with. + * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations + * All attributes that contain ordering information. + * + * @return array<string, array<string, string>> + * Implementations, as module names keyed by hook name and "$class::$method" + * identifier. + */ + protected static function calculateImplementations( + array $implementations, + self $collector, + array $orderExtraTypes, + array $hookOrderOperations, + ): array { // List of hooks and modules formatted for hook_module_implements_alter(). $moduleImplementsMap = []; foreach ($implementations as $hook => $implementationsByModule) { @@ -232,7 +268,7 @@ protected static function registerImplementations( } } - $alteredImplementations = []; + $implementationsByHook = []; foreach ($moduleImplementsMap as $hook => $moduleImplements) { $extraHooks = $orderExtraTypes[$hook] ?? []; // Add implementations to the array we pass to legacy ordering @@ -247,15 +283,113 @@ protected static function registerImplementations( foreach ($moduleImplements as $module => $v) { foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { foreach ($methods as $method) { - $alteredImplementations[$hook]["$class::$method"] = $module; + $implementationsByHook[$hook]["$class::$method"] = $module; } } + if (count($extraHooks) > 1) { + $combinedHook = implode(':', $extraHooks); + foreach ($extraHooks as $extraHook) { + foreach ($implementations[$extraHook][$module] ?? [] as $class => $methods) { + foreach ($methods as $method) { + $implementationsByHook[$combinedHook]["$class::$method"] = $module; + } + } + } + } + } + } + + foreach ($hookOrderOperations as $hookOrderOperation) { + static::applyOrderAttributeOperation( + $implementationsByHook, + $orderExtraTypes, + $hookOrderOperation, + ); + } + + return $implementationsByHook; + } + + /** + * Applies hook order changes from a single attribute with order information. + * + * @param array<string, array<string, string>> $implementationsByHook + * Implementations, as module names keyed by hook name and "$class::$method" + * identifier. + * @param array<string, list<string>> $orderExtraTypes + * Extra types to order a hook with. + * @param \Drupal\Core\Hook\HookOperation $hookOrderOperation + * Hook attribute with order information. + */ + protected static function applyOrderAttributeOperation( + array &$implementationsByHook, + array $orderExtraTypes, + HookOperation $hookOrderOperation, + ): void { + // ::process() adds the hook serving as key to the order extraTypes so it + // does not need to be added if there's a extraTypes for the hook. + $hooks = $orderExtraTypes[$hookOrderOperation->hook] ?? [$hookOrderOperation->hook]; + $combinedHook = implode(':', $hooks); + $identifier = $hookOrderOperation->class . '::' . $hookOrderOperation->method; + $module = $implementationsByHook[$combinedHook][$identifier] ?? NULL; + if ($module === NULL) { + // Implementation is not in the list. Nothing to reorder. + return; + } + $list = $implementationsByHook[$combinedHook]; + $order = $hookOrderOperation->order; + if ($order === NULL) { + throw new \InvalidArgumentException('This method must only be called with attributes that have order information.'); + } + if ($order === Order::First) { + unset($list[$identifier]); + $list = [$identifier => $module] + $list; + } + elseif ($order === Order::Last) { + unset($list[$identifier]); + $list[$identifier] = $module; + } + elseif ($order instanceof ComplexOrder) { + $shouldBeAfter = !$order->value; + unset($list[$identifier]); + $identifiers = array_keys($list); + $modules = array_values($list); + $compareIndices = []; + if (isset($hookOrderOperation->order->modules)) { + $compareIndices = array_keys(array_intersect($modules, $hookOrderOperation->order->modules)); + } + foreach ($hookOrderOperation->order->classesAndMethods as [$otherClass, $otherMethod]) { + $compareIndices[] = array_search("$otherClass::$otherMethod", $identifiers, TRUE); + } + if (!$compareIndices) { + return; } + $splice_index = $shouldBeAfter + ? max($compareIndices) + 1 + : min($compareIndices); + array_splice($identifiers, $splice_index, 0, [$identifier]); + array_splice($modules, $splice_index, 0, [$module]); + $list = array_combine($identifiers, $modules); } + $implementationsByHook[$combinedHook] = $list; + } + /** + * Writes all implementations to the container. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * The container builder. + * @param array<string, array<string, string>> $implementationsByHook + * Implementations, as module names keyed by hook name and "$class::$method" + * identifier. + */ + protected static function writeImplementationsToContainer( + ContainerBuilder $container, + array $implementationsByHook, + ): void { $map = []; $tagsInfoByClass = []; - foreach ($alteredImplementations as $hook => $hookImplementations) { + foreach ($implementationsByHook as $hook => $hookImplementations) { $priority = 0; foreach ($hookImplementations as $class_and_method => $module) { [$class, $method] = explode('::', $class_and_method); @@ -283,140 +417,7 @@ protected static function registerImplementations( } } - // Pass necessary parameters to moduleHandler. - $definition = $container->getDefinition('module_handler'); - $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); $container->setParameter('hook_implementations_map', $map); - - foreach ($hookOrderOperations as $hookOrderOperation) { - assert($hookOrderOperation instanceof HookOperation); - // ::process() adds the hook serving as key to the order extraTypes so it - // does not need to be added if there's a extraTypes for the hook. - $hooks = $orderExtraTypes[$hookOrderOperation->hook] ?? [$hookOrderOperation->hook]; - $combinedHook = implode(':', $hooks); - if ($hookOrderOperation->order instanceof ComplexOrder) { - // Collect classes and methods for - // self::registerComplexHookImplementations(). - $classesAndMethods = $hookOrderOperation->order->classesAndMethods; - foreach ($hookOrderOperation->order->modules as $module) { - foreach ($hooks as $hook) { - foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { - foreach ($methods as $method) { - $classesAndMethods[] = [$class, $method]; - } - } - } - } - // Verify the correct structure of - // $hookOrderOperation->order->classesAndMethods and create specifiers - // for HookPriority::change() while at it. - $otherSpecifiers = array_map( - static function ($pair) { - if (!is_array($pair)) { - return throw new \LogicException('classesAndMethods needs to be an array of arrays'); - } - return $pair[0] . '::' . $pair[1]; - }, - $classesAndMethods, - ); - if (count($hooks) > 1) { - // The hook implementation in $hookOrderOperation and everything in - // $classesAndMethods will be ordered relative to each other as if - // they were implementing a single hook. This needs to be marked on - // their service definition and added to the - // hook_implementations_map container parameter. - $classesAndMethods[] = [$hookOrderOperation->class, $hookOrderOperation->method]; - - $map1 = $container->getParameter('hook_implementations_map'); - $priority1 = 0; - foreach ($classesAndMethods as [$class1, $method1]) { - // Ordering against not installed modules is possible. - if (isset($moduleFinder[$class1][$method1])) { - if (count(array_unique($moduleFinder[$class1][$method1])) > 1) { - throw new \LogicException('Complex ordering can only work when all implementations on a single method are for the same module.'); - } - $map1[$combinedHook][$class1][$method1] = reset($moduleFinder[$class1][$method1]); - $container->findDefinition($class1)->addTag('kernel.event_listener', [ - 'event' => "drupal_hook.$combinedHook", - 'method' => $method1, - 'priority' => $priority1--, - ]); - } - } - $container->setParameter('hook_implementations_map', $map1); - } - } - else { - $otherSpecifiers = NULL; - } - - foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $tags) { - foreach ($tags as $key => $tag) { - if ($tag['event'] === "drupal_hook.$combinedHook") { - $index = "$id.$key"; - $priority = $tag['priority']; - // Symfony documents event listener priorities to be integers, - // HookCollectorPass sets them to be integers, ::set() only - // accepts integers. - assert(is_int($priority)); - $priorities[$index] = $priority; - $specifier = "$id::" . $tag['method']; - if ($specifier === "$hookOrderOperation->class::$hookOrderOperation->method") { - $index_this = $index; - } - // $other_specifiers is defined for before and after, for these - // compare only the priority of those. For first and last the - // priority of every other hook matters. - elseif (!isset($otherSpecifiers) || in_array($specifier, $otherSpecifiers)) { - $priorities_other[$specifier] = $priority; - } - } - } - } - if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) { - return; - } - - $shouldBeLarger = (bool) $hookOrderOperation->order->value; - // The priority of the hook being changed. - $priority_this = $priorities[$index_this]; - // The priority of the hook being compared to. - $priority_other = $shouldBeLarger ? max($priorities_other) : min($priorities_other); - // If the order is correct there is nothing to do. If the two priorities - // are the same then the order is undefined and so it can't be correct. - // If they are not the same and $priority_this is already larger exactly - // when $shouldBeLarger says then it's the correct order. - if ($priority_this !== $priority_other && ($shouldBeLarger === ($priority_this > $priority_other))) { - return; - } - $priority_new = $priority_other + ($shouldBeLarger ? 1 : -1); - // For first and last this new priority is already larger/smaller - // than all existing priorities but for before / after it might belong to - // an already existing hook. In this case set the new priority temporarily - // to be halfway between $priority_other and $priority_new then give all - // hook implementations new, integer priorities keeping this new order. - // This ensures the hook implementation being changed is in the right order - // relative to both $priority_other and the hook whose priority was - // $priority_new. - if (in_array($priority_new, $priorities)) { - $priorities[$index_this] = $priority_other + ($shouldBeLarger ? 0.5 : -0.5); - asort($priorities); - $changed_indexes = array_keys($priorities); - $priorities = array_combine($changed_indexes, range(1, count($changed_indexes))); - } - else { - $priorities[$index_this] = $priority_new; - $changed_indexes = [$index_this]; - } - foreach ($changed_indexes as $index) { - [$id1, $key1] = explode('.', $index); - $definition = $container->findDefinition($id1); - $tags = $definition->getTags(); - $tags['kernel.event_listener'][$key1]['priority'] = $priorities[$index]; - $definition->setTags($tags); - } - } } /** -- GitLab From 67176715d09f6599cd1281011d06179be3c2a2f2 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 01:20:12 +0100 Subject: [PATCH 166/268] More elegant array merge in ModuleHandler. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index e8d658bb71b8..9c80e74cab99 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -460,7 +460,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // for ordering because the set might contain hooks not included in // this alter() call. \Drupal\Core\Hook\HookPriority::change() // registers the implementations of combined hooks. - foreach (array_merge($extra_hooks, [$type . '_alter']) as $extra_hook) { + foreach ([...$extra_hooks, $hook] as $extra_hook) { if (isset($this->orderedExtraTypes[$extra_hook])) { $orderedHooks = $this->orderedExtraTypes[$extra_hook]; $extra_listeners = $this->findListenersForAlter(implode(':', $orderedHooks)); -- GitLab From 0c8edc03e798f1ac275354c81103e3001ec4e44b Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 13:39:43 +0100 Subject: [PATCH 167/268] Collect different attribute types separately. --- .../Drupal/Core/Hook/HookCollectorPass.php | 107 ++++++++---------- 1 file changed, 45 insertions(+), 62 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 96806ded7509..d5b9f5a86106 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -59,13 +59,21 @@ class HookCollectorPass implements CompilerPassInterface { private array $groupIncludes = []; /** - * A list of attributes for hook implementations. + * Implementations, as module names keyed by hook name and "$class::$method". * - * Keys are module, class and method. Values are Hook attributes. - * - * @var array<string, array<class-string, array<string, list<\Drupal\Core\Hook\HookOperation>>>> + * @var array<string, array<string, string>> + */ + protected array $implementations = []; + + /** + * @var array<int, list<\Drupal\Core\Hook\HookOperation>> + */ + protected array $orderAttributesByPhase = [0 => [], 1 => []]; + + /** + * @var list<\Drupal\Core\Hook\Attribute\RemoveHook> */ - protected array $moduleHooks = []; + protected array $removeHookAttributes = []; /** * {@inheritdoc} @@ -82,46 +90,12 @@ public function process(ContainerBuilder $container): array { // Hooks that should be ordered together when extra types are involved. $orderExtraTypes = []; - // Hook attributes that contain ordering information. - $hookOrderOperations = []; - - // List of modules that the hooks are defined for, keyed by class and - // method. - $moduleFinder = []; - - // These attributes need to be processed after all hooks have been - // processed. - $processAfter = [ - RemoveHook::class => [], - ReOrderHook::class => [], - ]; - foreach (array_keys($container->getParameter('container.modules')) as $module) { - foreach ($collector->moduleHooks[$module] ?? [] as $class => $attributesByMethod) { - foreach ($attributesByMethod as $method => $attributes) { - foreach ($attributes as $hookAttribute) { - assert($hookAttribute instanceof HookOperation); - if (isset($processAfter[get_class($hookAttribute)])) { - $processAfter[get_class($hookAttribute)][] = $hookAttribute; - continue; - } - if (!$hookAttribute instanceof Hook) { - // This is an unsupported attribute class, the code below would - // not work. - continue; - } - if ($class !== ProceduralCall::class) { - self::checkForProceduralOnlyHooks($hookAttribute, $class); - } - // Set properties on hook class that are needed for registration. - $hookAttribute->set($class, $module, $method); - // Store the implementation details for registering the hook. - $implementations[$hookAttribute->hook][$hookAttribute->module][$class][$hookAttribute->method] = $hookAttribute->method; - // Reverse lookup for modules implementing hooks. - $moduleFinder[$class][$hookAttribute->method][$hookAttribute->hook] = $hookAttribute->module; - if ($hookAttribute->order) { - $hookOrderOperations[] = $hookAttribute; - } - } + $modules = array_keys($container->getParameter('container.modules')); + foreach ($collector->implementations as $hook => $hookImplementations) { + foreach ($modules as $module) { + foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { + [$class, $method] = explode('::', $identifier); + $implementations[$hook][$module][$class][$method] = $method; } } } @@ -130,8 +104,9 @@ public function process(ContainerBuilder $container): array { // registering the hooks. This must happen after all collection, but before // registration to ensure the hook it is removing has already been // discovered. - foreach ($processAfter[RemoveHook::class] as $removeHook) { - if ($module = ($moduleFinder[$removeHook->class][$removeHook->method][$removeHook->hook] ?? '')) { + foreach ($collector->removeHookAttributes as $removeHook) { + $module = $collector->implementations[$removeHook->hook][$removeHook->class . '::' . $removeHook->method] ?? NULL; + if ($module !== NULL) { // Remove the hook implementation for the defined class, method, and // hook. unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); @@ -155,10 +130,8 @@ public function process(ContainerBuilder $container): array { // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes // precedence. - foreach ($processAfter[ReOrderHook::class] as $reOrderHook) { - $hookOrderOperations[] = $reOrderHook; - } - + /** @var list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations */ + $hookOrderOperations = array_merge(...$collector->orderAttributesByPhase); foreach ($hookOrderOperations as $hookWithOrder) { if ($hookWithOrder->order instanceof ComplexOrder && $hookWithOrder->order->extraTypes) { $extraTypes = [... $hookWithOrder->order->extraTypes, $hookWithOrder->hook]; @@ -173,7 +146,7 @@ public function process(ContainerBuilder $container): array { // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $orderExtraTypes, $hookOrderOperations, $moduleFinder); + static::registerImplementations($container, $collector, $implementations, $orderExtraTypes, $hookOrderOperations); } return $implementations; } @@ -193,12 +166,6 @@ public function process(ContainerBuilder $container): array { * Extra types to order a hook with. * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations * All attributes that contain ordering information. - * @param array<class-string, array<string, array<string, string>>> $moduleFinder - * Lookup map to find the module for each hook implementation. - * Array keys are the class, method, and hook, array values are module - * names. - * The module name can be different from the module the class is in, - * because an implementation can be on behalf of another module. */ protected static function registerImplementations( ContainerBuilder $container, @@ -206,7 +173,6 @@ protected static function registerImplementations( array $implementations, array $orderExtraTypes, array $hookOrderOperations, - array $moduleFinder, ): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); @@ -506,7 +472,24 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } } - $this->moduleHooks[$module][$class] = $attributes; + foreach ($attributes as $method => $methodAttributes) { + foreach ($methodAttributes as $attribute) { + if ($attribute instanceof Hook) { + self::checkForProceduralOnlyHooks($attribute, $class); + $this->implementations[$attribute->hook][$class . '::' . ($attribute->method ?: $method)] = $attribute->module ?? $module; + if ($attribute->order !== NULL) { + $attribute->set($class, $attribute->module ?? $module, $method); + $this->orderAttributesByPhase[0][] = $attribute; + } + } + elseif ($attribute instanceof ReOrderHook) { + $this->orderAttributesByPhase[1][] = $attribute; + } + elseif ($attribute instanceof RemoveHook) { + $this->removeHookAttributes[] = $attribute; + } + } + } } elseif (!$skip_procedural) { $implementations = $procedural_hook_file_cache->get($filename); @@ -570,15 +553,15 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv * The name of function implementing the hook. */ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { - $this->moduleHooks[$module][ProceduralCall::class][$function] = [new Hook($hook, method: $module . '_' . $hook)]; if ($hook === 'hook_info') { $this->hookInfo[] = $function; } - if ($hook === 'module_implements_alter') { + elseif ($hook === 'module_implements_alter') { $message = "$function without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788"; @trigger_error($message, E_USER_DEPRECATED); $this->moduleImplementsAlters[] = $function; } + $this->implementations[$hook][ProceduralCall::class . '::' . $module . '_' . $hook] = $module; if ($fileinfo->getExtension() !== 'module') { $this->includes[$function] = $fileinfo->getPathname(); } -- GitLab From a158d6f4be7c52490d092ee25cca4fe1cbd6fb71 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 13:55:57 +0100 Subject: [PATCH 168/268] Extract getOrderExtraTypes from ::process(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index d5b9f5a86106..76e11d68f68a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -87,9 +87,6 @@ public function process(ContainerBuilder $container): array { // List of modules implementing hooks with the implementation details. $implementations = []; - // Hooks that should be ordered together when extra types are involved. - $orderExtraTypes = []; - $modules = array_keys($container->getParameter('container.modules')); foreach ($collector->implementations as $hook => $hookImplementations) { foreach ($modules as $module) { @@ -132,6 +129,34 @@ public function process(ContainerBuilder $container): array { // precedence. /** @var list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations */ $hookOrderOperations = array_merge(...$collector->orderAttributesByPhase); + $orderExtraTypes = $collector->getOrderExtraTypes($hookOrderOperations); + + // @todo investigate whether this if() is needed after ModuleHandler::add() + // is removed. + // @see https://www.drupal.org/project/drupal/issues/3481778 + if (count($container->getDefinitions()) > 1) { + static::registerImplementations($container, $collector, $implementations, $orderExtraTypes, $hookOrderOperations); + } + return $implementations; + } + + /** + * Gets groups of extra hooks from collected data. + * + * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations + * All attributes that contain ordering information. + * + * @return array<string, list<string>> + * Lists of extra hooks keyed by main hook. + */ + protected function getOrderExtraTypes(array $hookOrderOperations): array { + // Loop over all ReOrderHook attributes and gather order information + // before registering the hooks. This must happen after all collection, + // but before registration to ensure this ordering directive takes + // precedence. + /** @var list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations */ + $hookOrderOperations = array_merge(...$this->orderAttributesByPhase); + $orderExtraTypes = []; foreach ($hookOrderOperations as $hookWithOrder) { if ($hookWithOrder->order instanceof ComplexOrder && $hookWithOrder->order->extraTypes) { $extraTypes = [... $hookWithOrder->order->extraTypes, $hookWithOrder->hook]; @@ -141,14 +166,7 @@ public function process(ContainerBuilder $container): array { } } $orderExtraTypes = array_map('array_unique', $orderExtraTypes); - - // @todo investigate whether this if() is needed after ModuleHandler::add() - // is removed. - // @see https://www.drupal.org/project/drupal/issues/3481778 - if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $orderExtraTypes, $hookOrderOperations); - } - return $implementations; + return array_map('array_values', $orderExtraTypes); } /** -- GitLab From 461e7a4d980825a756196e613ff75dd36cba7596 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 14:01:04 +0100 Subject: [PATCH 169/268] Collapse registerImplementations() into ::process(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 87 +++++++------------ 1 file changed, 32 insertions(+), 55 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 76e11d68f68a..924768284c59 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -134,9 +134,39 @@ public function process(ContainerBuilder $container): array { // @todo investigate whether this if() is needed after ModuleHandler::add() // is removed. // @see https://www.drupal.org/project/drupal/issues/3481778 - if (count($container->getDefinitions()) > 1) { - static::registerImplementations($container, $collector, $implementations, $orderExtraTypes, $hookOrderOperations); + if (count($container->getDefinitions()) <= 1) { + return $implementations; } + + $container->register(ProceduralCall::class, ProceduralCall::class) + ->addArgument($collector->includes); + + // Gather includes for each hook_hook_info group. + // We store this in $groupIncludes so moduleHandler can ensure the files + // are included runtime when the hooks are invoked. + $groupIncludes = []; + foreach ($collector->hookInfo as $function) { + foreach ($function() as $hook => $info) { + if (isset($collector->groupIncludes[$info['group']])) { + $groupIncludes[$hook] = $collector->groupIncludes[$info['group']]; + } + } + } + + $implementationsByHook = static::calculateImplementations( + $implementations, + $collector, + $orderExtraTypes, + $hookOrderOperations, + ); + + static::writeImplementationsToContainer($container, $implementationsByHook); + + // Update the module handler definition. + $definition = $container->getDefinition('module_handler'); + $definition->setArgument('$groupIncludes', $groupIncludes); + $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); + return $implementations; } @@ -169,59 +199,6 @@ protected function getOrderExtraTypes(array $hookOrderOperations): array { return array_map('array_values', $orderExtraTypes); } - /** - * Register hook implementations as event listeners. - * - * Passes required include and ordering information to module_handler. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * The container. - * @param \Drupal\Core\Hook\HookCollectorPass $collector - * The collector. - * @param array<string, array<string, array<class-string, list<string>>>> $implementations - * All implementations, as method names keyed by hook, module and class. - * @param array<string, list<string>> $orderExtraTypes - * Extra types to order a hook with. - * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations - * All attributes that contain ordering information. - */ - protected static function registerImplementations( - ContainerBuilder $container, - HookCollectorPass $collector, - array $implementations, - array $orderExtraTypes, - array $hookOrderOperations, - ): void { - $container->register(ProceduralCall::class, ProceduralCall::class) - ->addArgument($collector->includes); - - // Gather includes for each hook_hook_info group. - // We store this in $groupIncludes so moduleHandler can ensure the files - // are included runtime when the hooks are invoked. - $groupIncludes = []; - foreach ($collector->hookInfo as $function) { - foreach ($function() as $hook => $info) { - if (isset($collector->groupIncludes[$info['group']])) { - $groupIncludes[$hook] = $collector->groupIncludes[$info['group']]; - } - } - } - - $implementationsByHook = static::calculateImplementations( - $implementations, - $collector, - $orderExtraTypes, - $hookOrderOperations, - ); - - static::writeImplementationsToContainer($container, $implementationsByHook); - - // Update the module handler definition. - $definition = $container->getDefinition('module_handler'); - $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); - } - /** * Calculates the ordered implementations. * -- GitLab From 2fd82d100cc0760056710b061b2dda8553ff2eed Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 14:57:45 +0100 Subject: [PATCH 170/268] Don't use the nested array $implementations for calculations. --- .../Drupal/Core/Hook/HookCollectorPass.php | 72 +++++++------------ 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 924768284c59..706229b25874 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -84,11 +84,21 @@ class HookCollectorPass implements CompilerPassInterface { public function process(ContainerBuilder $container): array { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); + $implementationsByHook = $collector->implementations; + + // Loop over all RemoveHook attributes and remove them from the maps before + // registering the hooks. This must happen after all collection, but before + // registration to ensure the hook it is removing has already been + // discovered. + foreach ($collector->removeHookAttributes as $removeHook) { + unset($implementationsByHook[$removeHook->hook][$removeHook->class . '::' . $removeHook->method]); + } + // List of modules implementing hooks with the implementation details. $implementations = []; $modules = array_keys($container->getParameter('container.modules')); - foreach ($collector->implementations as $hook => $hookImplementations) { + foreach ($implementationsByHook as $hook => $hookImplementations) { foreach ($modules as $module) { foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { [$class, $method] = explode('::', $identifier); @@ -97,30 +107,11 @@ public function process(ContainerBuilder $container): array { } } - // Loop over all RemoveHook attributes and remove them from the maps before - // registering the hooks. This must happen after all collection, but before - // registration to ensure the hook it is removing has already been - // discovered. - foreach ($collector->removeHookAttributes as $removeHook) { - $module = $collector->implementations[$removeHook->hook][$removeHook->class . '::' . $removeHook->method] ?? NULL; - if ($module !== NULL) { - // Remove the hook implementation for the defined class, method, and - // hook. - unset($implementations[$removeHook->hook][$module][$removeHook->class][$removeHook->method]); - // Remove empty arrays, after the entry was removed. - // Hook implementation removal is expected to be rare, therefore it will - // be faster to do it like this than cleaning the entire tree - // afterwards. - if (empty($implementations[$removeHook->hook][$module][$removeHook->class])) { - unset($implementations[$removeHook->hook][$module][$removeHook->class]); - if (empty($implementations[$removeHook->hook][$module])) { - unset($implementations[$removeHook->hook][$module]); - if (empty($implementations[$removeHook->hook])) { - unset($implementations[$removeHook->hook]); - } - } - } - } + // @todo investigate whether this if() is needed after ModuleHandler::add() + // is removed. + // @see https://www.drupal.org/project/drupal/issues/3481778 + if (count($container->getDefinitions()) <= 1) { + return $implementations; } // Loop over all ReOrderHook attributes and gather order information @@ -131,13 +122,6 @@ public function process(ContainerBuilder $container): array { $hookOrderOperations = array_merge(...$collector->orderAttributesByPhase); $orderExtraTypes = $collector->getOrderExtraTypes($hookOrderOperations); - // @todo investigate whether this if() is needed after ModuleHandler::add() - // is removed. - // @see https://www.drupal.org/project/drupal/issues/3481778 - if (count($container->getDefinitions()) <= 1) { - return $implementations; - } - $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($collector->includes); @@ -154,7 +138,7 @@ public function process(ContainerBuilder $container): array { } $implementationsByHook = static::calculateImplementations( - $implementations, + $implementationsByHook, $collector, $orderExtraTypes, $hookOrderOperations, @@ -202,7 +186,9 @@ protected function getOrderExtraTypes(array $hookOrderOperations): array { /** * Calculates the ordered implementations. * - * @param array<string, array<string, array<class-string, list<string>>>> $implementations + * @param array<string, array<string, string>> $implementationsByHookOrig + * Implementations before ordering, as module names keyed by hook name and + * "$class::$method" identifier. * All implementations, as method names keyed by hook, module and class. * @param \Drupal\Core\Hook\HookCollectorPass $collector * The collector. @@ -216,15 +202,15 @@ protected function getOrderExtraTypes(array $hookOrderOperations): array { * identifier. */ protected static function calculateImplementations( - array $implementations, + array $implementationsByHookOrig, self $collector, array $orderExtraTypes, array $hookOrderOperations, ): array { // List of hooks and modules formatted for hook_module_implements_alter(). $moduleImplementsMap = []; - foreach ($implementations as $hook => $implementationsByModule) { - foreach ($implementationsByModule as $module => $implementationsByClass) { + foreach ($implementationsByHookOrig as $hook => $hookImplementations) { + foreach ($hookImplementations as $module) { $moduleImplementsMap[$hook][$module] = ''; } } @@ -242,18 +228,14 @@ protected static function calculateImplementations( $alter($moduleImplements, $hook); } foreach ($moduleImplements as $module => $v) { - foreach ($implementations[$hook][$module] ?? [] as $class => $methods) { - foreach ($methods as $method) { - $implementationsByHook[$hook]["$class::$method"] = $module; - } + foreach (array_keys($implementationsByHookOrig[$hook], $module, TRUE) as $identifier) { + $implementationsByHook[$hook][$identifier] = $module; } if (count($extraHooks) > 1) { $combinedHook = implode(':', $extraHooks); foreach ($extraHooks as $extraHook) { - foreach ($implementations[$extraHook][$module] ?? [] as $class => $methods) { - foreach ($methods as $method) { - $implementationsByHook[$combinedHook]["$class::$method"] = $module; - } + foreach (array_keys($implementationsByHookOrig[$extraHook] ?? [], $module, TRUE) as $identifier) { + $implementationsByHook[$combinedHook][$identifier] = $module; } } } -- GitLab From bc78d93a072c6b7eb9ac3932256dbad3c73b7ffb Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 15:07:26 +0100 Subject: [PATCH 171/268] Don't call ->process() from ->getImplementations(). --- .../Drupal/Core/Hook/HookCollectorPass.php | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 706229b25874..bc464a1b7aa0 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -77,42 +77,11 @@ class HookCollectorPass implements CompilerPassInterface { /** * {@inheritdoc} - * - * @return array<string, array<string, array<class-string, array<string, string>>>> - * Hook implementation method names keyed by hook, module, class and method. */ - public function process(ContainerBuilder $container): array { + public function process(ContainerBuilder $container): void { $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); - $implementationsByHook = $collector->implementations; - - // Loop over all RemoveHook attributes and remove them from the maps before - // registering the hooks. This must happen after all collection, but before - // registration to ensure the hook it is removing has already been - // discovered. - foreach ($collector->removeHookAttributes as $removeHook) { - unset($implementationsByHook[$removeHook->hook][$removeHook->class . '::' . $removeHook->method]); - } - - // List of modules implementing hooks with the implementation details. - $implementations = []; - - $modules = array_keys($container->getParameter('container.modules')); - foreach ($implementationsByHook as $hook => $hookImplementations) { - foreach ($modules as $module) { - foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { - [$class, $method] = explode('::', $identifier); - $implementations[$hook][$module][$class][$method] = $method; - } - } - } - - // @todo investigate whether this if() is needed after ModuleHandler::add() - // is removed. - // @see https://www.drupal.org/project/drupal/issues/3481778 - if (count($container->getDefinitions()) <= 1) { - return $implementations; - } + $implementationsByHook = $collector->getFilteredImplementations(); // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, @@ -150,8 +119,21 @@ public function process(ContainerBuilder $container): array { $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); + } - return $implementations; + /** + * Gets implementation lists with removals already applied. + * + * @return array<string, list<string>> + * Implementations, as module names keyed by hook name and + * "$class::$method". + */ + protected function getFilteredImplementations(): array { + $implementationsByHook = $this->implementations; + foreach ($this->removeHookAttributes as $removeHook) { + unset($implementationsByHook[$removeHook->hook][$removeHook->class . '::' . $removeHook->method]); + } + return $implementationsByHook; } /** @@ -569,7 +551,25 @@ public function loadAllIncludes(): void { public function getImplementations(array $paths): array { $container = new ContainerBuilder(); $container->setParameter('container.modules', $paths); - return $this->process($container); + + $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); + + $implementationsByHook = $collector->getFilteredImplementations(); + + // List of modules implementing hooks with the implementation details. + $implementations = []; + + $modules = array_keys($container->getParameter('container.modules')); + foreach ($implementationsByHook as $hook => $hookImplementations) { + foreach ($modules as $module) { + foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { + [$class, $method] = explode('::', $identifier); + $implementations[$hook][$module][$class][$method] = $method; + } + } + } + + return $implementations; } /** -- GitLab From da39ef4a521d9b7f97f90d8a8b6dcd3510a831b0 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 15:39:23 +0100 Subject: [PATCH 172/268] No need to set container.modules anymore. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index bc464a1b7aa0..479f17c1e643 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -550,16 +550,15 @@ public function loadAllIncludes(): void { */ public function getImplementations(array $paths): array { $container = new ContainerBuilder(); - $container->setParameter('container.modules', $paths); - $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); + $collector = static::collectAllHookImplementations($paths, $container); $implementationsByHook = $collector->getFilteredImplementations(); // List of modules implementing hooks with the implementation details. $implementations = []; - $modules = array_keys($container->getParameter('container.modules')); + $modules = array_keys($paths); foreach ($implementationsByHook as $hook => $hookImplementations) { foreach ($modules as $module) { foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { -- GitLab From 4fccc6a4eac6d73622b0c0cfcd92084dd4420caa Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 15:58:50 +0100 Subject: [PATCH 173/268] Don't use the container to get a reflection class. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 479f17c1e643..eb11d8c21953 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -375,7 +375,7 @@ public static function collectAllHookImplementations(array $module_filenames, ?C if ($container?->hasParameter("$module.hooks_converted")) { $skip_procedural = $container->getParameter("$module.hooks_converted"); } - $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural, $container); + $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural); } return $collector; } @@ -392,10 +392,8 @@ public static function collectAllHookImplementations(array $module_filenames, ?C * matched first. * @param bool $skip_procedural * Skip the procedural check for the current module. - * @param \Symfony\Component\DependencyInjection\ContainerBuilder|null $container - * The container. */ - protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural, ?ContainerBuilder $container = NULL): void { + protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural): void { $hook_file_cache = FileCacheFactory::get('hook_implementations'); $procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $module_preg); @@ -426,7 +424,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $class = str_replace('/', '\\', $class); $attributes = []; if (class_exists($class)) { - $reflectionClass = $container?->getReflectionClass($class) ?? new \ReflectionClass($class); + $reflectionClass = new \ReflectionClass($class); $attributes = self::getAttributeInstances($reflectionClass); $hook_file_cache->set($filename, ['class' => $class, 'attributes' => $attributes]); } -- GitLab From 283fa59afcddaffa72cbd91788705cdda14685ab Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 16:02:44 +0100 Subject: [PATCH 174/268] Don't create a collector in the collector. --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index eb11d8c21953..2f257673df9a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -547,11 +547,7 @@ public function loadAllIncludes(): void { * @internal */ public function getImplementations(array $paths): array { - $container = new ContainerBuilder(); - - $collector = static::collectAllHookImplementations($paths, $container); - - $implementationsByHook = $collector->getFilteredImplementations(); + $implementationsByHook = $this->getFilteredImplementations(); // List of modules implementing hooks with the implementation details. $implementations = []; -- GitLab From ca020b89100b27faa459f39626e29415139d6454 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 18:19:32 +0100 Subject: [PATCH 175/268] Array type docs on HookCollectorPass::getImplementations(). --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 2f257673df9a..a88591514b37 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -541,6 +541,12 @@ public function loadAllIncludes(): void { /** * This method is only to be used by ModuleHandler. * + * @param array<string, array{pathname: string}> $paths + * Reduced module info arrays by module name. + * + * @return array<string, array<string, array<class-string, array<string, string>>>> + * Hook implementation method names keyed by hook, module, class and method. + * * @todo remove when ModuleHandler::add() is removed. * See https://www.drupal.org/project/drupal/issues/3481778 * -- GitLab From 1819e77a342880916972306efc9720dad1e347e1 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 17:52:19 +0100 Subject: [PATCH 176/268] Rename some vars in collectAllHookImplementations(). --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 02d78fe8be4d..98c7aaca76da 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -339,7 +339,7 @@ protected static function writeImplementationsToContainer( /** * Collects all hook implementations. * - * @param array $module_filenames + * @param array $module_list * An associative array. Keys are the module names, values are relevant * info yml file path. * @param list<string> $skipProceduralModules @@ -355,13 +355,17 @@ protected static function writeImplementationsToContainer( * @todo Pass only $container when ModuleHandler::add() is removed * @see https://www.drupal.org/project/drupal/issues/3481778 */ - public static function collectAllHookImplementations(array $module_filenames, array $skipProceduralModules = []): static { - $modules = array_map(static fn ($x) => preg_quote($x, '/'), array_keys($module_filenames)); + public static function collectAllHookImplementations(array $module_list, array $skipProceduralModules = []): static { + $modules = array_keys($module_list); // Longer modules first. - usort($modules, fn($a, $b) => strlen($b) - strlen($a)); - $module_preg = '/^(?<function>(?<module>' . implode('|', $modules) . ')_(?!preprocess_)(?!update_\d)(?<hook>[a-zA-Z0-9_\x80-\xff]+$))/'; + usort($modules, fn ($a, $b) => strlen($b) - strlen($a)); + $known_modules_pattern = implode('|', array_map( + static fn ($x) => preg_quote($x, '/'), + $modules, + )); + $module_preg = '/^(?<function>(?<module>' . $known_modules_pattern . ')_(?!preprocess_)(?!update_\d)(?<hook>[a-zA-Z0-9_\x80-\xff]+$))/'; $collector = new static(); - foreach ($module_filenames as $module => $info) { + foreach ($module_list as $module => $info) { $skip_procedural = in_array($module, $skipProceduralModules); $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural); } -- GitLab From 362f2f4607fb3f7ffa0a374b72fc310800cd6e50 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 18:19:00 +0100 Subject: [PATCH 177/268] Array type docs on ModuleHandler::__construct() - param $groupIncludes. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 9c80e74cab99..29b6348290ae 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -78,8 +78,9 @@ class ModuleHandler implements ModuleHandlerInterface { * The event dispatcher. * @param array $hookImplementationsMap * An array keyed by hook, classname, method and the value is the module. - * @param array $groupIncludes - * An array of .inc files to get helpers from. + * @param array<string, list<string>> $groupIncludes + * Lists of *.inc file paths that contain procedural implementations, keyed + * by hook name. * @param array<string, list<string>> $orderedExtraTypes * A multidimensional array of hooks that have been ordered and the * extra_types they have been ordered against. This is stored separately -- GitLab From 18a7511374ae4fef782d81a0f84b1b0dba492fb8 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 4 Mar 2025 00:03:00 +0100 Subject: [PATCH 178/268] Really out of scope array docs. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 14 +++++++------- .../Core/Extension/ModuleHandlerInterface.php | 2 +- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 14 +++++++++++--- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 29b6348290ae..d83b68fb506d 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -61,7 +61,7 @@ class ModuleHandler implements ModuleHandlerInterface { /** * Hook and module keyed list of listeners. * - * @var array + * @var array<string, array<string, list<callable>>> */ protected array $invokeMap = []; @@ -70,13 +70,13 @@ class ModuleHandler implements ModuleHandlerInterface { * * @param string $root * The app root. - * @param array $module_list + * @param array<string, array{type: string, pathname: string, filename: string}> $module_list * An associative array whose keys are the names of installed modules and * whose values are Extension class parameters. This is normally the * %container.modules% parameter being set up by DrupalKernel. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher * The event dispatcher. - * @param array $hookImplementationsMap + * @param array<string, array<class-string, array<string, string>>> $hookImplementationsMap * An array keyed by hook, classname, method and the value is the module. * @param array<string, list<string>> $groupIncludes * Lists of *.inc file paths that contain procedural implementations, keyed @@ -496,12 +496,12 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { /** * Reorder modules for alters. * - * @param array $modules + * @param list<string> $modules * A list of modules. * @param string $hook - * The hook being worked on, for example form_alter. + * The hook being worked on, for example 'form_alter'. * - * @return array + * @return list<string> * The list, potentially reordered and changed by * hook_module_implements_alter(). */ @@ -577,7 +577,7 @@ public function writeCache() { * @param string $hook * The name of the hook. * - * @return array + * @return array<string, list<callable>> * A list of event listeners implementing this hook. */ protected function getHookListeners(string $hook): array { diff --git a/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php b/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php index 597da739a146..529fd7275a81 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php @@ -225,7 +225,7 @@ public function hasImplementations(string $hook, $modules = NULL): bool; * * @param string $hook * The name of the hook to invoke. - * @param callable $callback + * @param callable(callable, string): mixed $callback * A callable that invokes a hook implementation. Such that * $callback is callable(callable, string): mixed. * Arguments: diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 98c7aaca76da..de4c75f55124 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -33,9 +33,11 @@ class HookCollectorPass implements CompilerPassInterface { /** - * A list of include files. + * A map of include files by function name. * * (This is required only for BC.) + * + * @var array<string, string> */ protected array $includes = []; @@ -43,6 +45,8 @@ class HookCollectorPass implements CompilerPassInterface { * A list of functions implementing hook_module_implements_alter(). * * (This is required only for BC.) + * + * @var list<callable-string> */ protected array $moduleImplementsAlters = []; @@ -50,11 +54,15 @@ class HookCollectorPass implements CompilerPassInterface { * A list of functions implementing hook_hook_info(). * * (This is required only for BC.) + * + * @var list<callable-string> */ private array $hookInfo = []; /** - * A list of .inc files. + * Include files, keyed by the $group part of "/$module.$group.inc". + * + * @var array<string, list<string>> */ private array $groupIncludes = []; @@ -339,7 +347,7 @@ protected static function writeImplementationsToContainer( /** * Collects all hook implementations. * - * @param array $module_list + * @param array<string, array{pathname: string}> $module_list * An associative array. Keys are the module names, values are relevant * info yml file path. * @param list<string> $skipProceduralModules -- GitLab From 9f86b3d4bd5534254b8f3767bd7b4c6231f30489 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 17:20:34 +0100 Subject: [PATCH 179/268] Split HookCollector out of HookCollectorPass, step 1. --- core/lib/Drupal/Core/CoreServiceProvider.php | 4 +- .../Drupal/Core/Hook/HookCollectorPass.php | 22 ++-------- .../Core/Hook/HookCollectorPassTmpRename.php | 40 +++++++++++++++++++ .../Core/Hook/HookCollectorPassTest.php | 10 ++--- .../Tests/Core/GroupIncludesTestTrait.php | 2 +- .../Tests/Core/Hook/HookCollectorPassTest.php | 8 ++-- 6 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index cd9766c14b5e..288c75efcad9 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -10,7 +10,7 @@ use Drupal\Core\DependencyInjection\Compiler\CorsCompilerPass; use Drupal\Core\DependencyInjection\Compiler\DeprecatedServicePass; use Drupal\Core\DependencyInjection\Compiler\DevelopmentSettingsPass; -use Drupal\Core\Hook\HookCollectorPass; +use Drupal\Core\Hook\HookCollectorPassTmpRename; use Drupal\Core\DependencyInjection\Compiler\LoggerAwarePass; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass; @@ -62,7 +62,7 @@ public function register(ContainerBuilder $container) { ->addTag('stream_wrapper', ['scheme' => 'private']); } - $container->addCompilerPass(new HookCollectorPass()); + $container->addCompilerPass(new HookCollectorPassTmpRename()); // Add the compiler pass that lets service providers modify existing // service definitions. This pass must come before all passes operating on // services so that later list-building passes are operating on the diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index de4c75f55124..08c4522d16ad 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -11,10 +11,9 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter; -use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\Core\Hook\Attribute\RemoveHook; +use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -30,7 +29,7 @@ * Finally, a hook_implementations_map container parameter is added. This * contains a mapping from [hook,class,method] to the module name. */ -class HookCollectorPass implements CompilerPassInterface { +class HookCollectorPass { /** * A map of include files by function name. @@ -83,28 +82,13 @@ class HookCollectorPass implements CompilerPassInterface { */ protected array $removeHookAttributes = []; - /** - * {@inheritdoc} - */ - public function process(ContainerBuilder $container): void { - $module_list = $container->getParameter('container.modules'); - $parameters = $container->getParameterBag()->all(); - $skip_procedural_modules = array_filter( - array_keys($module_list), - fn (string $module) => !empty($parameters["$module.hooks_converted"]), - ); - $collector = static::collectAllHookImplementations($module_list, $skip_procedural_modules); - - $collector->writeToContainer($container); - } - /** * Writes collected definitions to the container builder. * * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * Container builder. */ - protected function writeToContainer(ContainerBuilder $container): void { + public function writeToContainer(ContainerBuilder $container): void { $orderExtraTypes = $this->getOrderExtraTypes(); $container->register(ProceduralCall::class, ProceduralCall::class) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php b/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php new file mode 100644 index 000000000000..aff7bb22cdcd --- /dev/null +++ b/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php @@ -0,0 +1,40 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Collects and registers hook implementations. + * + * A hook implementation is a class in a Drupal\modulename\Hook namespace + * where either the class itself or the methods have a #[Hook] attribute. + * These classes are automatically registered as autowired services. + * + * Services for procedural implementation of hooks are also registered + * using the ProceduralCall class. + * + * Finally, a hook_implementations_map container parameter is added. This + * contains a mapping from [hook,class,method] to the module name. + */ +class HookCollectorPassTmpRename implements CompilerPassInterface { + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container): void { + $module_list = $container->getParameter('container.modules'); + $parameters = $container->getParameterBag()->all(); + $skip_procedural_modules = array_filter( + array_keys($module_list), + fn (string $module) => !empty($parameters["$module.hooks_converted"]), + ); + $collector = HookCollectorPass::collectAllHookImplementations($module_list, $skip_procedural_modules); + + $collector->writeToContainer($container); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 10765f3a084b..309b8ef42a7a 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -4,13 +4,13 @@ namespace Drupal\KernelTests\Core\Hook; -use Drupal\Core\Hook\HookCollectorPass; +use Drupal\Core\Hook\HookCollectorPassTmpRename; use Drupal\KernelTests\KernelTestBase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; /** - * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPass + * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPassTmpRename * @group Hook */ class HookCollectorPassTest extends KernelTestBase { @@ -38,7 +38,7 @@ public function testSymlink(): void { ]; $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPass())->process($container); + (new HookCollectorPassTmpRename())->process($container); $implementations = [ 'user_format_name_alter' => [ 'Drupal\user_hooks_test\Hook\UserHooksTest' => [ @@ -64,7 +64,7 @@ public function testOrdering(): void { include_once 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test_all1/src/Hook/ModuleHandlerTestAll1Hooks.php'; $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPass())->process($container); + (new HookCollectorPassTmpRename())->process($container); $priorities = []; foreach ($container->findTaggedServiceIds('kernel.event_listener') as $tags) { foreach ($tags as $attributes) { @@ -95,7 +95,7 @@ public function testLegacyModuleImplementsAlter(): void { include_once 'core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.module'; $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPass())->process($container); + (new HookCollectorPassTmpRename())->process($container); // This test will also fail if the deprecation notice shows up. $this->assertFalse(isset($GLOBALS['ShouldNotRunLegacyModuleImplementsAlter'])); diff --git a/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php b/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php index 21157cdf1a05..b1ee243a8d02 100644 --- a/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php +++ b/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php @@ -7,7 +7,7 @@ use org\bovigo\vfs\vfsStream; /** - * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPass + * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPassTmpRename * @group Hook */ trait GroupIncludesTestTrait { diff --git a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php index 2d6fba3b451f..07673b77c118 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php @@ -6,7 +6,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\ProceduralCall; -use Drupal\Core\Hook\HookCollectorPass; +use Drupal\Core\Hook\HookCollectorPassTmpRename; use Drupal\Tests\UnitTestCase; use Drupal\Tests\Core\GroupIncludesTestTrait; use org\bovigo\vfs\vfsStream; @@ -14,7 +14,7 @@ use Symfony\Component\DependencyInjection\Definition; /** - * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPass + * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPassTmpRename * @group Hook */ class HookCollectorPassTest extends UnitTestCase { @@ -65,7 +65,7 @@ function test_module_should_be_skipped(); $container = new ContainerBuilder(); $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPass())->process($container); + (new HookCollectorPassTmpRename())->process($container); $this->assertSame($implementations, $container->getParameter('hook_implementations_map')); $this->assertSame($includes, $container->getDefinition(ProceduralCall::class)->getArguments()[0]); } @@ -79,7 +79,7 @@ public function testGroupIncludes(): void { $container = new ContainerBuilder(); $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPass())->process($container); + (new HookCollectorPassTmpRename())->process($container); $argument = $container->getDefinition('module_handler')->getArgument('$groupIncludes'); $this->assertSame(self::GROUP_INCLUDES, $argument); } -- GitLab From 2ecc406d125f947be7828a71b380fdb57c1eb5ec Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 17:39:43 +0100 Subject: [PATCH 180/268] Split HookCollector out of HookCollectorPass, step 2. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 4 ++-- .../Core/Hook/{HookCollectorPass.php => HookCollector.php} | 2 +- core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename core/lib/Drupal/Core/Hook/{HookCollectorPass.php => HookCollector.php} (99%) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index d83b68fb506d..9b6fad985155 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -6,7 +6,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Core\Hook\Attribute\LegacyHook; -use Drupal\Core\Hook\HookCollectorPass; +use Drupal\Core\Hook\HookCollector; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -212,7 +212,7 @@ protected function add($type, $name, $path) { $this->moduleList[$name] = new Extension($this->root, $type, $pathname, $filename); $this->resetImplementations(); $paths = [$name => ['pathname' => $pathname]]; - $hook_collector = HookCollectorPass::collectAllHookImplementations($paths); + $hook_collector = HookCollector::collectAllHookImplementations($paths); // A module freshly added will not be registered on the container yet. // ProceduralCall service does not yet know about it. // Note in HookCollectorPass: diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollector.php similarity index 99% rename from core/lib/Drupal/Core/Hook/HookCollectorPass.php rename to core/lib/Drupal/Core/Hook/HookCollector.php index 08c4522d16ad..421ccab6104d 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -29,7 +29,7 @@ * Finally, a hook_implementations_map container parameter is added. This * contains a mapping from [hook,class,method] to the module name. */ -class HookCollectorPass { +class HookCollector { /** * A map of include files by function name. diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php b/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php index aff7bb22cdcd..b8b6b0485aa9 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php @@ -32,7 +32,7 @@ public function process(ContainerBuilder $container): void { array_keys($module_list), fn (string $module) => !empty($parameters["$module.hooks_converted"]), ); - $collector = HookCollectorPass::collectAllHookImplementations($module_list, $skip_procedural_modules); + $collector = HookCollector::collectAllHookImplementations($module_list, $skip_procedural_modules); $collector->writeToContainer($container); } -- GitLab From af57ed77c9c7e3b88acecd695df80ed69d24ecfe Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 17:40:06 +0100 Subject: [PATCH 181/268] Split HookCollector out of HookCollectorPass, step 3. --- core/lib/Drupal/Core/CoreServiceProvider.php | 4 ++-- ...ollectorPassTmpRename.php => HookCollectorPass.php} | 2 +- .../KernelTests/Core/Hook/HookCollectorPassTest.php | 10 +++++----- .../tests/Drupal/Tests/Core/GroupIncludesTestTrait.php | 2 +- .../Drupal/Tests/Core/Hook/HookCollectorPassTest.php | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) rename core/lib/Drupal/Core/Hook/{HookCollectorPassTmpRename.php => HookCollectorPass.php} (94%) diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 288c75efcad9..cd9766c14b5e 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -10,7 +10,7 @@ use Drupal\Core\DependencyInjection\Compiler\CorsCompilerPass; use Drupal\Core\DependencyInjection\Compiler\DeprecatedServicePass; use Drupal\Core\DependencyInjection\Compiler\DevelopmentSettingsPass; -use Drupal\Core\Hook\HookCollectorPassTmpRename; +use Drupal\Core\Hook\HookCollectorPass; use Drupal\Core\DependencyInjection\Compiler\LoggerAwarePass; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass; @@ -62,7 +62,7 @@ public function register(ContainerBuilder $container) { ->addTag('stream_wrapper', ['scheme' => 'private']); } - $container->addCompilerPass(new HookCollectorPassTmpRename()); + $container->addCompilerPass(new HookCollectorPass()); // Add the compiler pass that lets service providers modify existing // service definitions. This pass must come before all passes operating on // services so that later list-building passes are operating on the diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php similarity index 94% rename from core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php rename to core/lib/Drupal/Core/Hook/HookCollectorPass.php index b8b6b0485aa9..2839dc110919 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPassTmpRename.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -20,7 +20,7 @@ * Finally, a hook_implementations_map container parameter is added. This * contains a mapping from [hook,class,method] to the module name. */ -class HookCollectorPassTmpRename implements CompilerPassInterface { +class HookCollectorPass implements CompilerPassInterface { /** * {@inheritdoc} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 309b8ef42a7a..10765f3a084b 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -4,13 +4,13 @@ namespace Drupal\KernelTests\Core\Hook; -use Drupal\Core\Hook\HookCollectorPassTmpRename; +use Drupal\Core\Hook\HookCollectorPass; use Drupal\KernelTests\KernelTestBase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; /** - * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPassTmpRename + * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPass * @group Hook */ class HookCollectorPassTest extends KernelTestBase { @@ -38,7 +38,7 @@ public function testSymlink(): void { ]; $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPassTmpRename())->process($container); + (new HookCollectorPass())->process($container); $implementations = [ 'user_format_name_alter' => [ 'Drupal\user_hooks_test\Hook\UserHooksTest' => [ @@ -64,7 +64,7 @@ public function testOrdering(): void { include_once 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test_all1/src/Hook/ModuleHandlerTestAll1Hooks.php'; $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPassTmpRename())->process($container); + (new HookCollectorPass())->process($container); $priorities = []; foreach ($container->findTaggedServiceIds('kernel.event_listener') as $tags) { foreach ($tags as $attributes) { @@ -95,7 +95,7 @@ public function testLegacyModuleImplementsAlter(): void { include_once 'core/tests/Drupal/Tests/Core/Extension/modules/module_implements_alter_test_legacy/module_implements_alter_test_legacy.module'; $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPassTmpRename())->process($container); + (new HookCollectorPass())->process($container); // This test will also fail if the deprecation notice shows up. $this->assertFalse(isset($GLOBALS['ShouldNotRunLegacyModuleImplementsAlter'])); diff --git a/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php b/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php index b1ee243a8d02..21157cdf1a05 100644 --- a/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php +++ b/core/tests/Drupal/Tests/Core/GroupIncludesTestTrait.php @@ -7,7 +7,7 @@ use org\bovigo\vfs\vfsStream; /** - * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPassTmpRename + * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPass * @group Hook */ trait GroupIncludesTestTrait { diff --git a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php index 07673b77c118..2d6fba3b451f 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php @@ -6,7 +6,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\ProceduralCall; -use Drupal\Core\Hook\HookCollectorPassTmpRename; +use Drupal\Core\Hook\HookCollectorPass; use Drupal\Tests\UnitTestCase; use Drupal\Tests\Core\GroupIncludesTestTrait; use org\bovigo\vfs\vfsStream; @@ -14,7 +14,7 @@ use Symfony\Component\DependencyInjection\Definition; /** - * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPassTmpRename + * @coversDefaultClass \Drupal\Core\Hook\HookCollectorPass * @group Hook */ class HookCollectorPassTest extends UnitTestCase { @@ -65,7 +65,7 @@ function test_module_should_be_skipped(); $container = new ContainerBuilder(); $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPassTmpRename())->process($container); + (new HookCollectorPass())->process($container); $this->assertSame($implementations, $container->getParameter('hook_implementations_map')); $this->assertSame($includes, $container->getDefinition(ProceduralCall::class)->getArguments()[0]); } @@ -79,7 +79,7 @@ public function testGroupIncludes(): void { $container = new ContainerBuilder(); $container->setParameter('container.modules', $module_filenames); $container->setDefinition('module_handler', new Definition()); - (new HookCollectorPassTmpRename())->process($container); + (new HookCollectorPass())->process($container); $argument = $container->getDefinition('module_handler')->getArgument('$groupIncludes'); $this->assertSame(self::GROUP_INCLUDES, $argument); } -- GitLab From dda116bd40bc8f9e9181c3340d42641444f3f6b2 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 18:53:36 +0100 Subject: [PATCH 182/268] Fix test covers docs after rename. --- core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php index 2d6fba3b451f..2bd30610cb19 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php @@ -22,8 +22,8 @@ class HookCollectorPassTest extends UnitTestCase { use GroupIncludesTestTrait; /** - * @covers ::collectAllHookImplementations - * @covers ::filterIterator + * @covers \Drupal\Core\Hook\HookCollector::collectAllHookImplementations + * @covers \Drupal\Core\Hook\HookCollector::filterIterator */ public function testCollectAllHookImplementations(): void { vfsStream::setup('drupal_root'); @@ -72,7 +72,7 @@ function test_module_should_be_skipped(); /** * @covers ::process - * @covers ::collectModuleHookImplementations + * @covers \Drupal\Core\Hook\HookCollector::collectModuleHookImplementations */ public function testGroupIncludes(): void { $module_filenames = self::setupGroupIncludes(); -- GitLab From 4b9a88953227dbc30288ca78d49e474da1444b89 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 17:16:20 +0100 Subject: [PATCH 183/268] Don't pass collectors and containers around. --- .../Drupal/Core/Hook/HookCollectorPass.php | 80 ++++++++----------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index a88591514b37..02d78fe8be4d 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -79,39 +79,42 @@ class HookCollectorPass implements CompilerPassInterface { * {@inheritdoc} */ public function process(ContainerBuilder $container): void { - $collector = static::collectAllHookImplementations($container->getParameter('container.modules'), $container); + $module_list = $container->getParameter('container.modules'); + $parameters = $container->getParameterBag()->all(); + $skip_procedural_modules = array_filter( + array_keys($module_list), + fn (string $module) => !empty($parameters["$module.hooks_converted"]), + ); + $collector = static::collectAllHookImplementations($module_list, $skip_procedural_modules); - $implementationsByHook = $collector->getFilteredImplementations(); + $collector->writeToContainer($container); + } - // Loop over all ReOrderHook attributes and gather order information - // before registering the hooks. This must happen after all collection, - // but before registration to ensure this ordering directive takes - // precedence. - /** @var list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations */ - $hookOrderOperations = array_merge(...$collector->orderAttributesByPhase); - $orderExtraTypes = $collector->getOrderExtraTypes($hookOrderOperations); + /** + * Writes collected definitions to the container builder. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * Container builder. + */ + protected function writeToContainer(ContainerBuilder $container): void { + $orderExtraTypes = $this->getOrderExtraTypes(); $container->register(ProceduralCall::class, ProceduralCall::class) - ->addArgument($collector->includes); + ->addArgument($this->includes); // Gather includes for each hook_hook_info group. // We store this in $groupIncludes so moduleHandler can ensure the files // are included runtime when the hooks are invoked. $groupIncludes = []; - foreach ($collector->hookInfo as $function) { + foreach ($this->hookInfo as $function) { foreach ($function() as $hook => $info) { - if (isset($collector->groupIncludes[$info['group']])) { - $groupIncludes[$hook] = $collector->groupIncludes[$info['group']]; + if (isset($this->groupIncludes[$info['group']])) { + $groupIncludes[$hook] = $this->groupIncludes[$info['group']]; } } } - $implementationsByHook = static::calculateImplementations( - $implementationsByHook, - $collector, - $orderExtraTypes, - $hookOrderOperations, - ); + $implementationsByHook = $this->calculateImplementations($orderExtraTypes); static::writeImplementationsToContainer($container, $implementationsByHook); @@ -139,13 +142,10 @@ protected function getFilteredImplementations(): array { /** * Gets groups of extra hooks from collected data. * - * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations - * All attributes that contain ordering information. - * * @return array<string, list<string>> * Lists of extra hooks keyed by main hook. */ - protected function getOrderExtraTypes(array $hookOrderOperations): array { + protected function getOrderExtraTypes(): array { // Loop over all ReOrderHook attributes and gather order information // before registering the hooks. This must happen after all collection, // but before registration to ensure this ordering directive takes @@ -168,27 +168,16 @@ protected function getOrderExtraTypes(array $hookOrderOperations): array { /** * Calculates the ordered implementations. * - * @param array<string, array<string, string>> $implementationsByHookOrig - * Implementations before ordering, as module names keyed by hook name and - * "$class::$method" identifier. - * All implementations, as method names keyed by hook, module and class. - * @param \Drupal\Core\Hook\HookCollectorPass $collector - * The collector. * @param array<string, list<string>> $orderExtraTypes * Extra types to order a hook with. - * @param list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations - * All attributes that contain ordering information. * * @return array<string, array<string, string>> * Implementations, as module names keyed by hook name and "$class::$method" * identifier. */ - protected static function calculateImplementations( - array $implementationsByHookOrig, - self $collector, - array $orderExtraTypes, - array $hookOrderOperations, - ): array { + protected function calculateImplementations(array $orderExtraTypes): array { + $implementationsByHookOrig = $this->getFilteredImplementations(); + // List of hooks and modules formatted for hook_module_implements_alter(). $moduleImplementsMap = []; foreach ($implementationsByHookOrig as $hook => $hookImplementations) { @@ -206,7 +195,7 @@ protected static function calculateImplementations( $moduleImplements += $moduleImplementsMap[$extraHook] ?? []; } // Process all hook_module_implements_alter() for build time ordering. - foreach ($collector->moduleImplementsAlters as $alter) { + foreach ($this->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); } foreach ($moduleImplements as $module => $v) { @@ -224,6 +213,8 @@ protected static function calculateImplementations( } } + /** @var list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations */ + $hookOrderOperations = array_merge(...$this->orderAttributesByPhase); foreach ($hookOrderOperations as $hookOrderOperation) { static::applyOrderAttributeOperation( $implementationsByHook, @@ -351,8 +342,8 @@ protected static function writeImplementationsToContainer( * @param array $module_filenames * An associative array. Keys are the module names, values are relevant * info yml file path. - * @param \Symfony\Component\DependencyInjection\ContainerBuilder|null $container - * The container. + * @param list<string> $skipProceduralModules + * Module names that are known to not have procedural hook implementations. * * @return static * A HookCollectorPass instance holding all hook implementations and @@ -362,19 +353,16 @@ protected static function writeImplementationsToContainer( * This method is only used by ModuleHandler. * * @todo Pass only $container when ModuleHandler::add() is removed - * @see https://www.drupal.org/project/drupal/issues/3481778 + * @see https://www.drupal.org/project/drupal/issues/3481778 */ - public static function collectAllHookImplementations(array $module_filenames, ?ContainerBuilder $container = NULL): static { + public static function collectAllHookImplementations(array $module_filenames, array $skipProceduralModules = []): static { $modules = array_map(static fn ($x) => preg_quote($x, '/'), array_keys($module_filenames)); // Longer modules first. usort($modules, fn($a, $b) => strlen($b) - strlen($a)); $module_preg = '/^(?<function>(?<module>' . implode('|', $modules) . ')_(?!preprocess_)(?!update_\d)(?<hook>[a-zA-Z0-9_\x80-\xff]+$))/'; $collector = new static(); foreach ($module_filenames as $module => $info) { - $skip_procedural = FALSE; - if ($container?->hasParameter("$module.hooks_converted")) { - $skip_procedural = $container->getParameter("$module.hooks_converted"); - } + $skip_procedural = in_array($module, $skipProceduralModules); $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural); } return $collector; -- GitLab From 6b8d5165c24194f7b028b6cee41c0412b98cacc8 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 7 Mar 2025 18:27:36 +0100 Subject: [PATCH 184/268] Let HookCollector store its own list of module names. --- .../Drupal/Core/Extension/ModuleHandler.php | 2 +- core/lib/Drupal/Core/Hook/HookCollector.php | 26 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 9b6fad985155..e93633aca03b 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -222,7 +222,7 @@ protected function add($type, $name, $path) { // includes. $hook_collector->loadAllIncludes(); // Register procedural implementations. - foreach ($hook_collector->getImplementations($paths) as $hook => $moduleImplements) { + foreach ($hook_collector->getImplementations() as $hook => $moduleImplements) { foreach ($moduleImplements as $module => $classImplements) { foreach ($classImplements[ProceduralCall::class] ?? [] as $method) { $this->invokeMap[$hook][$module][] = $method; diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php index 421ccab6104d..be9528de139a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -82,6 +82,16 @@ class HookCollector { */ protected array $removeHookAttributes = []; + /** + * Constructor. Should not be called directly. + * + * @param list<string> $modules + * Names of installed modules. + */ + protected function __construct( + protected readonly array $modules, + ) {} + /** * Writes collected definitions to the container builder. * @@ -349,14 +359,14 @@ protected static function writeImplementationsToContainer( */ public static function collectAllHookImplementations(array $module_list, array $skipProceduralModules = []): static { $modules = array_keys($module_list); - // Longer modules first. - usort($modules, fn ($a, $b) => strlen($b) - strlen($a)); + $modules_by_length = $modules; + usort($modules_by_length, fn ($a, $b) => strlen($b) - strlen($a)); $known_modules_pattern = implode('|', array_map( static fn ($x) => preg_quote($x, '/'), - $modules, + $modules_by_length, )); $module_preg = '/^(?<function>(?<module>' . $known_modules_pattern . ')_(?!preprocess_)(?!update_\d)(?<hook>[a-zA-Z0-9_\x80-\xff]+$))/'; - $collector = new static(); + $collector = new static($modules); foreach ($module_list as $module => $info) { $skip_procedural = in_array($module, $skipProceduralModules); $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural); @@ -525,9 +535,6 @@ public function loadAllIncludes(): void { /** * This method is only to be used by ModuleHandler. * - * @param array<string, array{pathname: string}> $paths - * Reduced module info arrays by module name. - * * @return array<string, array<string, array<class-string, array<string, string>>>> * Hook implementation method names keyed by hook, module, class and method. * @@ -536,15 +543,14 @@ public function loadAllIncludes(): void { * * @internal */ - public function getImplementations(array $paths): array { + public function getImplementations(): array { $implementationsByHook = $this->getFilteredImplementations(); // List of modules implementing hooks with the implementation details. $implementations = []; - $modules = array_keys($paths); foreach ($implementationsByHook as $hook => $hookImplementations) { - foreach ($modules as $module) { + foreach ($this->modules as $module) { foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { [$class, $method] = explode('::', $identifier); $implementations[$hook][$module][$class][$method] = $method; -- GitLab From de1f34c78b679af2d9563a576de4f12d9af037e6 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 9 Mar 2025 15:57:44 +0100 Subject: [PATCH 185/268] Test changes after merge. --- .../KernelTests/Core/Hook/HookOrderTest.php | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index 9b87ae92b00a..335b71e79095 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -226,19 +226,7 @@ public function testAlterOrder(): void { BAlterHooks::class . '::testAlterAfterCExtra', CAlterHooks::class . '::testAlter', DAlterHooks::class . '::testAlter', - ], 'test', [ - ['test', 'test_unknown'], - ['test_unknown', 'test', 'test_unknown'], - ['test_unknown_1', 'test_unknown_2', 'test'], - ]); - - // Prepending a type changes the order and loses the implementation for 'D'. - // @todo This is probably bad. - $this->assertAlterCallOrder([ - CAlterHooks::class . '::testAlter', - AAlterHooks::class . '::testAlterAfterC', - BAlterHooks::class . '::testAlterAfterCExtra', - ], ['test_other_unknown', 'test'], []); + ], 'test'); $this->assertAlterCallOrder([ AAlterHooks::class . '::testSubtypeAlter', @@ -249,15 +237,6 @@ public function testAlterOrder(): void { $this->assertAlterCallOrder([ // The implementation from 'D' is gone. - CAlterHooks::class . '::testAlter', - CAlterHooks::class . '::testSubtypeAlter', - AAlterHooks::class . '::testAlterAfterC', - AAlterHooks::class . '::testSubtypeAlter', - BAlterHooks::class . '::testAlterAfterCExtra', - BAlterHooks::class . '::testSubtypeAlter', - ], ['test', 'test_subtype'], []); - - $this->assertAlterCallOrder([ AAlterHooks::class . '::testAlterAfterC', AAlterHooks::class . '::testSubtypeAlter', BAlterHooks::class . '::testAlterAfterCExtra', @@ -266,7 +245,7 @@ public function testAlterOrder(): void { CAlterHooks::class . '::testSubtypeAlter', DAlterHooks::class . '::testAlter', DAlterHooks::class . '::testSubtypeAlter', - ], ['test', 'test_subtype', 'test_other'], []); + ], ['test', 'test_subtype']); $this->disableModules(['hk_b_test']); @@ -302,14 +281,16 @@ public function testFormAlterOrder(): void { ], $this->alter('form')['#calls'] ?? NULL); $this->assertSameCallList([ - BFormAlterHooks::class . '::formAlter', - BFormAlterHooks::class . '::myFormAlter', AFormAlterHooks::class . '::formAlter', AFormAlterHooks::class . '::formAlterAfterB', AFormAlterHooks::class . '::formAlterAfterBExtra', AFormAlterHooks::class . '::myFormAlter', AFormAlterHooks::class . '::myFormAlterAfterB', AFormAlterHooks::class . '::myFormAlterAfterBExtra', + BFormAlterHooks::class . '::formAlter', + BFormAlterHooks::class . '::myFormAlter', + CFormAlterHooks::class . '::formAlter', + CFormAlterHooks::class . '::myFormAlter', ], $this->alter(['form', 'form_myform'])['#calls'] ?? NULL); } -- GitLab From 87258105a88938788283040f04437ef138c28669 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 11 Mar 2025 03:15:42 +0100 Subject: [PATCH 186/268] Drop extra hooks in order attribute, order at call time. --- .../Drupal/Core/Extension/ModuleHandler.php | 249 ++++++++++-------- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 41 +-- .../Core/Hook/Attribute/ReOrderHook.php | 21 +- .../Drupal/Core/Hook/Attribute/RemoveHook.php | 14 +- core/lib/Drupal/Core/Hook/ComplexOrder.php | 64 ----- .../Core/Hook/HookAttributeInterface.php | 14 + core/lib/Drupal/Core/Hook/HookCollector.php | 218 +++++++-------- core/lib/Drupal/Core/Hook/HookOperation.php | 36 --- core/lib/Drupal/Core/Hook/Order.php | 9 +- core/lib/Drupal/Core/Hook/OrderAfter.php | 8 +- core/lib/Drupal/Core/Hook/OrderBefore.php | 8 +- core/lib/Drupal/Core/Hook/OrderInterface.php | 21 ++ .../OrderOperation/AbsoluteOrderOperation.php | 41 +++ .../OrderOperationInterface.php | 20 ++ .../OrderOperation/RelativeOrderOperation.php | 75 ++++++ .../Drupal/Core/Hook/RelativeOrderBase.php | 56 ++++ .../ckeditor5/src/Hook/Ckeditor5Hooks.php | 1 - .../HookOrder/hk_a_test/hk_a_test.module | 9 +- .../hk_a_test/src/Hook/AAlterHooks.php | 3 + .../hk_a_test/src/Hook/AFormAlterHooks.php | 17 +- .../HookOrder/hk_a_test/src/Hook/AHooks.php | 11 +- .../HookOrder/hk_b_test/hk_b_test.module | 9 +- .../hk_b_test/src/Hook/BAlterHooks.php | 9 +- .../hk_b_test/src/Hook/BFormAlterHooks.php | 5 +- .../HookOrder/hk_b_test/src/Hook/BHooks.php | 5 +- .../HookOrder/hk_c_test/hk_c_test.module | 9 +- .../hk_c_test/src/Hook/CAlterHooks.php | 3 + .../hk_c_test/src/Hook/CFormAlterHooks.php | 5 +- .../HookOrder/hk_c_test/src/Hook/CHooks.php | 11 +- .../HookOrder/hk_d_test/hk_d_test.module | 9 +- .../hk_d_test/src/Hook/DAlterHooks.php | 3 + .../HookOrder/hk_d_test/src/Hook/DHooks.php | 9 +- .../src/Hook/TestHookOrderExtraTypes.php | 1 - .../KernelTests/Core/Hook/HookOrderTest.php | 39 ++- 34 files changed, 591 insertions(+), 462 deletions(-) delete mode 100644 core/lib/Drupal/Core/Hook/ComplexOrder.php create mode 100644 core/lib/Drupal/Core/Hook/HookAttributeInterface.php delete mode 100644 core/lib/Drupal/Core/Hook/HookOperation.php create mode 100644 core/lib/Drupal/Core/Hook/OrderInterface.php create mode 100644 core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php create mode 100644 core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php create mode 100644 core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php create mode 100644 core/lib/Drupal/Core/Hook/RelativeOrderBase.php diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index e93633aca03b..4eb0bbe255a5 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -7,6 +7,7 @@ use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\HookCollector; +use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -58,6 +59,22 @@ class ModuleHandler implements ModuleHandlerInterface { */ protected $includeFileKeys = []; + /** + * Lists of implementation callables by hook. + * + * @var array<string, list<callable>> + */ + protected array $listenersByHook = []; + + /** + * Lists of module names by hook. + * + * The indices are exactly the same as in $listenersByHook. + * + * @var array<string, list<string>> + */ + protected array $modulesByHook = []; + /** * Hook and module keyed list of listeners. * @@ -65,6 +82,13 @@ class ModuleHandler implements ModuleHandlerInterface { */ protected array $invokeMap = []; + /** + * Ordering rules by hook name. + * + * @var array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>> + */ + protected array $orderingRules = []; + /** * Constructs a ModuleHandler object. * @@ -81,11 +105,8 @@ class ModuleHandler implements ModuleHandlerInterface { * @param array<string, list<string>> $groupIncludes * Lists of *.inc file paths that contain procedural implementations, keyed * by hook name. - * @param array<string, list<string>> $orderedExtraTypes - * A multidimensional array of hooks that have been ordered and the - * extra_types they have been ordered against. This is stored separately - * from $hookImplementationsMap to prevent ordering again since this set - * has already been fully ordered in HookCollectorPass. + * @param ?string $serialized_ordering_rules + * Ordering rules by hook name, serialized. * * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider @@ -96,8 +117,16 @@ public function __construct( protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], - protected array $orderedExtraTypes = [], + ?string $serialized_ordering_rules = NULL, ) { + if ($serialized_ordering_rules !== NULL) { + $this->orderingRules = unserialize($serialized_ordering_rules); + foreach ($this->orderingRules as $rules) { + foreach ($rules as $rule) { + assert($rule instanceof OrderOperationInterface); + } + } + } $this->root = $root; $this->moduleList = []; foreach ($module_list as $name => $module) { @@ -225,6 +254,9 @@ protected function add($type, $name, $path) { foreach ($hook_collector->getImplementations() as $hook => $moduleImplements) { foreach ($moduleImplements as $module => $classImplements) { foreach ($classImplements[ProceduralCall::class] ?? [] as $method) { + // @todo Reorder these after adding! + $this->listenersByHook[$hook][] = $method; + $this->modulesByHook[$hook][] = $module; $this->invokeMap[$hook][$module][] = $method; } } @@ -324,10 +356,9 @@ public function hasImplementations(string $hook, $modules = NULL): bool { * {@inheritdoc} */ public function invokeAllWith(string $hook, callable $callback): void { - foreach ($this->getHookListeners($hook) as $module => $listeners) { - foreach ($listeners as $listener) { - $callback($listener, $module); - } + foreach ($this->getFlatHookListeners($hook) as $i => $listener) { + $module = $this->modulesByHook[$hook][$i]; + $callback($listener, $module); } } @@ -429,14 +460,6 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // specific variants of it, as in the case of ['form', 'form_FORM_ID']. if (is_array($type)) { $cid = implode(',', $type); - $extra_types = $type; - $type = array_shift($extra_types); - // Allow if statements in this function to use the faster isset() rather - // than !empty() both when $type is passed as a string, or as an array - // with one item. - if (empty($extra_types)) { - unset($extra_types); - } } else { $cid = $type; @@ -446,47 +469,10 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // list of functions to call, and on subsequent calls, iterate through them // quickly. if (!isset($this->alterEventListeners[$cid])) { - $this->alterEventListeners[$cid] = []; - $hook = $type . '_alter'; - $hook_listeners = $this->getHookListeners($hook); - $extra_modules = FALSE; - $extra_listeners = []; - if (isset($extra_types)) { - $extra_hooks = array_map(static fn ($x) => $x . '_alter', $extra_types); - // First get the listeners implementing extra hooks. - foreach ($extra_hooks as $extra_hook) { - $hook_listeners = $this->findListenersForAlter($extra_hook, $hook_listeners, $extra_modules); - } - // Second, gather implementations ordered together. These are only used - // for ordering because the set might contain hooks not included in - // this alter() call. \Drupal\Core\Hook\HookPriority::change() - // registers the implementations of combined hooks. - foreach ([...$extra_hooks, $hook] as $extra_hook) { - if (isset($this->orderedExtraTypes[$extra_hook])) { - $orderedHooks = $this->orderedExtraTypes[$extra_hook]; - $extra_listeners = $this->findListenersForAlter(implode(':', $orderedHooks)); - // Remove already ordered hooks. - $extra_hooks = array_diff($extra_hooks, $orderedHooks); - } - } - } - // If multiple alters were called, but they were already ordered by - // ordering attributes then keep that order. - if (isset($extra_hooks) && empty($extra_hooks)) { - $modules = array_keys(array_intersect_key($extra_listeners, $hook_listeners)); - } - else { - // Otherwise, use a legacy ordering mechanism if needed. - $modules = array_keys($hook_listeners); - if ($extra_modules) { - $modules = $this->legacyReOrderModulesForAlter($modules, $hook); - } - } - foreach ($modules as $module) { - foreach ($hook_listeners[$module] ?? [] as $listener) { - $this->alterEventListeners[$cid][] = $listener; - } - } + $hooks = is_array($type) + ? array_map(fn (string $type) => $type . '_alter', $type) + : [$type . '_alter']; + $this->alterEventListeners[$cid] = $this->getCombinedListeners(...$hooks); } foreach ($this->alterEventListeners[$cid] as $listener) { $listener($data, $context1, $context2); @@ -494,29 +480,78 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { } /** - * Reorder modules for alters. + * Builds a list of listeners for an alter hook. * - * @param list<string> $modules - * A list of modules. - * @param string $hook - * The hook being worked on, for example 'form_alter'. + * @param string $main_hook + * The primary alter hook, e.g. 'form_alter'. + * @param string ...$extra_hooks + * Additional alter hooks, e.g. 'form_FORM_ID_alter'. * - * @return list<string> - * The list, potentially reordered and changed by - * hook_module_implements_alter(). + * @return list<callable> + * List of implementation callables. */ - protected function legacyReOrderModulesForAlter(array $modules, string $hook): array { - // Order by module order first. - $modules = array_intersect(array_keys($this->moduleList), $modules); - // Alter expects the module list to be in the keys. - $implementations = array_fill_keys($modules, FALSE); - // Let modules adjust the order solely based on the primary hook. This - // ensures the same module order regardless of whether this block - // runs. Calling $this->alter() recursively in this way does not - // result in an infinite loop, because this call is for a single - // $type, so we won't end up in this method again. - $this->alter('module_implements', $implementations, $hook); - return array_keys($implementations); + protected function getCombinedListeners(string $main_hook, string ...$extra_hooks): array { + if (!$extra_hooks) { + return $this->getFlatHookListeners($main_hook); + } + $listeners_by_module = $this->getHookListeners($main_hook); + foreach ($extra_hooks as $extra_hook) { + foreach ($this->getHookListeners($extra_hook) as $module => $extra_listeners) { + foreach ($extra_listeners as $extra_listener) { + $listeners_by_module[$module][] = $extra_listener; + } + } + } + $modules = array_intersect( + array_keys($this->moduleList), + array_keys($listeners_by_module), + ); + $module_implements = array_fill_keys($modules, FALSE); + $this->alter('module_implements', $module_implements, $main_hook); + $listeners_by_identifier = []; + $modules_by_identifier = []; + foreach (array_keys($module_implements) as $module) { + foreach ($listeners_by_module[$module] ?? [] as $listener) { + $identifier = is_array($listener) + ? get_class($listener[0]) . '::' . $listener[1] + : ProceduralCall::class . '::' . $listener; + if (isset($listeners_by_identifier[$identifier])) { + throw new \LogicException(sprintf( + 'The hook implementation %s is registered for more than one hook. This is not allowed for hooks that are called together, in this case %s.', + $identifier, + json_encode([$main_hook, ...$extra_hooks]))); + } + $listeners_by_identifier[$identifier] = $listener; + $modules_by_identifier[$identifier] = $module; + } + } + $identifiers = array_keys($listeners_by_identifier); + foreach ([$main_hook, ...$extra_hooks] as $hook) { + foreach ($this->orderingRules[$hook] ?? [] as $rule) { + $rule->apply($identifiers, $modules_by_identifier); + assert($identifiers === array_unique($identifiers)); + $identifiers = array_values($identifiers); + assert(!array_diff($identifiers, array_keys($modules_by_identifier))); + assert(!array_diff(array_keys($modules_by_identifier), $identifiers)); + $lost_identifiers = array_diff(array_keys($modules_by_identifier), $identifiers); + assert(!$lost_identifiers, sprintf( + "Lost identifiers for\n%s:\n%s", + var_export($rule, TRUE), + json_encode($lost_identifiers), + )); + $added_identifiers = array_diff($identifiers, array_keys($modules_by_identifier)); + assert(!$added_identifiers, sprintf( + 'Added identifiers for\n%s:\n%s', + var_export($rule, TRUE), + json_encode($added_identifiers), + )); + } + } + $identifiers = array_values(array_unique($identifiers)); + return array_map( + fn (string $identifier) => $listeners_by_identifier[$identifier], + $identifiers, + ); } /** @@ -574,6 +609,8 @@ public function writeCache() { } /** + * Gets hook listeners by module. + * * @param string $hook * The name of the hook. * @@ -582,6 +619,27 @@ public function writeCache() { */ protected function getHookListeners(string $hook): array { if (!isset($this->invokeMap[$hook])) { + $this->invokeMap[$hook] = []; + foreach ($this->getFlatHookListeners($hook) as $i => $listener) { + $module = $this->modulesByHook[$hook][$i]; + $this->invokeMap[$hook][$module][] = $listener; + } + } + + return $this->invokeMap[$hook] ?? []; + } + + /** + * Gets a list of hook listener callbacks. + * + * @param string $hook + * The hook name. + * + * @return list<callable> + * A list of hook implementation callables. + */ + protected function getFlatHookListeners(string $hook): array { + if (!isset($this->listenersByHook[$hook])) { foreach ($this->eventDispatcher->getListeners("drupal_hook.$hook") as $listener) { if (is_array($listener) && is_object($listener[0])) { $module = $this->hookImplementationsMap[$hook][get_class($listener[0])][$listener[1]]; @@ -594,7 +652,8 @@ protected function getHookListeners(string $hook): array { $callable = $listener; } if (isset($this->moduleList[$module])) { - $this->invokeMap[$hook][$module][] = $callable; + $this->listenersByHook[$hook][] = $callable; + $this->modulesByHook[$hook][] = $module; } } } @@ -606,35 +665,7 @@ protected function getHookListeners(string $hook): array { } } - return $this->invokeMap[$hook] ?? []; - } - - /** - * Helper to get hook listeners when in alter. - * - * @param string $hook - * The extra hook or combination hook to check for. - * @param array<string, list<callable>> $hook_listeners - * Hook listeners for the current hook_alter. - * @param bool|null $extra_modules - * Whether there are extra modules to order. - * - * @return array<string, list<callable>> - * The hook listeners. - */ - public function findListenersForAlter(string $hook, array $hook_listeners = [], ?bool &$extra_modules = NULL): array { - foreach ($this->getHookListeners($hook) as $module => $listeners) { - if (isset($hook_listeners[$module])) { - $hook_listeners[$module] = array_merge($hook_listeners[$module], $listeners); - } - else { - $hook_listeners[$module] = $listeners; - // It is used by reference. - // @phpcs:ignore DrupalPractice.CodeAnalysis.VariableAnalysis.UnusedVariable - $extra_modules = TRUE; - } - } - return $hook_listeners; + return $this->listenersByHook[$hook] ?? []; } } diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 0a4c5b5cdec9..f41c8b752286 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -4,9 +4,8 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\ComplexOrder; -use Drupal\Core\Hook\HookOperation; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\HookAttributeInterface; +use Drupal\Core\Hook\OrderInterface; /** * Attribute for defining a class method as a hook implementation. @@ -101,7 +100,7 @@ * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class Hook extends HookOperation { +class Hook implements HookAttributeInterface { /** * Constructs a Hook attribute object. @@ -117,38 +116,14 @@ class Hook extends HookOperation { * (optional) The module this implementation is for. This allows one module * to implement a hook on behalf of another module. Defaults to the module * the implementation is in. - * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order + * @param \Drupal\Core\Hook\OrderInterface|null $order * (optional) Set the order of the implementation. */ public function __construct( - string $hook, - string $method = '', + public string $hook, + public string $method = '', public ?string $module = NULL, - Order|ComplexOrder|null $order = NULL, - ) { - parent::__construct($hook, $method, order: $order); - } - - /** - * Set necessary parameters for the hook attribute. - * - * @param class-string $class - * The class for the hook. - * @param string $module - * The module for the hook. - * @param string $method - * The method for the hook. - */ - public function set(string $class, string $module, string $method): void { - if (!$this->class) { - $this->class = $class; - } - if (!$this->module) { - $this->module = $module; - } - if (!$this->method) { - $this->method = $method; - } - } + public OrderInterface|null $order = NULL, + ) {} } diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index 1f88da580f4b..0f22afc322c0 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -4,9 +4,8 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\ComplexOrder; -use Drupal\Core\Hook\HookOperation; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\HookAttributeInterface; +use Drupal\Core\Hook\OrderInterface; /** * Set the order of an already existing implementation. @@ -14,7 +13,7 @@ * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class ReOrderHook extends HookOperation { +class ReOrderHook implements HookAttributeInterface { /** * Constructs a ReOrderHook object. @@ -26,16 +25,14 @@ class ReOrderHook extends HookOperation { * @param string $method * The method name of the #Hook being modified. If the hook attribute is * on a class and does not have method set, then use __invoke. - * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder $order + * @param \Drupal\Core\Hook\OrderInterface $order * Set the order of the implementation. */ public function __construct( - string $hook, - string $class, - string $method, - Order|ComplexOrder $order, - ) { - parent::__construct($hook, $method, $class, $order); - } + public string $hook, + public string $class, + public string $method, + public OrderInterface $order, + ) {} } diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index 5b4cca3b132a..e75baa8960cb 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookOperation; +use Drupal\Core\Hook\HookAttributeInterface; /** * Attribute for removing an implementation. @@ -12,7 +12,7 @@ * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class RemoveHook extends HookOperation { +class RemoveHook implements HookAttributeInterface { /** * Constructs a RemoveHook object. @@ -26,11 +26,9 @@ class RemoveHook extends HookOperation { * on a class and does not have method set, then use __invoke. */ public function __construct( - string $hook, - string $class, - string $method, - ) { - parent::__construct($hook, $method, $class); - } + public readonly string $hook, + public readonly string $class, + public readonly string $method, + ) {} } diff --git a/core/lib/Drupal/Core/Hook/ComplexOrder.php b/core/lib/Drupal/Core/Hook/ComplexOrder.php deleted file mode 100644 index a44e0bd2e815..000000000000 --- a/core/lib/Drupal/Core/Hook/ComplexOrder.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook; - -/** - * Set this implementation to be before or after others. - */ -abstract readonly class ComplexOrder { - - /** - * Whether the priority of this hook should be larger than others. - */ - const bool VALUE = FALSE; - - /** - * Whether the priority of this hook should be larger than others. - * - * This is fixed to the constant ::VALUE, it simplifies ordering by ensuring - * ComplexOrder and Order types both have a value property. - * - * @var bool - */ - public bool $value; - - /** - * Constructs a ComplexOrder object. - * - * @param list<string> $modules - * A list of modules. - * @param list<array{class-string, string}> $classesAndMethods - * A list of classes and methods, for example: - * @code - * [ - * [Foo::class, 'someMethod'], - * [Bar::class, 'someOtherMethod'], - * ] - * @endcode - * @param list<string> $extraTypes - * A list of hooks to be ordered together. Ordering by attributes happens - * at build time by setting up the order of the listeners of a hook - * correctly. However, ModuleHandlerInterface::alter() can be called with - * multiple hooks runtime. If the hook defined on this method/class - * requires ordering relative to other such hooks then this parameter can - * be used to order relative to implementations of all hooks in the set. - * Include all alter hooks to be ordered against in the set even if no - * single alter() call includes all of them. For example, this can be used - * to order a hook_form_BASE_FORM_ID_alter() implementation relative to - * multiple hook_form_FORM_ID_alter() implementations as - * Drupal\ckeditor5\Hook\Ckeditor5Hooks::formFilterFormatFormAlter() does. - */ - public function __construct( - public array $modules = [], - public array $classesAndMethods = [], - public array $extraTypes = [], - ) { - if (!$this->modules && !$this->classesAndMethods) { - throw new \LogicException('Order must provide either modules or class-method pairs to order against.'); - } - $this->value = static::VALUE; - } - -} diff --git a/core/lib/Drupal/Core/Hook/HookAttributeInterface.php b/core/lib/Drupal/Core/Hook/HookAttributeInterface.php new file mode 100644 index 000000000000..d96db307ea14 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/HookAttributeInterface.php @@ -0,0 +1,14 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +/** + * Common interface for attributes used for hook discovery. + * + * @internal + */ +interface HookAttributeInterface { + +} diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php index be9528de139a..a0efd03d2252 100644 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -66,21 +66,37 @@ class HookCollector { private array $groupIncludes = []; /** - * Implementations, as module names keyed by hook name and "$class::$method". + * OOP implementation module names keyed by hook name and "$class::$method". * * @var array<string, array<string, string>> */ - protected array $implementations = []; + protected array $oopImplementations = []; /** - * @var array<int, list<\Drupal\Core\Hook\HookOperation>> + * Procedural implementation module names by hook name. + * + * @var array<string, list<string>> + */ + protected array $proceduralImplementations = []; + + /** + * Order operations grouped by hook name and weight. + * + * Operations with higher weight are applied last, which means they can + * override the changes from previous operations. + * + * @var array<string, array<int, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>>> + * + * @todo Review how to combine operations from different hooks. */ - protected array $orderAttributesByPhase = [0 => [], 1 => []]; + protected array $orderOperations = []; /** - * @var list<\Drupal\Core\Hook\Attribute\RemoveHook> + * Identifiers to remove, as "$class::$method", keyed by hook name. + * + * @var array<string, list<string>> */ - protected array $removeHookAttributes = []; + protected array $removeHookIdentifiers = []; /** * Constructor. Should not be called directly. @@ -99,8 +115,6 @@ protected function __construct( * Container builder. */ public function writeToContainer(ContainerBuilder $container): void { - $orderExtraTypes = $this->getOrderExtraTypes(); - $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($this->includes); @@ -116,14 +130,14 @@ public function writeToContainer(ContainerBuilder $container): void { } } - $implementationsByHook = $this->calculateImplementations($orderExtraTypes); + $implementationsByHook = $this->calculateImplementations(); static::writeImplementationsToContainer($container, $implementationsByHook); // Update the module handler definition. $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$orderedExtraTypes', $orderExtraTypes); + $definition->setArgument('$serialized_ordering_rules', serialize($this->getOrderOperations())); } /** @@ -134,68 +148,51 @@ public function writeToContainer(ContainerBuilder $container): void { * "$class::$method". */ protected function getFilteredImplementations(): array { - $implementationsByHook = $this->implementations; - foreach ($this->removeHookAttributes as $removeHook) { - unset($implementationsByHook[$removeHook->hook][$removeHook->class . '::' . $removeHook->method]); + $implementationsByHook = []; + foreach ($this->proceduralImplementations as $hook => $procedural_modules) { + foreach ($procedural_modules as $module) { + $implementationsByHook[$hook][ProceduralCall::class . '::' . $module . '_' . $hook] = $module; + } } - return $implementationsByHook; - } - - /** - * Gets groups of extra hooks from collected data. - * - * @return array<string, list<string>> - * Lists of extra hooks keyed by main hook. - */ - protected function getOrderExtraTypes(): array { - // Loop over all ReOrderHook attributes and gather order information - // before registering the hooks. This must happen after all collection, - // but before registration to ensure this ordering directive takes - // precedence. - /** @var list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations */ - $hookOrderOperations = array_merge(...$this->orderAttributesByPhase); - $orderExtraTypes = []; - foreach ($hookOrderOperations as $hookWithOrder) { - if ($hookWithOrder->order instanceof ComplexOrder && $hookWithOrder->order->extraTypes) { - $extraTypes = [... $hookWithOrder->order->extraTypes, $hookWithOrder->hook]; - foreach ($extraTypes as $extraHook) { - $orderExtraTypes[$extraHook] = array_merge($orderExtraTypes[$extraHook] ?? [], $extraTypes); - } + foreach ($this->oopImplementations as $hook => $oopImplementations) { + if (!isset($implementationsByHook[$hook])) { + $implementationsByHook[$hook] = $oopImplementations; + } + else { + $implementationsByHook[$hook] += $oopImplementations; } } - $orderExtraTypes = array_map('array_unique', $orderExtraTypes); - return array_map('array_values', $orderExtraTypes); + foreach ($this->removeHookIdentifiers as $hook => $identifiers_to_remove) { + foreach ($identifiers_to_remove as $identifier_to_remove) { + unset($implementationsByHook[$hook][$identifier_to_remove]); + } + if (empty($implementationsByHook[$hook])) { + unset($implementationsByHook[$hook]); + } + } + return $implementationsByHook; } /** * Calculates the ordered implementations. * - * @param array<string, list<string>> $orderExtraTypes - * Extra types to order a hook with. - * * @return array<string, array<string, string>> * Implementations, as module names keyed by hook name and "$class::$method" * identifier. */ - protected function calculateImplementations(array $orderExtraTypes): array { + protected function calculateImplementations(): array { $implementationsByHookOrig = $this->getFilteredImplementations(); // List of hooks and modules formatted for hook_module_implements_alter(). $moduleImplementsMap = []; foreach ($implementationsByHookOrig as $hook => $hookImplementations) { - foreach ($hookImplementations as $module) { + foreach (array_intersect($this->modules, $hookImplementations) as $module) { $moduleImplementsMap[$hook][$module] = ''; } } $implementationsByHook = []; foreach ($moduleImplementsMap as $hook => $moduleImplements) { - $extraHooks = $orderExtraTypes[$hook] ?? []; - // Add implementations to the array we pass to legacy ordering - // when the definition specifies that they should be ordered together. - foreach ($extraHooks as $extraHook) { - $moduleImplements += $moduleImplementsMap[$extraHook] ?? []; - } // Process all hook_module_implements_alter() for build time ordering. foreach ($this->moduleImplementsAlters as $alter) { $alter($moduleImplements, $hook); @@ -204,92 +201,52 @@ protected function calculateImplementations(array $orderExtraTypes): array { foreach (array_keys($implementationsByHookOrig[$hook], $module, TRUE) as $identifier) { $implementationsByHook[$hook][$identifier] = $module; } - if (count($extraHooks) > 1) { - $combinedHook = implode(':', $extraHooks); - foreach ($extraHooks as $extraHook) { - foreach (array_keys($implementationsByHookOrig[$extraHook] ?? [], $module, TRUE) as $identifier) { - $implementationsByHook[$combinedHook][$identifier] = $module; - } - } - } } } - /** @var list<\Drupal\Core\Hook\HookOperation> $hookOrderOperations */ - $hookOrderOperations = array_merge(...$this->orderAttributesByPhase); - foreach ($hookOrderOperations as $hookOrderOperation) { - static::applyOrderAttributeOperation( - $implementationsByHook, - $orderExtraTypes, - $hookOrderOperation, - ); + foreach ($this->getOrderOperations() as $hook => $order_operations) { + self::applyOrderOperations($implementationsByHook[$hook], $order_operations); } return $implementationsByHook; } /** - * Applies hook order changes from a single attribute with order information. + * Gets order operations by hook. * - * @param array<string, array<string, string>> $implementationsByHook - * Implementations, as module names keyed by hook name and "$class::$method" - * identifier. - * @param array<string, list<string>> $orderExtraTypes - * Extra types to order a hook with. - * @param \Drupal\Core\Hook\HookOperation $hookOrderOperation - * Hook attribute with order information. + * @return array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>> + * Order operations by hook name. */ - protected static function applyOrderAttributeOperation( - array &$implementationsByHook, - array $orderExtraTypes, - HookOperation $hookOrderOperation, - ): void { - // ::process() adds the hook serving as key to the order extraTypes so it - // does not need to be added if there's a extraTypes for the hook. - $hooks = $orderExtraTypes[$hookOrderOperation->hook] ?? [$hookOrderOperation->hook]; - $combinedHook = implode(':', $hooks); - $identifier = $hookOrderOperation->class . '::' . $hookOrderOperation->method; - $module = $implementationsByHook[$combinedHook][$identifier] ?? NULL; - if ($module === NULL) { - // Implementation is not in the list. Nothing to reorder. - return; + protected function getOrderOperations(): array { + $operations_by_hook = []; + foreach ($this->orderOperations as $hook => $order_operations_by_weight) { + ksort($order_operations_by_weight); + $operations_by_hook[$hook] = array_merge(...$order_operations_by_weight); } - $list = $implementationsByHook[$combinedHook]; - $order = $hookOrderOperation->order; - if ($order === NULL) { - throw new \InvalidArgumentException('This method must only be called with attributes that have order information.'); - } - if ($order === Order::First) { - unset($list[$identifier]); - $list = [$identifier => $module] + $list; - } - elseif ($order === Order::Last) { - unset($list[$identifier]); - $list[$identifier] = $module; - } - elseif ($order instanceof ComplexOrder) { - $shouldBeAfter = !$order->value; - unset($list[$identifier]); - $identifiers = array_keys($list); - $modules = array_values($list); - $compareIndices = []; - if (isset($hookOrderOperation->order->modules)) { - $compareIndices = array_keys(array_intersect($modules, $hookOrderOperation->order->modules)); - } - foreach ($hookOrderOperation->order->classesAndMethods as [$otherClass, $otherMethod]) { - $compareIndices[] = array_search("$otherClass::$otherMethod", $identifiers, TRUE); - } - if (!$compareIndices) { - return; - } - $splice_index = $shouldBeAfter - ? max($compareIndices) + 1 - : min($compareIndices); - array_splice($identifiers, $splice_index, 0, [$identifier]); - array_splice($modules, $splice_index, 0, [$module]); - $list = array_combine($identifiers, $modules); + return $operations_by_hook; + } + + /** + * Applies order operations to a hook implementation list. + * + * @param array<string, string> $implementation_list + * Implementation list for one hook, as module names keyed by + * "$class::$method" identifiers. + * @param list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface> $order_operations + * A list of order operations for one hook. + */ + public static function applyOrderOperations(array &$implementation_list, array $order_operations): void { + $module_finder = $implementation_list; + $identifiers = array_keys($module_finder); + foreach ($order_operations as $order_operation) { + $order_operation->apply($identifiers, $module_finder); + assert($identifiers === array_unique($identifiers)); + $identifiers = array_values($identifiers); } - $implementationsByHook[$combinedHook] = $list; + // Clean up after bad order operations. + $identifiers = array_combine($identifiers, $identifiers); + $identifiers = array_intersect_key($identifiers, $module_finder); + $implementation_list = array_replace($identifiers, $module_finder); } /** @@ -427,17 +384,16 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, foreach ($methodAttributes as $attribute) { if ($attribute instanceof Hook) { self::checkForProceduralOnlyHooks($attribute, $class); - $this->implementations[$attribute->hook][$class . '::' . ($attribute->method ?: $method)] = $attribute->module ?? $module; + $this->oopImplementations[$attribute->hook][$class . '::' . ($attribute->method ?: $method)] = $attribute->module ?? $module; if ($attribute->order !== NULL) { - $attribute->set($class, $attribute->module ?? $module, $method); - $this->orderAttributesByPhase[0][] = $attribute; + $this->orderOperations[$attribute->hook][0][] = $attribute->order->getOperation("$class::$method"); } } elseif ($attribute instanceof ReOrderHook) { - $this->orderAttributesByPhase[1][] = $attribute; + $this->orderOperations[$attribute->hook][1][] = $attribute->order->getOperation($attribute->class . '::' . $attribute->method); } elseif ($attribute instanceof RemoveHook) { - $this->removeHookAttributes[] = $attribute; + $this->removeHookIdentifiers[$attribute->hook][] = $attribute->class . '::' . $attribute->method; } } } @@ -453,6 +409,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, break; } if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches) && !StaticReflectionParser::hasAttribute($attributes, LegacyModuleImplementsAlter::class)) { + assert($function === $matches['module'] . '_' . $matches['hook']); $implementations[] = ['function' => $function, 'module' => $matches['module'], 'hook' => $matches['hook']]; } } @@ -504,6 +461,7 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv * The name of function implementing the hook. */ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { + assert($function === $module . '_' . $hook); if ($hook === 'hook_info') { $this->hookInfo[] = $function; } @@ -512,7 +470,7 @@ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $h @trigger_error($message, E_USER_DEPRECATED); $this->moduleImplementsAlters[] = $function; } - $this->implementations[$hook][ProceduralCall::class . '::' . $module . '_' . $hook] = $module; + $this->proceduralImplementations[$hook][] = $module; if ($fileinfo->getExtension() !== 'module') { $this->includes[$function] = $fileinfo->getPathname(); } @@ -593,7 +551,7 @@ public static function checkForProceduralOnlyHooks(Hook $hookAttribute, string $ * @param \ReflectionClass $reflectionClass * A reflected class. * - * @return array<string, list<\Drupal\Core\Hook\HookOperation>> + * @return array<string, list<\Drupal\Core\Hook\HookAttributeInterface>> * Lists of Hook attribute instances by method name. */ protected static function getAttributeInstances(\ReflectionClass $reflectionClass): array { @@ -601,7 +559,7 @@ protected static function getAttributeInstances(\ReflectionClass $reflectionClas $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); $reflections[] = $reflectionClass; foreach ($reflections as $reflection) { - if ($reflectionAttributes = $reflection->getAttributes(HookOperation::class, \ReflectionAttribute::IS_INSTANCEOF)) { + if ($reflectionAttributes = $reflection->getAttributes(HookAttributeInterface::class, \ReflectionAttribute::IS_INSTANCEOF)) { $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; $attributes[$method] = array_map(static fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflectionAttributes); } diff --git a/core/lib/Drupal/Core/Hook/HookOperation.php b/core/lib/Drupal/Core/Hook/HookOperation.php deleted file mode 100644 index f212588a42c6..000000000000 --- a/core/lib/Drupal/Core/Hook/HookOperation.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook; - -/** - * Base class for attributes that affect or define hook implementations. - * - * @internal - */ -abstract class HookOperation { - - /** - * Constructs a HookOperation object. - * - * @param string $hook - * The hook being implemented or modified. - * @param string $method - * The method for the hook being implemented or modified. - * This is required when modifying existing hook implementations it is - * optional otherwise. See \Drupal\Core\Hook\Attribute\Hook for more - * information. - * @param class-string $class - * (optional) The class of the hook being implemented or modified. - * @param \Drupal\Core\Hook\Order|\Drupal\Core\Hook\ComplexOrder|null $order - * (optional) Set the order of the hook referenced. - */ - public function __construct( - public string $hook, - public string $method, - public ?string $class = NULL, - public Order|ComplexOrder|null $order = NULL, - ) {} - -} diff --git a/core/lib/Drupal/Core/Hook/Order.php b/core/lib/Drupal/Core/Hook/Order.php index f26ef257db72..413f5d479e92 100644 --- a/core/lib/Drupal/Core/Hook/Order.php +++ b/core/lib/Drupal/Core/Hook/Order.php @@ -4,10 +4,13 @@ namespace Drupal\Core\Hook; +use Drupal\Core\Hook\OrderOperation\AbsoluteOrderOperation; +use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; + /** * Set this implementation to be first or last. */ -enum Order: int { +enum Order: int implements OrderInterface { // This implementation should fire first. case First = 1; @@ -15,4 +18,8 @@ enum Order: int { // This implementation should fire last. case Last = 0; + public function getOperation(string $identifier): OrderOperationInterface { + return new AbsoluteOrderOperation($identifier, $this === self::Last); + } + } diff --git a/core/lib/Drupal/Core/Hook/OrderAfter.php b/core/lib/Drupal/Core/Hook/OrderAfter.php index be7811c1b79c..2a8ae3b09c92 100644 --- a/core/lib/Drupal/Core/Hook/OrderAfter.php +++ b/core/lib/Drupal/Core/Hook/OrderAfter.php @@ -7,11 +7,13 @@ /** * Set this implementation to be after others. */ -readonly class OrderAfter extends ComplexOrder { +readonly class OrderAfter extends RelativeOrderBase { /** - * After means the priority should not be larger than others. + * {@inheritdoc} */ - const bool VALUE = FALSE; + protected function isAfter(): bool { + return TRUE; + } } diff --git a/core/lib/Drupal/Core/Hook/OrderBefore.php b/core/lib/Drupal/Core/Hook/OrderBefore.php index 4b1a1df6208b..7b223be62142 100644 --- a/core/lib/Drupal/Core/Hook/OrderBefore.php +++ b/core/lib/Drupal/Core/Hook/OrderBefore.php @@ -7,11 +7,13 @@ /** * Set this implementation to be before others. */ -readonly class OrderBefore extends ComplexOrder { +readonly class OrderBefore extends RelativeOrderBase { /** - * Before means the priority should be larger than others. + * {@inheritdoc} */ - const bool VALUE = TRUE; + protected function isAfter(): bool { + return FALSE; + } } diff --git a/core/lib/Drupal/Core/Hook/OrderInterface.php b/core/lib/Drupal/Core/Hook/OrderInterface.php new file mode 100644 index 000000000000..9c00a91dd0e7 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/OrderInterface.php @@ -0,0 +1,21 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Core\Hook; + +use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; + +interface OrderInterface { + + /** + * @param string $identifier + * Identifier of the implementation to move to a new position. + * The format is "$class::$module". + * + * @return \Drupal\Core\Hook\OrderOperation\OrderOperationInterface + * Order operation to apply to a hook implementation list. + */ + public function getOperation(string $identifier): OrderOperationInterface; + +} diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php new file mode 100644 index 000000000000..0debf4574098 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Core\Hook\OrderOperation; + +class AbsoluteOrderOperation implements OrderOperationInterface { + + /** + * Constructor. + * + * @param string $identifier + * Identifier of the hook implementation to move to a new position. + * The format is "$class::$method". + * @param bool $isLast + * TRUE to move to the end, FALSE to move to the start. + */ + public function __construct( + protected readonly string $identifier, + protected readonly bool $isLast, + ) {} + + /** + * {@inheritdoc} + */ + public function apply(array &$identifiers, array $module_finder): void { + $index = array_search($this->identifier, $identifiers); + if ($index === FALSE) { + // The element does not exist. + return; + } + unset($identifiers[$index]); + if ($this->isLast) { + $identifiers[] = $this->identifier; + } + else { + $identifiers = [$this->identifier, ...$identifiers]; + } + } + +} diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php new file mode 100644 index 000000000000..a1fc25d7a9b3 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php @@ -0,0 +1,20 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Core\Hook\OrderOperation; + +interface OrderOperationInterface { + + /** + * Alters a list of hook implementations. + * + * @param list<string> $identifiers + * Implementation identifiers, as "$class::$method". + * @param array<string, string> $module_finder + * Lookup map to find a module name for each implementation. + * This may contain more entries than $identifiers. + */ + public function apply(array &$identifiers, array $module_finder): void; + +} diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php new file mode 100644 index 000000000000..cc5c8c412f23 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php @@ -0,0 +1,75 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Core\Hook\OrderOperation; + +class RelativeOrderOperation implements OrderOperationInterface { + + /** + * Constructor. + * + * @param string $identifier + * Identifier of the hook implementation to move to a new position. + * The format is "$class::$method". + * @param list<string> $modulesToOrderAgainst + * Module names the implementations of which to order against. + * @param array $identifiersToOrderAgainst + * Identifiers of implementations to order against. + * The format is "$class::$method". + * @param bool $isAfter + * TRUE, if the implementation to move should be after the implementations + * to order against. + */ + public function __construct( + protected readonly string $identifier, + protected readonly array $modulesToOrderAgainst, + protected readonly array $identifiersToOrderAgainst, + protected readonly bool $isAfter, + ) {} + + /** + * {@inheritdoc} + */ + public function apply(array &$identifiers, array $module_finder): void { + assert(array_is_list($identifiers)); + $index = array_search($this->identifier, $identifiers); + if ($index === FALSE) { + // Nothing to reorder. + return; + } + $identifiers_to_order_against = $this->identifiersToOrderAgainst; + if ($this->modulesToOrderAgainst) { + $identifiers_to_order_against = [ + ...$identifiers_to_order_against, + ...array_keys(array_intersect($module_finder, $this->modulesToOrderAgainst)), + ]; + } + $indices_to_order_against = array_keys(array_intersect($identifiers, $identifiers_to_order_against)); + if ($indices_to_order_against === []) { + return; + } + if ($this->isAfter) { + $max_index_to_order_against = max($indices_to_order_against); + if ($index >= $max_index_to_order_against) { + // The element is already after the other elements. + return; + } + array_splice($identifiers, $max_index_to_order_against + 1, 0, $this->identifier); + // Remove the element after splicing. + unset($identifiers[$index]); + return; + } + else { + $min_index_to_order_against = min($indices_to_order_against); + if ($index <= $min_index_to_order_against) { + // The element is already before the other elements. + return; + } + // Remove the element before splicing. + unset($identifiers[$index]); + array_splice($identifiers, $min_index_to_order_against, 0, $this->identifier); + } + } + +} diff --git a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php new file mode 100644 index 000000000000..736f9faee7e0 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php @@ -0,0 +1,56 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Hook; + +use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; +use Drupal\Core\Hook\OrderOperation\RelativeOrderOperation; + +/** + * Orders an implementation relative to other implementations. + */ +abstract readonly class RelativeOrderBase implements OrderInterface { + + /** + * Constructor. + * + * @param list<string> $modules + * A list of modules the implementations of which to order against. + * @param list<array{class-string, string}> $classesAndMethods + * A list of implementations to order against, as [$class, $method]. + */ + public function __construct( + public array $modules = [], + public array $classesAndMethods = [], + ) { + if (!$this->modules && !$this->classesAndMethods) { + throw new \LogicException('Order must provide either modules or class-method pairs to order against.'); + } + } + + /** + * Specifies the ordering direction. + * + * @return bool + * TRUE, if the ordered implementation should be inserted _after_ the + * implementations specified in the constructor. + */ + abstract protected function isAfter(): bool; + + /** + * {@inheritdoc} + */ + public function getOperation(string $identifier): OrderOperationInterface { + return new RelativeOrderOperation( + $identifier, + $this->modules, + array_map( + fn(array $class_and_method) => implode('::', $class_and_method), + $this->classesAndMethods, + ), + $this->isAfter(), + ); + } + +} diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index 0f0b937f30b5..fdc1d7fcf423 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -112,7 +112,6 @@ public function theme() : array { #[Hook('form_filter_format_form_alter', order: new OrderAfter( modules: ['editor', 'media'], - extraTypes: ['form_filter_format_add_form_alter', 'form_filter_format_edit_form_alter'], ) )] public function formFilterFormatFormAlter(array &$form, FormStateInterface $form_state, $form_id) : void { diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module index c02369025037..4405f7b017e1 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module @@ -1,13 +1,18 @@ <?php +/** + * @file + * Contains procedural hook implementations. + */ + declare(strict_types=1); use Drupal\hk_a_test\Hook\ModuleImplementsAlter; /** - * Implements hook_testhook(). + * Implements hook_test_hook(). */ -function hk_a_test_testhook(): string { +function hk_a_test_test_hook(): string { return __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php index e548a6d48b77..bb4eb3466c2f 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php @@ -7,6 +7,9 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\OrderAfter; +/** + * Hooks for testing ordering. + */ class AAlterHooks { #[Hook('test_alter', order: new OrderAfter(modules: ['hk_c_test']))] diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php index d4e08fe1f953..7ab99f0e8278 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php @@ -7,6 +7,9 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\OrderAfter; +/** + * Hooks for testing ordering. + */ class AFormAlterHooks { #[Hook('form_alter')] @@ -14,7 +17,7 @@ public function formAlter(array &$form): void { $form['#calls'][] = __METHOD__; } - #[Hook('form_myform_alter')] + #[Hook('form_my_form_alter')] public function myFormAlter(array &$form): void { $form['#calls'][] = __METHOD__; } @@ -24,19 +27,9 @@ public function formAlterAfterB(array &$form): void { $form['#calls'][] = __METHOD__; } - #[Hook('form_myform_alter', order: new OrderAfter(modules: ['hk_b_test']))] + #[Hook('form_my_form_alter', order: new OrderAfter(modules: ['hk_b_test']))] public function myFormAlterAfterB(array &$form): void { $form['#calls'][] = __METHOD__; } - #[Hook('form_alter', order: new OrderAfter(modules: ['hk_b_test'], extraTypes: ['form_myform_alter']))] - public function formAlterAfterBExtra(array &$form): void { - $form['#calls'][] = __METHOD__; - } - - #[Hook('form_myform_alter', order: new OrderAfter(modules: ['hk_b_test'], extraTypes: ['form_alter']))] - public function myFormAlterAfterBExtra(array &$form): void { - $form['#calls'][] = __METHOD__; - } - } diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php index 009a940e0a85..b00ad46f5d58 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php @@ -8,24 +8,27 @@ use Drupal\Core\Hook\Order; use Drupal\Core\Hook\OrderAfter; +/** + * Hooks for testing ordering. + */ class AHooks { - #[Hook('testhook')] + #[Hook('test_hook')] public function testHook(): string { return __METHOD__; } - #[Hook('testhook', order: Order::First)] + #[Hook('test_hook', order: Order::First)] public function testHookFirst(): string { return __METHOD__; } - #[Hook('testhook', order: Order::Last)] + #[Hook('test_hook', order: Order::Last)] public function testHookLast(): string { return __METHOD__; } - #[Hook('testhook', order: new OrderAfter(modules: ['hk_b_test']))] + #[Hook('test_hook', order: new OrderAfter(modules: ['hk_b_test']))] public function testHookAfterB(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module index ef2949485daa..bc0b1c03e247 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module @@ -1,11 +1,16 @@ <?php +/** + * @file + * Contains procedural hook implementations. + */ + declare(strict_types=1); /** - * Implements hook_testhook(). + * Implements hook_test_hook(). */ -function hk_b_test_testhook(): string { +function hk_b_test_test_hook(): string { return __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php index 790a71b3adc9..b84361a08d94 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php @@ -5,15 +5,12 @@ namespace Drupal\hk_b_test\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderAfter; +/** + * Hooks for testing ordering. + */ class BAlterHooks { - #[Hook('test_alter', order: new OrderAfter(modules: ['hk_c_test'], extraTypes: ['test_subtype_alter']))] - public function testAlterAfterCExtra(array &$calls): void { - $calls[] = __METHOD__; - } - #[Hook('test_subtype_alter')] public function testSubtypeAlter(array &$calls): void { $calls[] = __METHOD__; diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php index 4c02511647de..dcd47f742946 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php @@ -6,6 +6,9 @@ use Drupal\Core\Hook\Attribute\Hook; +/** + * Hooks for testing ordering. + */ class BFormAlterHooks { #[Hook('form_alter')] @@ -13,7 +16,7 @@ public function formAlter(array &$form): void { $form['#calls'][] = __METHOD__; } - #[Hook('form_myform_alter')] + #[Hook('form_my_form_alter')] public function myFormAlter(array &$form): void { $form['#calls'][] = __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php index 3b3953c5a2cb..52bdb497c447 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php @@ -6,9 +6,12 @@ use Drupal\Core\Hook\Attribute\Hook; +/** + * Hooks for testing ordering. + */ class BHooks { - #[Hook('testhook')] + #[Hook('test_hook')] public function testHook(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module index 2856847c2305..963f3e1695a3 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module @@ -1,11 +1,16 @@ <?php +/** + * @file + * Contains procedural hook implementations. + */ + declare(strict_types=1); /** - * Implements hook_testhook(). + * Implements hook_test_hook(). */ -function hk_c_test_testhook(): string { +function hk_c_test_test_hook(): string { return __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php index 447814a9fbbd..93eb6c5fb7ba 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php @@ -6,6 +6,9 @@ use Drupal\Core\Hook\Attribute\Hook; +/** + * Hooks for testing ordering. + */ class CAlterHooks { #[Hook('test_alter')] diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php index ce4c276c6d11..03566e881ffe 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php @@ -6,6 +6,9 @@ use Drupal\Core\Hook\Attribute\Hook; +/** + * Hooks for testing ordering. + */ class CFormAlterHooks { #[Hook('form_alter')] @@ -13,7 +16,7 @@ public function formAlter(array &$form): void { $form['#calls'][] = __METHOD__; } - #[Hook('form_myform_alter')] + #[Hook('form_my_form_alter')] public function myFormAlter(array &$form): void { $form['#calls'][] = __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php index 99d924e5d343..70130ca3064e 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php @@ -7,14 +7,17 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order; +/** + * Hooks for testing ordering. + */ class CHooks { - #[Hook('testhook')] + #[Hook('test_hook')] public function testHook(): string { return __METHOD__; } - #[Hook('testhook', order: Order::First)] + #[Hook('test_hook', order: Order::First)] public function testHookFirst(): string { return __METHOD__; } @@ -24,7 +27,7 @@ public function testHookFirst(): string { * * @see \Drupal\hk_d_test\Hook\DHooks */ - #[Hook('testhook')] + #[Hook('test_hook')] public function testHookReOrderFirst(): string { return __METHOD__; } @@ -34,7 +37,7 @@ public function testHookReOrderFirst(): string { * * @see \Drupal\hk_d_test\Hook\DHooks */ - #[Hook('testhook')] + #[Hook('test_hook')] public function testHookRemoved(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module b/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module index f3e9c5b48091..ab79c190ffa5 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module @@ -1,10 +1,15 @@ <?php +/** + * @file + * Contains procedural hook implementations. + */ + declare(strict_types=1); /** - * Implements hook_testhook(). + * Implements hook_test_hook(). */ -function hk_d_test_testhook(): string { +function hk_d_test_test_hook(): string { return __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php index 817e1ba60f28..d47443a55968 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php @@ -6,6 +6,9 @@ use Drupal\Core\Hook\Attribute\Hook; +/** + * Hooks for testing ordering. + */ class DAlterHooks { #[Hook('test_alter')] diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php index a6b413c3f2a7..cd92b6b0cd4e 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php @@ -10,11 +10,14 @@ use Drupal\Core\Hook\Order; use Drupal\hk_c_test\Hook\CHooks; -#[ReOrderHook('testhook', CHooks::class, 'testHookReOrderFirst', Order::First)] -#[RemoveHook('testhook', CHooks::class, 'testHookRemoved')] +/** + * Hooks for testing ordering. + */ +#[ReOrderHook('test_hook', CHooks::class, 'testHookReOrderFirst', Order::First)] +#[RemoveHook('test_hook', CHooks::class, 'testHookRemoved')] class DHooks { - #[Hook('testhook')] + #[Hook('test_hook')] public function testHook(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php index 321cc19e5bf1..dbf57bf3e364 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php @@ -27,7 +27,6 @@ class TestHookOrderExtraTypes { #[Hook('custom_hook_extra_types1_alter', order: new OrderAfter( modules: ['hook_order_last_alphabetically'], - extraTypes: ['custom_hook_extra_types2_alter'], ) )] public static function customHookExtraTypes(): void { diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index 335b71e79095..12aaeb7ad6c7 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -13,6 +13,7 @@ use Drupal\hk_b_test\Hook\BHooks; use Drupal\hk_c_test\Hook\CAlterHooks; use Drupal\hk_c_test\Hook\CFormAlterHooks; +use Drupal\hk_c_test\Hook\CHooks; use Drupal\hk_d_test\Hook\DAlterHooks; use Drupal\hk_d_test\Hook\DHooks; use Drupal\KernelTests\KernelTestBase; @@ -222,9 +223,8 @@ function (array &$implementations, string $hook): void { public function testAlterOrder(): void { $this->assertAlterCallOrder([ - AAlterHooks::class . '::testAlterAfterC', - BAlterHooks::class . '::testAlterAfterCExtra', CAlterHooks::class . '::testAlter', + AAlterHooks::class . '::testAlterAfterC', DAlterHooks::class . '::testAlter', ], 'test'); @@ -237,12 +237,11 @@ public function testAlterOrder(): void { $this->assertAlterCallOrder([ // The implementation from 'D' is gone. - AAlterHooks::class . '::testAlterAfterC', AAlterHooks::class . '::testSubtypeAlter', - BAlterHooks::class . '::testAlterAfterCExtra', BAlterHooks::class . '::testSubtypeAlter', CAlterHooks::class . '::testAlter', CAlterHooks::class . '::testSubtypeAlter', + AAlterHooks::class . '::testAlterAfterC', DAlterHooks::class . '::testAlter', DAlterHooks::class . '::testSubtypeAlter', ], ['test', 'test_subtype']); @@ -262,10 +261,10 @@ public function testAlterOrder(): void { ], 'test_subtype', prepend_unknown_type: FALSE); $this->assertAlterCallOrder([ + AAlterHooks::class . '::testSubtypeAlter', CAlterHooks::class . '::testAlter', CAlterHooks::class . '::testSubtypeAlter', AAlterHooks::class . '::testAlterAfterC', - AAlterHooks::class . '::testSubtypeAlter', DAlterHooks::class . '::testAlter', DAlterHooks::class . '::testSubtypeAlter', ], ['test', 'test_subtype'], prepend_unknown_type: FALSE); @@ -274,43 +273,41 @@ public function testAlterOrder(): void { public function testFormAlterOrder(): void { $this->assertSameCallList([ AFormAlterHooks::class . '::formAlter', - AFormAlterHooks::class . '::formAlterAfterB', - AFormAlterHooks::class . '::formAlterAfterBExtra', BFormAlterHooks::class . '::formAlter', + AFormAlterHooks::class . '::formAlterAfterB', CFormAlterHooks::class . '::formAlter', ], $this->alter('form')['#calls'] ?? NULL); $this->assertSameCallList([ AFormAlterHooks::class . '::formAlter', - AFormAlterHooks::class . '::formAlterAfterB', - AFormAlterHooks::class . '::formAlterAfterBExtra', AFormAlterHooks::class . '::myFormAlter', - AFormAlterHooks::class . '::myFormAlterAfterB', - AFormAlterHooks::class . '::myFormAlterAfterBExtra', BFormAlterHooks::class . '::formAlter', BFormAlterHooks::class . '::myFormAlter', + AFormAlterHooks::class . '::myFormAlterAfterB', + AFormAlterHooks::class . '::formAlterAfterB', CFormAlterHooks::class . '::formAlter', CFormAlterHooks::class . '::myFormAlter', - ], $this->alter(['form', 'form_myform'])['#calls'] ?? NULL); + ], $this->alter(['form', 'form_my_form'])['#calls'] ?? NULL); } public function testHookOrder(): void { $this->assertSameCallList( [ - // All the implementations from CHooks are gone. - // @todo This is probably bad. + CHooks::class . '::testHookReOrderFirst', + CHooks::class . '::testHookFirst', AHooks::class . '::testHookFirst', - 'hk_a_test_testhook', + 'hk_a_test_test_hook', AHooks::class . '::testHook', - AHooks::class . '::testHookAfterB', - AHooks::class . '::testHookLast', - 'hk_b_test_testhook', + 'hk_b_test_test_hook', BHooks::class . '::testHook', - 'hk_c_test_testhook', - 'hk_d_test_testhook', + AHooks::class . '::testHookAfterB', + 'hk_c_test_test_hook', + CHooks::class . '::testHook', + 'hk_d_test_test_hook', DHooks::class . '::testHook', + AHooks::class . '::testHookLast', ], - \Drupal::moduleHandler()->invokeAll('testhook'), + \Drupal::moduleHandler()->invokeAll('test_hook'), ); } -- GitLab From 84aacd32b93e8e58c2d0a4cc1154b8007c2be598 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 13 Mar 2025 01:40:37 +0100 Subject: [PATCH 187/268] Ignore deprecations in HookOrderTest. --- core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index 12aaeb7ad6c7..1a4d22140112 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -17,10 +17,12 @@ use Drupal\hk_d_test\Hook\DAlterHooks; use Drupal\hk_d_test\Hook\DHooks; use Drupal\KernelTests\KernelTestBase; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; /** * @group Hook */ +#[IgnoreDeprecations] class HookOrderTest extends KernelTestBase { /** -- GitLab From 8ac7816ca0fbafd8b023ee43dfbb6fb9128f2ca2 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 11 Mar 2025 03:28:19 +0100 Subject: [PATCH 188/268] Test that the side effect no longer happens. --- .../hk_extra_test/hk_extra_test.info.yml | 6 ++++++ .../hk_extra_test/src/Hook/Ordering.php | 17 +++++++++++++++++ .../KernelTests/Core/Hook/HookOrderTest.php | 6 ++++++ 3 files changed, 29 insertions(+) create mode 100644 core/modules/system/tests/modules/HookOrder/hk_extra_test/hk_extra_test.info.yml create mode 100644 core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php diff --git a/core/modules/system/tests/modules/HookOrder/hk_extra_test/hk_extra_test.info.yml b/core/modules/system/tests/modules/HookOrder/hk_extra_test/hk_extra_test.info.yml new file mode 100644 index 000000000000..a710a069b4f8 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_extra_test/hk_extra_test.info.yml @@ -0,0 +1,6 @@ +name: Hook Extra Test +type: module +description: 'Test module used to test hook ordering.' +package: Testing +version: VERSION +core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php b/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php new file mode 100644 index 000000000000..fec032ed59e3 --- /dev/null +++ b/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\hk_extra_test\Hook; + +use Drupal\Core\Extension\ProceduralCall; +use Drupal\Core\Hook\Attribute\ReOrderHook; +use Drupal\Core\Hook\OrderBefore; + +/** + * Hooks for testing ordering. + */ +#[ReOrderHook('procedural_alter', ProceduralCall::class, 'hk_a_test_procedural_alter', new OrderBefore(['hk_b_test'], []))] +class Ordering { + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index 1a4d22140112..3074e3ed701a 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -223,6 +223,12 @@ function (array &$implementations, string $hook): void { ], ['procedural', 'procedural_subtype']); } + public function testProceduralOrderSideEffect(): void { + $this->enableModules(['hk_extra_test']); + // The previous test should behave exactly the same. + $this->testProceduralAlterOrder(); + } + public function testAlterOrder(): void { $this->assertAlterCallOrder([ CAlterHooks::class . '::testAlter', -- GitLab From dffa3f8fca6e444d3c755b4a1a2c4a81c0328431 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 14 Mar 2025 01:10:36 +0100 Subject: [PATCH 189/268] Safer serialization of order operation objects. --- .../Drupal/Core/Extension/ModuleHandler.php | 32 +++++++----- core/lib/Drupal/Core/Hook/HookCollector.php | 12 ++++- .../Hook/OrderOperation/OrderOperation.php | 49 +++++++++++++++++++ 3 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 4eb0bbe255a5..babca86199b7 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -7,7 +7,7 @@ use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\HookCollector; -use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; +use Drupal\Core\Hook\OrderOperation\OrderOperation; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -105,7 +105,7 @@ class ModuleHandler implements ModuleHandlerInterface { * @param array<string, list<string>> $groupIncludes * Lists of *.inc file paths that contain procedural implementations, keyed * by hook name. - * @param ?string $serialized_ordering_rules + * @param array<string, list<string>> $packedOrderOperations * Ordering rules by hook name, serialized. * * @see \Drupal\Core\DrupalKernel @@ -117,16 +117,8 @@ public function __construct( protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, protected array $groupIncludes = [], - ?string $serialized_ordering_rules = NULL, + protected array $packedOrderOperations = [], ) { - if ($serialized_ordering_rules !== NULL) { - $this->orderingRules = unserialize($serialized_ordering_rules); - foreach ($this->orderingRules as $rules) { - foreach ($rules as $rule) { - assert($rule instanceof OrderOperationInterface); - } - } - } $this->root = $root; $this->moduleList = []; foreach ($module_list as $name => $module) { @@ -527,7 +519,7 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook } $identifiers = array_keys($listeners_by_identifier); foreach ([$main_hook, ...$extra_hooks] as $hook) { - foreach ($this->orderingRules[$hook] ?? [] as $rule) { + foreach ($this->getHookOrderingRules($hook) as $rule) { $rule->apply($identifiers, $modules_by_identifier); assert($identifiers === array_unique($identifiers)); $identifiers = array_values($identifiers); @@ -554,6 +546,22 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook ); } + /** + * Gets ordering rules for a hook. + * + * @param string $hook + * Hook name. + * + * @return list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface> + * List of order operations for the hook. + */ + protected function getHookOrderingRules(string $hook): array { + return $this->orderingRules[$hook] ??= array_map( + OrderOperation::unpack(...), + $this->packedOrderOperations[$hook] ?? [], + ); + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php index a0efd03d2252..6a83000a4cca 100644 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -14,6 +14,7 @@ use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; +use Drupal\Core\Hook\OrderOperation\OrderOperation; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -137,7 +138,16 @@ public function writeToContainer(ContainerBuilder $container): void { // Update the module handler definition. $definition = $container->getDefinition('module_handler'); $definition->setArgument('$groupIncludes', $groupIncludes); - $definition->setArgument('$serialized_ordering_rules', serialize($this->getOrderOperations())); + + $packed_order_operations = []; + $order_operations = $this->getOrderOperations(); + foreach (preg_grep('@_alter$@', array_keys($order_operations)) as $alter_hook) { + $packed_order_operations[$alter_hook] = array_map( + OrderOperation::pack(...), + $order_operations[$alter_hook], + ); + } + $definition->setArgument('$packedOrderOperations', $packed_order_operations); } /** diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php new file mode 100644 index 000000000000..3508bd877d45 --- /dev/null +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Core\Hook\OrderOperation; + +/** + * Static methods related to order operations. + * + * These should be used instead of serialize() or unserialize(), to avoid + * security issues with unserialize(), and for additional validation. + */ +class OrderOperation { + + const array KNOWN_CLASSES = [ + AbsoluteOrderOperation::class, + RelativeOrderOperation::class, + ]; + + /** + * Serializes an order operation object. + * + * @param \Drupal\Core\Hook\OrderOperation\OrderOperationInterface $operation + * Order operation object. + * + * @return string + * Serialized object. + */ + public static function pack(OrderOperationInterface $operation): string { + if (!in_array(get_class($operation), static::KNOWN_CLASSES)) { + throw new \InvalidArgumentException('Unsupported order operation class ' . get_class($operation)); + } + return serialize($operation); + } + + /** + * Unserializes an order operation object. + * + * @param string $serialized_operation + * Serialized operation. + * + * @return \Drupal\Core\Hook\OrderOperation\OrderOperationInterface + * Unserialized operation. + */ + public static function unpack(string $serialized_operation): OrderOperationInterface { + return unserialize($serialized_operation, ['allowed_classes' => static::KNOWN_CLASSES]); + } + +} -- GitLab From bd83f7a0325081e21ff0c839091df725c5a3d117 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 14 Mar 2025 02:34:19 +0100 Subject: [PATCH 190/268] Don't use serialize(). --- .../OrderOperation/AbsoluteOrderOperation.php | 7 +++++ .../Hook/OrderOperation/OrderOperation.php | 30 ++++++++++--------- .../OrderOperationInterface.php | 9 ++++++ .../OrderOperation/RelativeOrderOperation.php | 7 +++++ 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php index 0debf4574098..d8b2d7e9bbe3 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php @@ -20,6 +20,13 @@ public function __construct( protected readonly bool $isLast, ) {} + /** + * {@inheritdoc} + */ + public function pack(): array { + return get_object_vars($this); + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php index 3508bd877d45..0ddf18d61751 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -13,8 +13,8 @@ class OrderOperation { const array KNOWN_CLASSES = [ - AbsoluteOrderOperation::class, - RelativeOrderOperation::class, + 'absolute' => AbsoluteOrderOperation::class, + 'relative' => RelativeOrderOperation::class, ]; /** @@ -23,27 +23,29 @@ class OrderOperation { * @param \Drupal\Core\Hook\OrderOperation\OrderOperationInterface $operation * Order operation object. * - * @return string - * Serialized object. + * @return array + * Packed operation. */ - public static function pack(OrderOperationInterface $operation): string { - if (!in_array(get_class($operation), static::KNOWN_CLASSES)) { - throw new \InvalidArgumentException('Unsupported order operation class ' . get_class($operation)); - } - return serialize($operation); + public static function pack(OrderOperationInterface $operation): array { + $type = array_search(get_class($operation), static::KNOWN_CLASSES) + ?: throw new \InvalidArgumentException('Unsupported order operation class ' . get_class($operation)); + return [$type, $operation->pack()]; } /** * Unserializes an order operation object. * - * @param string $serialized_operation - * Serialized operation. + * @param array $packed_operation + * Packed operation. * * @return \Drupal\Core\Hook\OrderOperation\OrderOperationInterface - * Unserialized operation. + * Unpacked operation. */ - public static function unpack(string $serialized_operation): OrderOperationInterface { - return unserialize($serialized_operation, ['allowed_classes' => static::KNOWN_CLASSES]); + public static function unpack(array $packed_operation): OrderOperationInterface { + [$type, $args] = $packed_operation; + $class = static::KNOWN_CLASSES[$type] + ?? throw new \InvalidArgumentException('Unsupported order operation type ' . $type); + return new $class(...$args); } } diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php index a1fc25d7a9b3..58e5087536f8 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php @@ -17,4 +17,13 @@ interface OrderOperationInterface { */ public function apply(array &$identifiers, array $module_finder): void; + /** + * Packs the object properties. + * + * @return array + * An array to pass as arguments to the constructor. + * Keys can be parameter names or indices. + */ + public function pack(): array; + } diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php index cc5c8c412f23..cbb8f17112b3 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php @@ -28,6 +28,13 @@ public function __construct( protected readonly bool $isAfter, ) {} + /** + * {@inheritdoc} + */ + public function pack(): array { + return get_object_vars($this); + } + /** * {@inheritdoc} */ -- GitLab From e40b24047e1e3ae9b9abe5d0547eafdfe82577a5 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 15 Mar 2025 01:31:48 +0100 Subject: [PATCH 191/268] Fix bad slash in comment. --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index f41c8b752286..ef3fa1fbe651 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -39,7 +39,7 @@ * @see https://www.drupal.org/node/3493962 * * Removing hook implementations can be done by using the attribute - * \Drupal\Core\Hook\Attribute/RemoveHook. + * \Drupal\Core\Hook\Attribute\RemoveHook. * * @see https://www.drupal.org/node/3496786 * -- GitLab From a2e4c66c1ca08b6f506d14fbd12541d712260ba5 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 15 Mar 2025 01:30:12 +0100 Subject: [PATCH 192/268] Use static for closures. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 4 ++-- core/lib/Drupal/Core/Hook/HookCollector.php | 4 ++-- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- core/lib/Drupal/Core/Hook/RelativeOrderBase.php | 2 +- core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index babca86199b7..ded3d9dd3f83 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -462,7 +462,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // quickly. if (!isset($this->alterEventListeners[$cid])) { $hooks = is_array($type) - ? array_map(fn (string $type) => $type . '_alter', $type) + ? array_map(static fn (string $type) => $type . '_alter', $type) : [$type . '_alter']; $this->alterEventListeners[$cid] = $this->getCombinedListeners(...$hooks); } @@ -541,7 +541,7 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook } $identifiers = array_values(array_unique($identifiers)); return array_map( - fn (string $identifier) => $listeners_by_identifier[$identifier], + static fn (string $identifier) => $listeners_by_identifier[$identifier], $identifiers, ); } diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php index 6a83000a4cca..6c367b4430d6 100644 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -327,7 +327,7 @@ protected static function writeImplementationsToContainer( public static function collectAllHookImplementations(array $module_list, array $skipProceduralModules = []): static { $modules = array_keys($module_list); $modules_by_length = $modules; - usort($modules_by_length, fn ($a, $b) => strlen($b) - strlen($a)); + usort($modules_by_length, static fn ($a, $b) => strlen($b) - strlen($a)); $known_modules_pattern = implode('|', array_map( static fn ($x) => preg_quote($x, '/'), $modules_by_length, @@ -452,7 +452,7 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv return TRUE; } // glob() doesn't support streams but scandir() does. - return !in_array($fileInfo->getFilename(), ['tests', 'js', 'css']) && !array_filter(scandir($key), fn ($filename) => str_ends_with($filename, '.info.yml')); + return !in_array($fileInfo->getFilename(), ['tests', 'js', 'css']) && !array_filter(scandir($key), static fn ($filename) => str_ends_with($filename, '.info.yml')); } return in_array($extension, ['inc', 'module', 'profile', 'install']); } diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 2839dc110919..ecb8b89a79ec 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -30,7 +30,7 @@ public function process(ContainerBuilder $container): void { $parameters = $container->getParameterBag()->all(); $skip_procedural_modules = array_filter( array_keys($module_list), - fn (string $module) => !empty($parameters["$module.hooks_converted"]), + static fn (string $module) => !empty($parameters["$module.hooks_converted"]), ); $collector = HookCollector::collectAllHookImplementations($module_list, $skip_procedural_modules); diff --git a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php index 736f9faee7e0..2bea5b352d81 100644 --- a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php +++ b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php @@ -46,7 +46,7 @@ public function getOperation(string $identifier): OrderOperationInterface { $identifier, $this->modules, array_map( - fn(array $class_and_method) => implode('::', $class_and_method), + static fn(array $class_and_method) => implode('::', $class_and_method), $this->classesAndMethods, ), $this->isAfter(), diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index 3074e3ed701a..e573967e162b 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -399,7 +399,7 @@ protected function assertSameCallList(array $expected, array $actual, string $me return '[]'; } $parts = array_map( - function (string $call_string) { + static function (string $call_string) { if (preg_match('@^(\w+\\\\)*(\w+)::(\w+)@', $call_string, $matches)) { [,, $class_shortname, $method] = $matches; return $class_shortname . '::class . ' . var_export('::' . $method, TRUE); -- GitLab From ea784de57b6ee1c6a6f67ad28b076711dea47d75 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 15 Mar 2025 03:54:03 +0100 Subject: [PATCH 193/268] Remove redundant and verbose asserts. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index ded3d9dd3f83..3b5547ede300 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -525,18 +525,6 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook $identifiers = array_values($identifiers); assert(!array_diff($identifiers, array_keys($modules_by_identifier))); assert(!array_diff(array_keys($modules_by_identifier), $identifiers)); - $lost_identifiers = array_diff(array_keys($modules_by_identifier), $identifiers); - assert(!$lost_identifiers, sprintf( - "Lost identifiers for\n%s:\n%s", - var_export($rule, TRUE), - json_encode($lost_identifiers), - )); - $added_identifiers = array_diff($identifiers, array_keys($modules_by_identifier)); - assert(!$added_identifiers, sprintf( - 'Added identifiers for\n%s:\n%s', - var_export($rule, TRUE), - json_encode($added_identifiers), - )); } } $identifiers = array_values(array_unique($identifiers)); -- GitLab From 47d22227bdb5ee959aefeb57da330a18fe542d69 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 15 Mar 2025 01:39:59 +0100 Subject: [PATCH 194/268] Change comment on OrderOperationInterface to avoid the term 'Alter'. --- .../Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php index 58e5087536f8..eaa281c8e55a 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php @@ -7,7 +7,7 @@ interface OrderOperationInterface { /** - * Alters a list of hook implementations. + * Changes the order of a list of hook implementations. * * @param list<string> $identifiers * Implementation identifiers, as "$class::$method". -- GitLab From 075161e73dac8f457f24946e16c70e599e2ec586 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 15 Mar 2025 01:20:38 +0100 Subject: [PATCH 195/268] Collect identifiers as part of the loop, instead of array_keys(). --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 3b5547ede300..38cb7d319ee6 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -502,6 +502,7 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook $this->alter('module_implements', $module_implements, $main_hook); $listeners_by_identifier = []; $modules_by_identifier = []; + $identifiers = []; foreach (array_keys($module_implements) as $module) { foreach ($listeners_by_module[$module] ?? [] as $listener) { $identifier = is_array($listener) @@ -515,9 +516,9 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook } $listeners_by_identifier[$identifier] = $listener; $modules_by_identifier[$identifier] = $module; + $identifiers[] = $identifier; } } - $identifiers = array_keys($listeners_by_identifier); foreach ([$main_hook, ...$extra_hooks] as $hook) { foreach ($this->getHookOrderingRules($hook) as $rule) { $rule->apply($identifiers, $modules_by_identifier); -- GitLab From 1db6ab851385bdeb4b2d44df9bd46232cfcf85a2 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Sat, 15 Mar 2025 01:25:58 +0100 Subject: [PATCH 196/268] Allow a method to implement multiple hooks from an alter call, if the module is the same. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 38cb7d319ee6..b3492fb30c55 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -508,13 +508,11 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook $identifier = is_array($listener) ? get_class($listener[0]) . '::' . $listener[1] : ProceduralCall::class . '::' . $listener; - if (isset($listeners_by_identifier[$identifier])) { - throw new \LogicException(sprintf( - 'The hook implementation %s is registered for more than one hook. This is not allowed for hooks that are called together, in this case %s.', - $identifier, - json_encode([$main_hook, ...$extra_hooks]))); - } $listeners_by_identifier[$identifier] = $listener; + if (isset($modules_by_identifier[$identifier]) && $modules_by_identifier[$identifier] !== $module) { + $other_module = $modules_by_identifier[$identifier]; + throw new \LogicException("$identifier defines two hooks which belongs to two different modules: $module and $other_module, this is not supported for ModuleHandler::alter() called with these two hooks."); + } $modules_by_identifier[$identifier] = $module; $identifiers[] = $identifier; } -- GitLab From 8f687df675a01ec10ea46db2e797d94dc6159d43 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 16:39:31 +0100 Subject: [PATCH 197/268] Optimize extra hooks with no implementations. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index b3492fb30c55..ed4e2dec79fa 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -483,12 +483,20 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { * List of implementation callables. */ protected function getCombinedListeners(string $main_hook, string ...$extra_hooks): array { - if (!$extra_hooks) { + $extra_listeners_by_hook = $extra_hooks + ? array_filter(array_map( + $this->getHookListeners(...), + array_combine($extra_hooks, $extra_hooks), + )) + : []; + if (!$extra_listeners_by_hook) { + // No extra hooks were provided in the call, or none of them has any + // listeners. return $this->getFlatHookListeners($main_hook); } $listeners_by_module = $this->getHookListeners($main_hook); - foreach ($extra_hooks as $extra_hook) { - foreach ($this->getHookListeners($extra_hook) as $module => $extra_listeners) { + foreach ($extra_listeners_by_hook as $extra_listeners_by_module) { + foreach ($extra_listeners_by_module as $module => $extra_listeners) { foreach ($extra_listeners as $extra_listener) { $listeners_by_module[$module][] = $extra_listener; } -- GitLab From e42f34a939d6543249ccfded1979ddff14ec69a5 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 17:35:56 +0100 Subject: [PATCH 198/268] Add comments, and inline the $modules variable into $module_implements expression. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index ed4e2dec79fa..28a82cd30da9 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -494,6 +494,8 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook // listeners. return $this->getFlatHookListeners($main_hook); } + // Combine the listeners from all hooks that are part of the ->alter() call. + // At first they need to be grouped by module. $listeners_by_module = $this->getHookListeners($main_hook); foreach ($extra_listeners_by_hook as $extra_listeners_by_module) { foreach ($extra_listeners_by_module as $module => $extra_listeners) { @@ -502,12 +504,19 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook } } } - $modules = array_intersect( + // Build an array to pass to hook_module_implements_alter(). + // The initial order is the one from 'container.modules' service parameter. + $module_implements = array_fill_keys(array_intersect( array_keys($this->moduleList), array_keys($listeners_by_module), - ); - $module_implements = array_fill_keys($modules, FALSE); + ), FALSE); + // Call hook_module_implements_alter() with the main hook. + // That hook was designed in older Drupal versions where all hook + // implementations were procedural, and each module could implement each + // hook only once. $this->alter('module_implements', $module_implements, $main_hook); + // Convert the list into a different structure to pass to the hook order + // operations. $listeners_by_identifier = []; $modules_by_identifier = []; $identifiers = []; -- GitLab From b110bd984966cfc30df669fbfe93d627698d935b Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 17:36:44 +0100 Subject: [PATCH 199/268] Don't add the same implementation more than once, log warnings instead. --- core/core.services.yml | 2 +- .../Drupal/Core/Extension/ModuleHandler.php | 44 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index 55d8d2ebc984..b7ea7bbc6598 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -653,7 +653,7 @@ services: arguments: ['@container.namespaces', '@cache.discovery', '@module_handler'] module_handler: class: Drupal\Core\Extension\ModuleHandler - arguments: ['%app.root%', '%container.modules%', '@event_dispatcher', '%hook_implementations_map%'] + arguments: ['%app.root%', '%container.modules%', '@event_dispatcher', '@logger.channel.default', '%hook_implementations_map%'] Drupal\Core\Extension\ModuleHandlerInterface: '@module_handler' module_installer: class: Drupal\Core\Extension\ModuleInstaller diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 28a82cd30da9..c7c1efa42cef 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -8,6 +8,7 @@ use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\HookCollector; use Drupal\Core\Hook\OrderOperation\OrderOperation; +use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -115,6 +116,7 @@ public function __construct( $root, array $module_list, protected EventDispatcherInterface $eventDispatcher, + protected readonly LoggerInterface $logger, protected array $hookImplementationsMap, protected array $groupIncludes = [], protected array $packedOrderOperations = [], @@ -525,11 +527,45 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook $identifier = is_array($listener) ? get_class($listener[0]) . '::' . $listener[1] : ProceduralCall::class . '::' . $listener; - $listeners_by_identifier[$identifier] = $listener; - if (isset($modules_by_identifier[$identifier]) && $modules_by_identifier[$identifier] !== $module) { - $other_module = $modules_by_identifier[$identifier]; - throw new \LogicException("$identifier defines two hooks which belongs to two different modules: $module and $other_module, this is not supported for ModuleHandler::alter() called with these two hooks."); + // Detect if the implementation is already part of the list. + // In general, a method can implement more than one hook. However, if + // both of these hooks are part of the same ->alter() call, that is + // almost always by mistake. + if ($other_module = $modules_by_identifier[$identifier] ?? NULL) { + $log_message_replacements = [ + '@implementation' => is_array($listener) + ? ('method ' . $identifier . '()') + : ('function ' . $listener[1] . '()'), + '@hooks' => "['" . implode("', '", [$main_hook, ...$extra_hooks]) . "']", + ]; + if ($other_module !== $module) { + // There is conflicting information about on behalf of which module + // this implementation is registered. At this point we cannot even + // be sure if the module is the one from the main hook or the extra + // hook. + // The module is mostly irrelevant for alter hooks, except for its + // impact on ordering. + $this->logger->warning( + 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call, on behalf of different modules @module and @other_module. Only one instance will be part of the implementation list for this hook combination. For the purpose of ordering, the module @module will be used.', + [ + ...$log_message_replacements, + '@module' => "'$module'", + '@other_module' => "'$other_module'", + ], + ); + } + else { + // There is no conflict, but probably one or more redundant #[Hook] + // attributes should be removed. + $this->logger->notice( + 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call. Only one instance will be part of the implementation list for this hook combination.', + $log_message_replacements, + ); + } + // Don't add an identifier more than once. + continue; } + $listeners_by_identifier[$identifier] = $listener; $modules_by_identifier[$identifier] = $module; $identifiers[] = $identifier; } -- GitLab From 3d2d07686d279aaee0c03bea20a05aa0bb11eb83 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 18:22:03 +0100 Subject: [PATCH 200/268] Re-add a comment about recursion from ->alter('module_implements'). --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index c7c1efa42cef..f72f621f58e2 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -516,6 +516,8 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook // That hook was designed in older Drupal versions where all hook // implementations were procedural, and each module could implement each // hook only once. + // This call to ->alter() does not cause infinite recursion, because it is + // called with only one alter type, so we don't end up in this line again. $this->alter('module_implements', $module_implements, $main_hook); // Convert the list into a different structure to pass to the hook order // operations. -- GitLab From 6a2704788a5a6597f279123055dfdd7153561331 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 18:50:32 +0100 Subject: [PATCH 201/268] Call array_values() in each order operation if the array was changed. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 6 +++++- .../Core/Hook/OrderOperation/AbsoluteOrderOperation.php | 1 + .../Core/Hook/OrderOperation/RelativeOrderOperation.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index f72f621f58e2..87cf47f6a801 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -575,8 +575,12 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook foreach ([$main_hook, ...$extra_hooks] as $hook) { foreach ($this->getHookOrderingRules($hook) as $rule) { $rule->apply($identifiers, $modules_by_identifier); + // Order operations must not: + // - Insert duplicate keys. + // - Change the array to be not a list. + // - Add or remove values. assert($identifiers === array_unique($identifiers)); - $identifiers = array_values($identifiers); + assert(array_is_list($identifiers)); assert(!array_diff($identifiers, array_keys($modules_by_identifier))); assert(!array_diff(array_keys($modules_by_identifier), $identifiers)); } diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php index d8b2d7e9bbe3..b7cf6b8e592a 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php @@ -43,6 +43,7 @@ public function apply(array &$identifiers, array $module_finder): void { else { $identifiers = [$this->identifier, ...$identifiers]; } + $identifiers = array_values($identifiers); } } diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php index cbb8f17112b3..fc615e332037 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php @@ -65,7 +65,7 @@ public function apply(array &$identifiers, array $module_finder): void { array_splice($identifiers, $max_index_to_order_against + 1, 0, $this->identifier); // Remove the element after splicing. unset($identifiers[$index]); - return; + $identifiers = array_values($identifiers); } else { $min_index_to_order_against = min($indices_to_order_against); -- GitLab From 052a4a752c6ff65fd9ddb94d5d407ca81fe56ba6 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 19:04:42 +0100 Subject: [PATCH 202/268] Rename AbsoluteOrderOperation -> FirstOrLast. --- core/lib/Drupal/Core/Hook/Order.php | 4 ++-- .../{AbsoluteOrderOperation.php => FirstOrLast.php} | 2 +- core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename core/lib/Drupal/Core/Hook/OrderOperation/{AbsoluteOrderOperation.php => FirstOrLast.php} (94%) diff --git a/core/lib/Drupal/Core/Hook/Order.php b/core/lib/Drupal/Core/Hook/Order.php index 413f5d479e92..8b5fb245cb71 100644 --- a/core/lib/Drupal/Core/Hook/Order.php +++ b/core/lib/Drupal/Core/Hook/Order.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook; -use Drupal\Core\Hook\OrderOperation\AbsoluteOrderOperation; +use Drupal\Core\Hook\OrderOperation\FirstOrLast; use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; /** @@ -19,7 +19,7 @@ enum Order: int implements OrderInterface { case Last = 0; public function getOperation(string $identifier): OrderOperationInterface { - return new AbsoluteOrderOperation($identifier, $this === self::Last); + return new FirstOrLast($identifier, $this === self::Last); } } diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php similarity index 94% rename from core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php rename to core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php index b7cf6b8e592a..341b92e51a46 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/AbsoluteOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\OrderOperation; -class AbsoluteOrderOperation implements OrderOperationInterface { +class FirstOrLast implements OrderOperationInterface { /** * Constructor. diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php index 0ddf18d61751..755f9ff73385 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -13,7 +13,7 @@ class OrderOperation { const array KNOWN_CLASSES = [ - 'absolute' => AbsoluteOrderOperation::class, + 'absolute' => FirstOrLast::class, 'relative' => RelativeOrderOperation::class, ]; -- GitLab From 41af924eda15507b3483a3210e7577bcb298e2b5 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 19:16:59 +0100 Subject: [PATCH 203/268] Make order operations internal. --- core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php | 3 +++ .../Core/Hook/OrderOperation/OrderOperationInterface.php | 3 +++ .../Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php | 3 +++ 3 files changed, 9 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php index 341b92e51a46..9a77fd5ae71f 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php @@ -4,6 +4,9 @@ namespace Drupal\Core\Hook\OrderOperation; +/** + * @internal + */ class FirstOrLast implements OrderOperationInterface { /** diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php index eaa281c8e55a..4a03c58d755b 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php @@ -4,6 +4,9 @@ namespace Drupal\Core\Hook\OrderOperation; +/** + * @internal + */ interface OrderOperationInterface { /** diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php index fc615e332037..1a5373fd971a 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php @@ -4,6 +4,9 @@ namespace Drupal\Core\Hook\OrderOperation; +/** + * @internal + */ class RelativeOrderOperation implements OrderOperationInterface { /** -- GitLab From 3209c080443cb3ac42524693caa2c812a1af25dc Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 20:51:20 +0100 Subject: [PATCH 204/268] Enhance docs, refer to 'listeners' instead of 'implementations' in docs. --- .../Core/Hook/OrderOperation/FirstOrLast.php | 4 +++- .../OrderOperation/OrderOperationInterface.php | 14 +++++++++++--- .../Hook/OrderOperation/RelativeOrderOperation.php | 14 ++++++++------ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php index 9a77fd5ae71f..73021ba2939e 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php @@ -5,6 +5,8 @@ namespace Drupal\Core\Hook\OrderOperation; /** + * Moves one listener to the start or end of the list. + * * @internal */ class FirstOrLast implements OrderOperationInterface { @@ -13,7 +15,7 @@ class FirstOrLast implements OrderOperationInterface { * Constructor. * * @param string $identifier - * Identifier of the hook implementation to move to a new position. + * Identifier of the hook listener to move to a new position. * The format is "$class::$method". * @param bool $isLast * TRUE to move to the end, FALSE to move to the start. diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php index 4a03c58d755b..4b3e4d08e19d 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php @@ -5,17 +5,25 @@ namespace Drupal\Core\Hook\OrderOperation; /** + * Operations that changes the order of hook listeners. + * + * Note that these are operations, not constraints, and operations applied + * earlier can be overridden by implementations applied later. + * * @internal */ interface OrderOperationInterface { /** - * Changes the order of a list of hook implementations. + * Changes the order of a list of hook listeners. * * @param list<string> $identifiers - * Implementation identifiers, as "$class::$method". + * Hook listener identifiers, as "$class::$method", to be changed by + * reference. + * The order operation must make sure that the array remains a list, and + * that the values are the same as before. * @param array<string, string> $module_finder - * Lookup map to find a module name for each implementation. + * Lookup map to find a module name for each listener. * This may contain more entries than $identifiers. */ public function apply(array &$identifiers, array $module_finder): void; diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php index 1a5373fd971a..243384edb6cf 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php @@ -5,6 +5,8 @@ namespace Drupal\Core\Hook\OrderOperation; /** + * Moves one listener to be called before or after other listeners. + * * @internal */ class RelativeOrderOperation implements OrderOperationInterface { @@ -13,16 +15,16 @@ class RelativeOrderOperation implements OrderOperationInterface { * Constructor. * * @param string $identifier - * Identifier of the hook implementation to move to a new position. + * Identifier of the hook listener to move to a new position. * The format is "$class::$method". * @param list<string> $modulesToOrderAgainst - * Module names the implementations of which to order against. - * @param array $identifiersToOrderAgainst - * Identifiers of implementations to order against. + * Module names of listeners to order against. + * @param list<string> $identifiersToOrderAgainst + * Identifiers of listeners to order against. * The format is "$class::$method". * @param bool $isAfter - * TRUE, if the implementation to move should be after the implementations - * to order against. + * TRUE, if the listener to move should be moved after the listener to order + * against, FALSE if it should be moved before. */ public function __construct( protected readonly string $identifier, -- GitLab From 0e2ba1f3b991b87ae666169636d759990fd5ca5f Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 20:54:00 +0100 Subject: [PATCH 205/268] Rename RelativeOrderOperation -> BeforeOrAfter. --- .../{RelativeOrderOperation.php => BeforeOrAfter.php} | 2 +- core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php | 2 +- core/lib/Drupal/Core/Hook/RelativeOrderBase.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename core/lib/Drupal/Core/Hook/OrderOperation/{RelativeOrderOperation.php => BeforeOrAfter.php} (97%) diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php similarity index 97% rename from core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php rename to core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php index 243384edb6cf..f04edd1cefa5 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/RelativeOrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php @@ -9,7 +9,7 @@ * * @internal */ -class RelativeOrderOperation implements OrderOperationInterface { +class BeforeOrAfter implements OrderOperationInterface { /** * Constructor. diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php index 755f9ff73385..9e556096df44 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -14,7 +14,7 @@ class OrderOperation { const array KNOWN_CLASSES = [ 'absolute' => FirstOrLast::class, - 'relative' => RelativeOrderOperation::class, + 'relative' => BeforeOrAfter::class, ]; /** diff --git a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php index 2bea5b352d81..f6a8bd380701 100644 --- a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php +++ b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php @@ -5,7 +5,7 @@ namespace Drupal\Core\Hook; use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; -use Drupal\Core\Hook\OrderOperation\RelativeOrderOperation; +use Drupal\Core\Hook\OrderOperation\BeforeOrAfter; /** * Orders an implementation relative to other implementations. @@ -42,7 +42,7 @@ abstract protected function isAfter(): bool; * {@inheritdoc} */ public function getOperation(string $identifier): OrderOperationInterface { - return new RelativeOrderOperation( + return new BeforeOrAfter( $identifier, $this->modules, array_map( -- GitLab From 85df3ddf3e54118de43d8c0d096e9a334ebf112d Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 21:09:01 +0100 Subject: [PATCH 206/268] Declare HookCollector and HookCollectorPass as internal. --- core/lib/Drupal/Core/Hook/HookCollector.php | 2 ++ core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php index 6c367b4430d6..6f861ee2d42e 100644 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -29,6 +29,8 @@ * * Finally, a hook_implementations_map container parameter is added. This * contains a mapping from [hook,class,method] to the module name. + * + * @internal */ class HookCollector { diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index ecb8b89a79ec..34a76ad96a5a 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -19,6 +19,8 @@ * * Finally, a hook_implementations_map container parameter is added. This * contains a mapping from [hook,class,method] to the module name. + * + * @internal */ class HookCollectorPass implements CompilerPassInterface { -- GitLab From 354d2b45683cce615ff947ee4f27e4fee09b021f Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 16 Mar 2025 23:21:26 +0100 Subject: [PATCH 207/268] Explain the weights in ->orderOperations. --- core/lib/Drupal/Core/Hook/HookCollector.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php index 6f861ee2d42e..d19610159ab8 100644 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -398,10 +398,14 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, 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) { -- GitLab From 2441c5999e37693efeaf30188bf34b9f6cb8d0f9 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 17 Mar 2025 01:54:39 +0100 Subject: [PATCH 208/268] The function name is redundant for addProceduralImplementation(). --- core/lib/Drupal/Core/Hook/HookCollector.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php index d19610159ab8..9b9e3a10d8c3 100644 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ b/core/lib/Drupal/Core/Hook/HookCollector.php @@ -426,13 +426,13 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, } if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches) && !StaticReflectionParser::hasAttribute($attributes, LegacyModuleImplementsAlter::class)) { assert($function === $matches['module'] . '_' . $matches['hook']); - $implementations[] = ['function' => $function, 'module' => $matches['module'], 'hook' => $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'], $implementation['function']); + $this->addProceduralImplementation($fileinfo, $implementation['hook'], $implementation['module']); } } if ($extension === 'inc') { @@ -471,13 +471,11 @@ protected static function filterIterator(\SplFileInfo $fileInfo, $key, \Recursiv * @param string $hook * The name of the hook. * @param string $module - * The module of the hook. Note this might be different from the module the - * function is in. - * @param string $function - * The name of function implementing the hook. + * The module implementing the hook, or on behalf of which the hook is + * implemented. */ - protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module, string $function): void { - assert($function === $module . '_' . $hook); + protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module): void { + $function = $module . '_' . $hook; if ($hook === 'hook_info') { $this->hookInfo[] = $function; } -- GitLab From 70b1758b89ad8a24868ea7ce9210cfb1bd8cb0d8 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 17 Mar 2025 02:59:11 +0100 Subject: [PATCH 209/268] Split and revamp tests for hook order. --- .../HookOrder/hk_a_test/hk_a_test.module | 7 + .../hk_a_test/src/Hook/AFormAlterHooks.php | 35 -- .../hk_b_test/src/Hook/BFormAlterHooks.php | 24 -- .../HookOrder/hk_b_test/src/Hook/BHooks.php | 5 + .../HookOrder/hk_c_test/hk_c_test.module | 7 + .../hk_c_test/src/Hook/CFormAlterHooks.php | 24 -- .../HookOrder/hk_d_test/src/Hook/DHooks.php | 5 + .../Core/Hook/HookAlterOrderTest.php | 232 +++++++++++ .../KernelTests/Core/Hook/HookOrderTest.php | 383 +----------------- .../Core/Hook/HookOrderTestTrait.php | 56 +++ 10 files changed, 329 insertions(+), 449 deletions(-) delete mode 100644 core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php delete mode 100644 core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php delete mode 100644 core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php create mode 100644 core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php create mode 100644 core/tests/Drupal/KernelTests/Core/Hook/HookOrderTestTrait.php diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module index 4405f7b017e1..d9ffedc61674 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module @@ -16,6 +16,13 @@ function hk_a_test_test_hook(): string { return __FUNCTION__; } +/** + * Implements hook_sparse_test_hook(). + */ +function hk_a_test_sparse_test_hook(): string { + return __FUNCTION__; +} + /** * Implements hook_procedural_alter(). */ diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php deleted file mode 100644 index 7ab99f0e8278..000000000000 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AFormAlterHooks.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hk_a_test\Hook; - -use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderAfter; - -/** - * Hooks for testing ordering. - */ -class AFormAlterHooks { - - #[Hook('form_alter')] - public function formAlter(array &$form): void { - $form['#calls'][] = __METHOD__; - } - - #[Hook('form_my_form_alter')] - public function myFormAlter(array &$form): void { - $form['#calls'][] = __METHOD__; - } - - #[Hook('form_alter', order: new OrderAfter(modules: ['hk_b_test']))] - public function formAlterAfterB(array &$form): void { - $form['#calls'][] = __METHOD__; - } - - #[Hook('form_my_form_alter', order: new OrderAfter(modules: ['hk_b_test']))] - public function myFormAlterAfterB(array &$form): void { - $form['#calls'][] = __METHOD__; - } - -} diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php deleted file mode 100644 index dcd47f742946..000000000000 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BFormAlterHooks.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hk_b_test\Hook; - -use Drupal\Core\Hook\Attribute\Hook; - -/** - * Hooks for testing ordering. - */ -class BFormAlterHooks { - - #[Hook('form_alter')] - public function formAlter(array &$form): void { - $form['#calls'][] = __METHOD__; - } - - #[Hook('form_my_form_alter')] - public function myFormAlter(array &$form): void { - $form['#calls'][] = __METHOD__; - } - -} diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php index 52bdb497c447..dc0d451b2978 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php @@ -16,4 +16,9 @@ public function testHook(): string { return __METHOD__; } + #[Hook('sparse_test_hook')] + public function sparseTestHook(): string { + return __METHOD__; + } + } diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module index 963f3e1695a3..d012e670d008 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module @@ -14,6 +14,13 @@ function hk_c_test_test_hook(): string { return __FUNCTION__; } +/** + * Implements hook_sparse_test_hook(). + */ +function hk_c_test_sparse_test_hook(): string { + return __FUNCTION__; +} + /** * Implements hook_procedural_alter(). */ diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php deleted file mode 100644 index 03566e881ffe..000000000000 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CFormAlterHooks.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\hk_c_test\Hook; - -use Drupal\Core\Hook\Attribute\Hook; - -/** - * Hooks for testing ordering. - */ -class CFormAlterHooks { - - #[Hook('form_alter')] - public function formAlter(array &$form): void { - $form['#calls'][] = __METHOD__; - } - - #[Hook('form_my_form_alter')] - public function myFormAlter(array &$form): void { - $form['#calls'][] = __METHOD__; - } - -} diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php index cd92b6b0cd4e..052063d67d61 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php @@ -22,4 +22,9 @@ public function testHook(): string { return __METHOD__; } + #[Hook('sparse_test_hook')] + public function sparseTestHook(): string { + return __METHOD__; + } + } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php new file mode 100644 index 000000000000..188611f492c1 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php @@ -0,0 +1,232 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\KernelTests\Core\Hook; + +use Drupal\hk_a_test\Hook\AAlterHooks; +use Drupal\hk_a_test\Hook\ModuleImplementsAlter; +use Drupal\hk_b_test\Hook\BAlterHooks; +use Drupal\hk_c_test\Hook\CAlterHooks; +use Drupal\hk_d_test\Hook\DAlterHooks; +use Drupal\KernelTests\KernelTestBase; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; + +/** + * @group Hook + */ +#[IgnoreDeprecations] +class HookAlterOrderTest extends KernelTestBase { + + use HookOrderTestTrait; + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'hk_a_test', + 'hk_b_test', + 'hk_c_test', + 'hk_d_test', + ]; + + public function testProceduralModuleImplementsAlterOrder(): void { + $this->assertAlterCallOrder($main_unaltered = [ + 'hk_a_test_procedural_alter', + 'hk_b_test_procedural_alter', + 'hk_c_test_procedural_alter', + ], 'procedural'); + + $this->assertAlterCallOrder($sub_unaltered = [ + 'hk_a_test_procedural_subtype_alter', + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_subtype_alter', + ], 'procedural_subtype'); + + $this->assertAlterCallOrder($combined_unaltered = [ + 'hk_a_test_procedural_alter', + 'hk_a_test_procedural_subtype_alter', + 'hk_b_test_procedural_alter', + 'hk_b_test_procedural_subtype_alter', + 'hk_c_test_procedural_alter', + 'hk_c_test_procedural_subtype_alter', + ], ['procedural', 'procedural_subtype']); + + $move_b_down = function (array &$implementations): void { + // Move B to the end, no matter which hook. + $group = $implementations['hk_b_test']; + unset($implementations['hk_b_test']); + $implementations['hk_b_test'] = $group; + }; + $modules = ['hk_a_test', 'hk_b_test', 'hk_c_test']; + + // Test with module B moved to the end for both hooks. + ModuleImplementsAlter::set( + function (array &$implementations, string $hook) use ($modules, $move_b_down): void { + if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { + return; + } + $this->assertSame($modules, array_keys($implementations)); + $move_b_down($implementations); + }, + ); + \Drupal::service('kernel')->rebuildContainer(); + + $this->assertAlterCallOrder($main_altered = [ + 'hk_a_test_procedural_alter', + 'hk_c_test_procedural_alter', + // The implementation of B has been moved. + 'hk_b_test_procedural_alter', + ], 'procedural'); + + $this->assertAlterCallOrder($sub_altered = [ + 'hk_a_test_procedural_subtype_alter', + 'hk_c_test_procedural_subtype_alter', + // The implementation of B has been moved. + 'hk_b_test_procedural_subtype_alter', + ], 'procedural_subtype'); + + $this->assertAlterCallOrder($combined_altered = [ + 'hk_a_test_procedural_alter', + 'hk_a_test_procedural_subtype_alter', + 'hk_c_test_procedural_alter', + 'hk_c_test_procedural_subtype_alter', + // The implementation of B has been moved. + 'hk_b_test_procedural_alter', + 'hk_b_test_procedural_subtype_alter', + ], ['procedural', 'procedural_subtype']); + + // If the altered hook is not the first one, implementations are back in + // their unaltered order. + $this->assertAlterCallOrder($main_unaltered, ['other_main_type', 'procedural']); + $this->assertAlterCallOrder($sub_unaltered, ['other_main_type', 'procedural_subtype']); + $this->assertAlterCallOrder($combined_unaltered, ['other_main_type', 'procedural', 'procedural_subtype']); + + // Test with module B moved to the end for the main hook. + ModuleImplementsAlter::set( + function (array &$implementations, string $hook) use ($modules, $move_b_down): void { + if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { + return; + } + $this->assertSame($modules, array_keys($implementations)); + if ($hook !== 'procedural_alter') { + return; + } + $move_b_down($implementations); + }, + ); + \Drupal::service('kernel')->rebuildContainer(); + + $this->assertAlterCallOrder($main_altered, 'procedural'); + $this->assertAlterCallOrder($sub_unaltered, 'procedural_subtype'); + $this->assertAlterCallOrder($combined_altered, ['procedural', 'procedural_subtype']); + + // Test with module B moved to the end for the subtype hook. + ModuleImplementsAlter::set( + function (array &$implementations, string $hook) use ($modules, $move_b_down): void { + if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { + return; + } + $this->assertSameCallList($modules, array_keys($implementations)); + if ($hook !== 'procedural_subtype_alter') { + return; + } + $move_b_down($implementations); + }, + ); + \Drupal::service('kernel')->rebuildContainer(); + + $this->assertAlterCallOrder($main_unaltered, 'procedural'); + $this->assertAlterCallOrder($sub_altered, 'procedural_subtype'); + $this->assertAlterCallOrder($combined_unaltered, ['procedural', 'procedural_subtype']); + } + + public function testProceduralOrderSideEffect(): void { + $this->enableModules(['hk_extra_test']); + // The previous test should behave exactly the same. + $this->testProceduralModuleImplementsAlterOrder(); + } + + public function testAlterOrder(): void { + $this->assertAlterCallOrder([ + CAlterHooks::class . '::testAlter', + AAlterHooks::class . '::testAlterAfterC', + DAlterHooks::class . '::testAlter', + ], 'test'); + + $this->assertAlterCallOrder([ + AAlterHooks::class . '::testSubtypeAlter', + BAlterHooks::class . '::testSubtypeAlter', + CAlterHooks::class . '::testSubtypeAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], 'test_subtype'); + + $this->assertAlterCallOrder([ + // The implementation from 'D' is gone. + AAlterHooks::class . '::testSubtypeAlter', + BAlterHooks::class . '::testSubtypeAlter', + CAlterHooks::class . '::testAlter', + CAlterHooks::class . '::testSubtypeAlter', + AAlterHooks::class . '::testAlterAfterC', + DAlterHooks::class . '::testAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], ['test', 'test_subtype']); + + $this->disableModules(['hk_b_test']); + + $this->assertAlterCallOrder([ + CAlterHooks::class . '::testAlter', + AAlterHooks::class . '::testAlterAfterC', + DAlterHooks::class . '::testAlter', + ], 'test'); + + $this->assertAlterCallOrder([ + AAlterHooks::class . '::testSubtypeAlter', + CAlterHooks::class . '::testSubtypeAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], 'test_subtype'); + + $this->assertAlterCallOrder([ + AAlterHooks::class . '::testSubtypeAlter', + CAlterHooks::class . '::testAlter', + CAlterHooks::class . '::testSubtypeAlter', + AAlterHooks::class . '::testAlterAfterC', + DAlterHooks::class . '::testAlter', + DAlterHooks::class . '::testSubtypeAlter', + ], ['test', 'test_subtype']); + } + + /** + * Asserts the call order from an alter call. + * + * Also asserts additional $type argument values that are meant to produce the + * same result. + * + * @param list<string> $expected + * Expected call list, as strings from __METHOD__ or __FUNCTION__. + * @param string|list<string> $type + * First argument to pass to ->alter(). + */ + protected function assertAlterCallOrder(array $expected, string|array $type): void { + $this->assertSameCallList( + $expected, + $this->alter($type), + ); + } + + /** + * Invokes ModuleHandler->alter() and returns the altered array. + * + * @param string|list<string> $type + * Alter type or list of alter types. + * + * @return array + * The altered array. + */ + protected function alter(string|array $type): array { + $data = []; + \Drupal::moduleHandler()->alter($type, $data); + return $data; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index e573967e162b..a1bf82a567ab 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -4,17 +4,9 @@ namespace Drupal\KernelTests\Core\Hook; -use Drupal\hk_a_test\Hook\AAlterHooks; -use Drupal\hk_a_test\Hook\AFormAlterHooks; use Drupal\hk_a_test\Hook\AHooks; -use Drupal\hk_a_test\Hook\ModuleImplementsAlter; -use Drupal\hk_b_test\Hook\BAlterHooks; -use Drupal\hk_b_test\Hook\BFormAlterHooks; use Drupal\hk_b_test\Hook\BHooks; -use Drupal\hk_c_test\Hook\CAlterHooks; -use Drupal\hk_c_test\Hook\CFormAlterHooks; use Drupal\hk_c_test\Hook\CHooks; -use Drupal\hk_d_test\Hook\DAlterHooks; use Drupal\hk_d_test\Hook\DHooks; use Drupal\KernelTests\KernelTestBase; use PHPUnit\Framework\Attributes\IgnoreDeprecations; @@ -25,6 +17,8 @@ #[IgnoreDeprecations] class HookOrderTest extends KernelTestBase { + use HookOrderTestTrait; + /** * {@inheritdoc} */ @@ -35,269 +29,6 @@ class HookOrderTest extends KernelTestBase { 'hk_d_test', ]; - public function testProceduralAlterOrder(): void { - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_b_test_procedural_alter', - 'hk_c_test_procedural_alter', - ], 'procedural'); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_subtype_alter', - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_subtype_alter', - ], 'procedural_subtype'); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_a_test_procedural_subtype_alter', - 'hk_b_test_procedural_alter', - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_alter', - 'hk_c_test_procedural_subtype_alter', - ], ['procedural', 'procedural_subtype']); - - // Test with module B moved to the end. - ModuleImplementsAlter::set( - function (array &$implementations, string $hook): void { - if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { - return; - } - $this->assertSameCallList([ - 'hk_a_test', - 'hk_b_test', - 'hk_c_test', - ], array_keys($implementations)); - // Move B to the end, no matter which hook. - $group = $implementations['hk_b_test']; - unset($implementations['hk_b_test']); - $implementations['hk_b_test'] = $group; - }, - ); - \Drupal::service('kernel')->rebuildContainer(); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_c_test_procedural_alter', - // The implementation of B has been moved. - 'hk_b_test_procedural_alter', - ], 'procedural', prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - // The implementation of B is back to its original position. - 'hk_b_test_procedural_alter', - 'hk_c_test_procedural_alter', - ], ['x', 'procedural']); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_subtype_alter', - 'hk_c_test_procedural_subtype_alter', - // The implementation of B has been moved. - 'hk_b_test_procedural_subtype_alter', - ], 'procedural_subtype', prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_subtype_alter', - // The implementation of B is back to its original position. - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_subtype_alter', - ], ['x', 'procedural_subtype'], prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_a_test_procedural_subtype_alter', - 'hk_c_test_procedural_alter', - 'hk_c_test_procedural_subtype_alter', - 'hk_b_test_procedural_alter', - 'hk_b_test_procedural_subtype_alter', - ], ['procedural', 'procedural_subtype'], prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_a_test_procedural_subtype_alter', - // The implementations of B are back to their original position. - 'hk_b_test_procedural_alter', - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_alter', - 'hk_c_test_procedural_subtype_alter', - ], ['x', 'procedural', 'procedural_subtype'], prepend_unknown_type: FALSE); - - // Test with module B moved to the end for the main hook. - ModuleImplementsAlter::set( - function (array &$implementations, string $hook): void { - if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { - return; - } - $this->assertSameCallList([ - 'hk_a_test', - 'hk_b_test', - 'hk_c_test', - ], array_keys($implementations)); - if ($hook !== 'procedural_alter') { - return; - } - // Move B to the end, no matter which hook. - $group = $implementations['hk_b_test']; - unset($implementations['hk_b_test']); - $implementations['hk_b_test'] = $group; - }, - ); - \Drupal::service('kernel')->rebuildContainer(); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_c_test_procedural_alter', - // The main hook has B last. - 'hk_b_test_procedural_alter', - ], 'procedural', prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - // The main hook has B in its original position. - 'hk_b_test_procedural_alter', - 'hk_c_test_procedural_alter', - ], ['x', 'procedural'], prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_subtype_alter', - // The subtype hook has B in its original place. - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_subtype_alter', - ], 'procedural_subtype'); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_a_test_procedural_subtype_alter', - 'hk_c_test_procedural_alter', - 'hk_c_test_procedural_subtype_alter', - // The mixed hook has B last. - 'hk_b_test_procedural_alter', - 'hk_b_test_procedural_subtype_alter', - ], ['procedural', 'procedural_subtype'], prepend_unknown_type: FALSE); - - // Test with module B moved to the end for the subtype hook. - ModuleImplementsAlter::set( - function (array &$implementations, string $hook): void { - if (!in_array($hook, ['procedural_alter', 'procedural_subtype_alter'])) { - return; - } - $this->assertSameCallList([ - 'hk_a_test', - 'hk_b_test', - 'hk_c_test', - ], array_keys($implementations)); - if ($hook !== 'procedural_subtype_alter') { - return; - } - // Move B to the end, no matter which hook. - $group = $implementations['hk_b_test']; - unset($implementations['hk_b_test']); - $implementations['hk_b_test'] = $group; - }, - ); - \Drupal::service('kernel')->rebuildContainer(); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - // The main hook has B in its original place. - 'hk_b_test_procedural_alter', - 'hk_c_test_procedural_alter', - ], 'procedural'); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_subtype_alter', - 'hk_c_test_procedural_subtype_alter', - // The subtype hook has B last. - 'hk_b_test_procedural_subtype_alter', - ], 'procedural_subtype', prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - 'hk_a_test_procedural_alter', - 'hk_a_test_procedural_subtype_alter', - // The mixed hook has B in its original place. - 'hk_b_test_procedural_alter', - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_alter', - 'hk_c_test_procedural_subtype_alter', - ], ['procedural', 'procedural_subtype']); - } - - public function testProceduralOrderSideEffect(): void { - $this->enableModules(['hk_extra_test']); - // The previous test should behave exactly the same. - $this->testProceduralAlterOrder(); - } - - public function testAlterOrder(): void { - $this->assertAlterCallOrder([ - CAlterHooks::class . '::testAlter', - AAlterHooks::class . '::testAlterAfterC', - DAlterHooks::class . '::testAlter', - ], 'test'); - - $this->assertAlterCallOrder([ - AAlterHooks::class . '::testSubtypeAlter', - BAlterHooks::class . '::testSubtypeAlter', - CAlterHooks::class . '::testSubtypeAlter', - DAlterHooks::class . '::testSubtypeAlter', - ], 'test_subtype', prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - // The implementation from 'D' is gone. - AAlterHooks::class . '::testSubtypeAlter', - BAlterHooks::class . '::testSubtypeAlter', - CAlterHooks::class . '::testAlter', - CAlterHooks::class . '::testSubtypeAlter', - AAlterHooks::class . '::testAlterAfterC', - DAlterHooks::class . '::testAlter', - DAlterHooks::class . '::testSubtypeAlter', - ], ['test', 'test_subtype']); - - $this->disableModules(['hk_b_test']); - - $this->assertAlterCallOrder([ - CAlterHooks::class . '::testAlter', - AAlterHooks::class . '::testAlterAfterC', - DAlterHooks::class . '::testAlter', - ], 'test', prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - AAlterHooks::class . '::testSubtypeAlter', - CAlterHooks::class . '::testSubtypeAlter', - DAlterHooks::class . '::testSubtypeAlter', - ], 'test_subtype', prepend_unknown_type: FALSE); - - $this->assertAlterCallOrder([ - AAlterHooks::class . '::testSubtypeAlter', - CAlterHooks::class . '::testAlter', - CAlterHooks::class . '::testSubtypeAlter', - AAlterHooks::class . '::testAlterAfterC', - DAlterHooks::class . '::testAlter', - DAlterHooks::class . '::testSubtypeAlter', - ], ['test', 'test_subtype'], prepend_unknown_type: FALSE); - } - - public function testFormAlterOrder(): void { - $this->assertSameCallList([ - AFormAlterHooks::class . '::formAlter', - BFormAlterHooks::class . '::formAlter', - AFormAlterHooks::class . '::formAlterAfterB', - CFormAlterHooks::class . '::formAlter', - ], $this->alter('form')['#calls'] ?? NULL); - - $this->assertSameCallList([ - AFormAlterHooks::class . '::formAlter', - AFormAlterHooks::class . '::myFormAlter', - BFormAlterHooks::class . '::formAlter', - BFormAlterHooks::class . '::myFormAlter', - AFormAlterHooks::class . '::myFormAlterAfterB', - AFormAlterHooks::class . '::formAlterAfterB', - CFormAlterHooks::class . '::formAlter', - CFormAlterHooks::class . '::myFormAlter', - ], $this->alter(['form', 'form_my_form'])['#calls'] ?? NULL); - } - public function testHookOrder(): void { $this->assertSameCallList( [ @@ -320,104 +51,24 @@ public function testHookOrder(): void { } /** - * Asserts the call order from an alter call. - * - * Also asserts additional $type argument values that are meant to produce the - * same result. + * Tests hook order when each module has either oop or procedural listeners. * - * @param list<string> $expected - * Expected call list, as strings from __METHOD__ or __FUNCTION__. - * @param string|list<string> $type - * First argument to pass to ->alter(). - * @param list<string|list<string>>|null $equivalent_types - * Alternative values for $type that are meant to produce the same result. - * If NULL, alternative value will be generated by appending and/ - * prepending "unknown" types, that is, types with no implementations. - * @param bool $prepend_unknown_type - * If TRUE, or if NULL and $equivalent_types is NULL, additional equivalent - * types will be generated where an unknown type is prepended. + * This would detect a possible mistake where we would first collect modules + * from all procedural and then from all oop implementations, without fixing + * the order. */ - protected function assertAlterCallOrder(array $expected, string|array $type, array|null $equivalent_types = NULL, ?bool $prepend_unknown_type = NULL): void { - if ($equivalent_types === NULL) { - $equivalent_type = []; - foreach ((array) $type as $i => $type_i) { - $equivalent_type[] = $type_i; - $equivalent_type[] = 'x_' . $i; - } - $equivalent_types = [$equivalent_type]; - $prepend_unknown_type ??= TRUE; - } - if ($prepend_unknown_type ?? FALSE) { - foreach ([(array) $type, ...$equivalent_types] as $type_i) { - $equivalent_types[] = ['x', ...$type_i]; - } - } - foreach ([$type, ...$equivalent_types] as $i => $type_i) { - $this->assertSameCallList( - $expected, - $this->alter($type_i), - $i . ': ' . json_encode($type_i), - ); - } - } - - /** - * Invokes ModuleHandler->alter() and returns the altered array. - * - * @param string|list<string> $type - * Alter type or list of alter types. - * - * @return array - * The altered array. - */ - protected function alter(string|array $type): array { - $data = []; - \Drupal::moduleHandler()->alter($type, $data); - return $data; - } - - /** - * Asserts that two lists of call strings are the same. - * - * It is meant for strings produced with __FUNCTION__ or __METHOD__. - * - * The assertion fails exactly when a regular ->assertSame() would fail, but - * it provides a more useful output on failure. - * - * @param list<string> $expected - * Expected list of strings. - * @param list<string> $actual - * Actual list of strings. - * @param string $message - * Message to pass to ->assertSame(). - */ - protected function assertSameCallList(array $expected, array $actual, string $message = ''): void { - // Format without the numeric array keys, but in a way that can be easily - // copied into the test. - $format = function (array $strings): string { - if (!$strings) { - return '[]'; - } - $parts = array_map( - static function (string $call_string) { - if (preg_match('@^(\w+\\\\)*(\w+)::(\w+)@', $call_string, $matches)) { - [,, $class_shortname, $method] = $matches; - return $class_shortname . '::class . ' . var_export('::' . $method, TRUE); - } - return var_export($call_string, TRUE); - }, - $strings, - ); - return "[\n " . implode(",\n ", $parts) . ",\n]"; - }; - $this->assertSame( - $format($expected), - $format($actual), - $message, + public function testSparseHookOrder(): void { + $this->assertSameCallList( + [ + // OOP and procedural listeners are correctly intermixed by module + // order. + 'hk_a_test_sparse_test_hook', + BHooks::class . '::sparseTestHook', + 'hk_c_test_sparse_test_hook', + DHooks::class . '::sparseTestHook', + ], + \Drupal::moduleHandler()->invokeAll('sparse_test_hook'), ); - // Finally, assert that array keys and the full class names are really the - // same, in a way that provides useful output on failure. - $this->assertSame($expected, $actual, $message); } } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTestTrait.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTestTrait.php new file mode 100644 index 000000000000..15238c7b33c7 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTestTrait.php @@ -0,0 +1,56 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\KernelTests\Core\Hook; + +/** + * @group Hook + */ +trait HookOrderTestTrait { + + /** + * Asserts that two lists of call strings are the same. + * + * It is meant for strings produced with __FUNCTION__ or __METHOD__. + * + * The assertion fails exactly when a regular ->assertSame() would fail, but + * it provides a more useful output on failure. + * + * @param list<string> $expected + * Expected list of strings. + * @param list<string> $actual + * Actual list of strings. + * @param string $message + * Message to pass to ->assertSame(). + */ + protected function assertSameCallList(array $expected, array $actual, string $message = ''): void { + // Format without the numeric array keys, but in a way that can be easily + // copied into the test. + $format = function (array $strings): string { + if (!$strings) { + return '[]'; + } + $parts = array_map( + static function (string $call_string) { + if (preg_match('@^(\w+\\\\)*(\w+)::(\w+)@', $call_string, $matches)) { + [,, $class_shortname, $method] = $matches; + return $class_shortname . '::class . ' . var_export('::' . $method, TRUE); + } + return var_export($call_string, TRUE); + }, + $strings, + ); + return "[\n " . implode(",\n ", $parts) . ",\n]"; + }; + $this->assertSame( + $format($expected), + $format($actual), + $message, + ); + // Finally, assert that array keys and the full class names are really the + // same, in a way that provides useful output on failure. + $this->assertSame($expected, $actual, $message); + } + +} -- GitLab From 2b55b5ec315ed083c0779b7213c782d56bd5961b Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 23 Mar 2025 19:06:05 +0100 Subject: [PATCH 210/268] Improve doc comments. --- core/lib/Drupal/Core/Hook/Order.php | 3 +++ core/lib/Drupal/Core/Hook/OrderInterface.php | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/core/lib/Drupal/Core/Hook/Order.php b/core/lib/Drupal/Core/Hook/Order.php index 8b5fb245cb71..8efab495117c 100644 --- a/core/lib/Drupal/Core/Hook/Order.php +++ b/core/lib/Drupal/Core/Hook/Order.php @@ -18,6 +18,9 @@ enum Order: int implements OrderInterface { // This implementation should fire last. case Last = 0; + /** + * {@inheritdoc} + */ public function getOperation(string $identifier): OrderOperationInterface { return new FirstOrLast($identifier, $this === self::Last); } diff --git a/core/lib/Drupal/Core/Hook/OrderInterface.php b/core/lib/Drupal/Core/Hook/OrderInterface.php index 9c00a91dd0e7..f00bb417b260 100644 --- a/core/lib/Drupal/Core/Hook/OrderInterface.php +++ b/core/lib/Drupal/Core/Hook/OrderInterface.php @@ -6,9 +6,14 @@ use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; +/** + * Interface for order specifiers used in hook attributes. + */ interface OrderInterface { /** + * Gets order operations specified by this object. + * * @param string $identifier * Identifier of the implementation to move to a new position. * The format is "$class::$module". -- GitLab From 3eada8485aba5f08111cf4c4c0764bbf457ade5f Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 23 Mar 2025 17:06:44 +0100 Subject: [PATCH 211/268] Collapse HookCollector back into HookCollectorPass. --- .../Drupal/Core/Extension/ModuleHandler.php | 4 +- core/lib/Drupal/Core/Hook/HookCollector.php | 584 ------------------ .../Drupal/Core/Hook/HookCollectorPass.php | 561 ++++++++++++++++- .../Tests/Core/Hook/HookCollectorPassTest.php | 6 +- 4 files changed, 565 insertions(+), 590 deletions(-) delete mode 100644 core/lib/Drupal/Core/Hook/HookCollector.php diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 87cf47f6a801..7a2ad28bc372 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -6,7 +6,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Core\Hook\Attribute\LegacyHook; -use Drupal\Core\Hook\HookCollector; +use Drupal\Core\Hook\HookCollectorPass; use Drupal\Core\Hook\OrderOperation\OrderOperation; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -235,7 +235,7 @@ protected function add($type, $name, $path) { $this->moduleList[$name] = new Extension($this->root, $type, $pathname, $filename); $this->resetImplementations(); $paths = [$name => ['pathname' => $pathname]]; - $hook_collector = HookCollector::collectAllHookImplementations($paths); + $hook_collector = HookCollectorPass::collectAllHookImplementations($paths); // A module freshly added will not be registered on the container yet. // ProceduralCall service does not yet know about it. // Note in HookCollectorPass: diff --git a/core/lib/Drupal/Core/Hook/HookCollector.php b/core/lib/Drupal/Core/Hook/HookCollector.php deleted file mode 100644 index 9b9e3a10d8c3..000000000000 --- a/core/lib/Drupal/Core/Hook/HookCollector.php +++ /dev/null @@ -1,584 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\Core\Hook; - -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\Hook\Attribute\LegacyModuleImplementsAlter; -use Drupal\Core\Hook\Attribute\RemoveHook; -use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\Core\Hook\Attribute\StopProceduralHookScan; -use Drupal\Core\Hook\OrderOperation\OrderOperation; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * Collects and registers hook implementations. - * - * A hook implementation is a class in a Drupal\modulename\Hook namespace - * where either the class itself or the methods have a #[Hook] attribute. - * These classes are automatically registered as autowired services. - * - * Services for procedural implementation of hooks are also registered - * using the ProceduralCall class. - * - * Finally, a hook_implementations_map container parameter is added. This - * contains a mapping from [hook,class,method] to the module name. - * - * @internal - */ -class HookCollector { - - /** - * A map of include files by function name. - * - * (This is required only for BC.) - * - * @var array<string, string> - */ - protected array $includes = []; - - /** - * A list of functions implementing hook_module_implements_alter(). - * - * (This is required only for BC.) - * - * @var list<callable-string> - */ - protected array $moduleImplementsAlters = []; - - /** - * A list of functions implementing hook_hook_info(). - * - * (This is required only for BC.) - * - * @var list<callable-string> - */ - private array $hookInfo = []; - - /** - * Include files, keyed by the $group part of "/$module.$group.inc". - * - * @var array<string, list<string>> - */ - private array $groupIncludes = []; - - /** - * OOP implementation module names keyed by hook name and "$class::$method". - * - * @var array<string, array<string, string>> - */ - protected array $oopImplementations = []; - - /** - * Procedural implementation module names by hook name. - * - * @var array<string, list<string>> - */ - protected array $proceduralImplementations = []; - - /** - * Order operations grouped by hook name and weight. - * - * Operations with higher weight are applied last, which means they can - * override the changes from previous operations. - * - * @var array<string, array<int, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>>> - * - * @todo Review how to combine operations from different hooks. - */ - protected array $orderOperations = []; - - /** - * Identifiers to remove, as "$class::$method", keyed by hook name. - * - * @var array<string, list<string>> - */ - protected array $removeHookIdentifiers = []; - - /** - * Constructor. Should not be called directly. - * - * @param list<string> $modules - * Names of installed modules. - */ - protected function __construct( - protected readonly array $modules, - ) {} - - /** - * Writes collected definitions to the container builder. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * Container builder. - */ - public function writeToContainer(ContainerBuilder $container): void { - $container->register(ProceduralCall::class, ProceduralCall::class) - ->addArgument($this->includes); - - // Gather includes for each hook_hook_info group. - // We store this in $groupIncludes so moduleHandler can ensure the files - // are included runtime when the hooks are invoked. - $groupIncludes = []; - foreach ($this->hookInfo as $function) { - foreach ($function() as $hook => $info) { - if (isset($this->groupIncludes[$info['group']])) { - $groupIncludes[$hook] = $this->groupIncludes[$info['group']]; - } - } - } - - $implementationsByHook = $this->calculateImplementations(); - - static::writeImplementationsToContainer($container, $implementationsByHook); - - // Update the module handler definition. - $definition = $container->getDefinition('module_handler'); - $definition->setArgument('$groupIncludes', $groupIncludes); - - $packed_order_operations = []; - $order_operations = $this->getOrderOperations(); - foreach (preg_grep('@_alter$@', array_keys($order_operations)) as $alter_hook) { - $packed_order_operations[$alter_hook] = array_map( - OrderOperation::pack(...), - $order_operations[$alter_hook], - ); - } - $definition->setArgument('$packedOrderOperations', $packed_order_operations); - } - - /** - * Gets implementation lists with removals already applied. - * - * @return array<string, list<string>> - * Implementations, as module names keyed by hook name and - * "$class::$method". - */ - protected function getFilteredImplementations(): array { - $implementationsByHook = []; - foreach ($this->proceduralImplementations as $hook => $procedural_modules) { - foreach ($procedural_modules as $module) { - $implementationsByHook[$hook][ProceduralCall::class . '::' . $module . '_' . $hook] = $module; - } - } - foreach ($this->oopImplementations as $hook => $oopImplementations) { - if (!isset($implementationsByHook[$hook])) { - $implementationsByHook[$hook] = $oopImplementations; - } - else { - $implementationsByHook[$hook] += $oopImplementations; - } - } - foreach ($this->removeHookIdentifiers as $hook => $identifiers_to_remove) { - foreach ($identifiers_to_remove as $identifier_to_remove) { - unset($implementationsByHook[$hook][$identifier_to_remove]); - } - if (empty($implementationsByHook[$hook])) { - unset($implementationsByHook[$hook]); - } - } - return $implementationsByHook; - } - - /** - * Calculates the ordered implementations. - * - * @return array<string, array<string, string>> - * Implementations, as module names keyed by hook name and "$class::$method" - * identifier. - */ - protected function calculateImplementations(): array { - $implementationsByHookOrig = $this->getFilteredImplementations(); - - // List of hooks and modules formatted for hook_module_implements_alter(). - $moduleImplementsMap = []; - foreach ($implementationsByHookOrig as $hook => $hookImplementations) { - foreach (array_intersect($this->modules, $hookImplementations) as $module) { - $moduleImplementsMap[$hook][$module] = ''; - } - } - - $implementationsByHook = []; - foreach ($moduleImplementsMap as $hook => $moduleImplements) { - // Process all hook_module_implements_alter() for build time ordering. - foreach ($this->moduleImplementsAlters as $alter) { - $alter($moduleImplements, $hook); - } - foreach ($moduleImplements as $module => $v) { - foreach (array_keys($implementationsByHookOrig[$hook], $module, TRUE) as $identifier) { - $implementationsByHook[$hook][$identifier] = $module; - } - } - } - - foreach ($this->getOrderOperations() as $hook => $order_operations) { - self::applyOrderOperations($implementationsByHook[$hook], $order_operations); - } - - return $implementationsByHook; - } - - /** - * Gets order operations by hook. - * - * @return array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>> - * Order operations by hook name. - */ - protected function getOrderOperations(): array { - $operations_by_hook = []; - foreach ($this->orderOperations as $hook => $order_operations_by_weight) { - ksort($order_operations_by_weight); - $operations_by_hook[$hook] = array_merge(...$order_operations_by_weight); - } - return $operations_by_hook; - } - - /** - * Applies order operations to a hook implementation list. - * - * @param array<string, string> $implementation_list - * Implementation list for one hook, as module names keyed by - * "$class::$method" identifiers. - * @param list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface> $order_operations - * A list of order operations for one hook. - */ - public static function applyOrderOperations(array &$implementation_list, array $order_operations): void { - $module_finder = $implementation_list; - $identifiers = array_keys($module_finder); - foreach ($order_operations as $order_operation) { - $order_operation->apply($identifiers, $module_finder); - assert($identifiers === array_unique($identifiers)); - $identifiers = array_values($identifiers); - } - // Clean up after bad order operations. - $identifiers = array_combine($identifiers, $identifiers); - $identifiers = array_intersect_key($identifiers, $module_finder); - $implementation_list = array_replace($identifiers, $module_finder); - } - - /** - * Writes all implementations to the container. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * The container builder. - * @param array<string, array<string, string>> $implementationsByHook - * Implementations, as module names keyed by hook name and "$class::$method" - * identifier. - */ - protected static function writeImplementationsToContainer( - ContainerBuilder $container, - array $implementationsByHook, - ): void { - $map = []; - $tagsInfoByClass = []; - foreach ($implementationsByHook as $hook => $hookImplementations) { - $priority = 0; - foreach ($hookImplementations as $class_and_method => $module) { - [$class, $method] = explode('::', $class_and_method); - $tagsInfoByClass[$class][] = [ - 'event' => "drupal_hook.$hook", - 'method' => $method, - 'priority' => $priority, - ]; - --$priority; - $map[$hook][$class][$method] = $module; - } - } - - foreach ($tagsInfoByClass as $class => $tagsInfo) { - if ($container->hasDefinition($class)) { - $definition = $container->findDefinition($class); - } - else { - $definition = $container - ->register($class, $class) - ->setAutowired(TRUE); - } - foreach ($tagsInfo as $tag_info) { - $definition->addTag('kernel.event_listener', $tag_info); - } - } - - $container->setParameter('hook_implementations_map', $map); - } - - /** - * Collects all hook implementations. - * - * @param array<string, array{pathname: string}> $module_list - * An associative array. Keys are the module names, values are relevant - * info yml file path. - * @param list<string> $skipProceduralModules - * Module names that are known to not have procedural hook implementations. - * - * @return static - * A HookCollectorPass instance holding all hook implementations and - * include file information. - * - * @internal - * This method is only used by ModuleHandler. - * - * @todo Pass only $container when ModuleHandler::add() is removed - * @see https://www.drupal.org/project/drupal/issues/3481778 - */ - public static function collectAllHookImplementations(array $module_list, array $skipProceduralModules = []): static { - $modules = array_keys($module_list); - $modules_by_length = $modules; - usort($modules_by_length, static fn ($a, $b) => strlen($b) - strlen($a)); - $known_modules_pattern = implode('|', array_map( - static fn ($x) => preg_quote($x, '/'), - $modules_by_length, - )); - $module_preg = '/^(?<function>(?<module>' . $known_modules_pattern . ')_(?!preprocess_)(?!update_\d)(?<hook>[a-zA-Z0-9_\x80-\xff]+$))/'; - $collector = new static($modules); - foreach ($module_list as $module => $info) { - $skip_procedural = in_array($module, $skipProceduralModules); - $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural); - } - return $collector; - } - - /** - * Collects procedural and Attribute hook implementations. - * - * @param string $dir - * The directory in which the module resides. - * @param string $module - * The name of the module. - * @param string $module_preg - * A regular expression matching every module, longer module names are - * matched first. - * @param bool $skip_procedural - * Skip the procedural check for the current module. - */ - protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural): void { - $hook_file_cache = FileCacheFactory::get('hook_implementations'); - $procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $module_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 === 'module' || $extension === 'profile') && !$iterator->getDepth() && !$skip_procedural) { - // There is an expectation for all modules and profiles to be loaded. - // .module and .profile files are not supposed to be in subdirectories. - // These need to be loaded even if the module has no procedural hooks. - include_once $filename; - } - 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, StopProceduralHookScan::class)) { - break; - } - if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches) && !StaticReflectionParser::hasAttribute($attributes, LegacyModuleImplementsAlter::class)) { - 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; - } - } - } - } - - /** - * Filter iterator callback. Allows include files and .php files in src/Hook. - */ - protected static function filterIterator(\SplFileInfo $fileInfo, $key, \RecursiveDirectoryIterator $iterator): bool { - $sub_path_name = $iterator->getSubPathname(); - $extension = $fileInfo->getExtension(); - if (str_starts_with($sub_path_name, 'src/Hook/')) { - return $iterator->isDir() || $extension === 'php'; - } - if ($iterator->isDir()) { - if ($sub_path_name === 'src' || $sub_path_name === 'src/Hook') { - return TRUE; - } - // glob() doesn't support streams but scandir() does. - return !in_array($fileInfo->getFilename(), ['tests', 'js', 'css']) && !array_filter(scandir($key), static fn ($filename) => str_ends_with($filename, '.info.yml')); - } - return in_array($extension, ['inc', 'module', 'profile', 'install']); - } - - /** - * Adds a procedural hook implementation. - * - * @param \SplFileInfo $fileinfo - * The file this procedural implementation is in. - * @param string $hook - * The name of the hook. - * @param string $module - * The module implementing the hook, or on behalf of which the hook is - * implemented. - */ - protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module): void { - $function = $module . '_' . $hook; - if ($hook === 'hook_info') { - $this->hookInfo[] = $function; - } - elseif ($hook === 'module_implements_alter') { - $message = "$function without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788"; - @trigger_error($message, E_USER_DEPRECATED); - $this->moduleImplementsAlters[] = $function; - } - $this->proceduralImplementations[$hook][] = $module; - if ($fileinfo->getExtension() !== 'module') { - $this->includes[$function] = $fileinfo->getPathname(); - } - } - - /** - * This method is only to be used by ModuleHandler. - * - * @todo remove when ModuleHandler::add() is removed. - * @see https://www.drupal.org/project/drupal/issues/3481778 - * - * @internal - */ - public function loadAllIncludes(): void { - foreach ($this->includes as $include) { - include_once $include; - } - } - - /** - * This method is only to be used by ModuleHandler. - * - * @return array<string, array<string, array<class-string, array<string, string>>>> - * Hook implementation method names keyed by hook, module, class and method. - * - * @todo remove when ModuleHandler::add() is removed. - * See https://www.drupal.org/project/drupal/issues/3481778 - * - * @internal - */ - public function getImplementations(): array { - $implementationsByHook = $this->getFilteredImplementations(); - - // List of modules implementing hooks with the implementation details. - $implementations = []; - - foreach ($implementationsByHook as $hook => $hookImplementations) { - foreach ($this->modules as $module) { - foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { - [$class, $method] = explode('::', $identifier); - $implementations[$hook][$module][$class][$method] = $method; - } - } - } - - return $implementations; - } - - /** - * Checks for hooks which can't be supported in classes. - * - * @param \Drupal\Core\Hook\Attribute\Hook $hookAttribute - * The hook to check. - * @param class-string $class - * The class the hook is implemented on. - */ - public static function checkForProceduralOnlyHooks(Hook $hookAttribute, string $class): void { - $staticDenyHooks = [ - 'hook_info', - 'install', - 'module_implements_alter', - 'requirements', - 'schema', - 'uninstall', - 'update_last_removed', - 'install_tasks', - 'install_tasks_alter', - ]; - - if (in_array($hookAttribute->hook, $staticDenyHooks) || preg_match('/^(post_update_|preprocess_|update_\d+$)/', $hookAttribute->hook)) { - throw new \LogicException("The hook $hookAttribute->hook on class $class does not support attributes and must remain procedural."); - } - } - - /** - * Get attribute instances from class and method reflections. - * - * @param \ReflectionClass $reflectionClass - * A reflected class. - * - * @return array<string, list<\Drupal\Core\Hook\HookAttributeInterface>> - * Lists of Hook attribute instances by method name. - */ - protected static function getAttributeInstances(\ReflectionClass $reflectionClass): array { - $attributes = []; - $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); - $reflections[] = $reflectionClass; - foreach ($reflections as $reflection) { - if ($reflectionAttributes = $reflection->getAttributes(HookAttributeInterface::class, \ReflectionAttribute::IS_INSTANCEOF)) { - $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; - $attributes[$method] = array_map(static fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflectionAttributes); - } - } - return $attributes; - } - -} diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 34a76ad96a5a..ed46838fc025 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -4,6 +4,17 @@ namespace Drupal\Core\Hook; +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\Hook\Attribute\LegacyModuleImplementsAlter; +use Drupal\Core\Hook\Attribute\RemoveHook; +use Drupal\Core\Hook\Attribute\ReOrderHook; +use Drupal\Core\Hook\Attribute\StopProceduralHookScan; +use Drupal\Core\Hook\OrderOperation\OrderOperation; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -24,6 +35,84 @@ */ class HookCollectorPass implements CompilerPassInterface { + /** + * A map of include files by function name. + * + * (This is required only for BC.) + * + * @var array<string, string> + */ + protected array $includes = []; + + /** + * A list of functions implementing hook_module_implements_alter(). + * + * (This is required only for BC.) + * + * @var list<callable-string> + */ + protected array $moduleImplementsAlters = []; + + /** + * A list of functions implementing hook_hook_info(). + * + * (This is required only for BC.) + * + * @var list<callable-string> + */ + private array $hookInfo = []; + + /** + * Include files, keyed by the $group part of "/$module.$group.inc". + * + * @var array<string, list<string>> + */ + private array $groupIncludes = []; + + /** + * OOP implementation module names keyed by hook name and "$class::$method". + * + * @var array<string, array<string, string>> + */ + protected array $oopImplementations = []; + + /** + * Procedural implementation module names by hook name. + * + * @var array<string, list<string>> + */ + protected array $proceduralImplementations = []; + + /** + * Order operations grouped by hook name and weight. + * + * Operations with higher weight are applied last, which means they can + * override the changes from previous operations. + * + * @var array<string, array<int, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>>> + * + * @todo Review how to combine operations from different hooks. + */ + protected array $orderOperations = []; + + /** + * Identifiers to remove, as "$class::$method", keyed by hook name. + * + * @var array<string, list<string>> + */ + protected array $removeHookIdentifiers = []; + + /** + * Constructor. Should not be called directly. + * + * @param list<string> $modules + * Names of installed modules. + * When used as a compiler pass, this parameter should be omitted. + */ + protected function __construct( + protected readonly array $modules = [], + ) {} + /** * {@inheritdoc} */ @@ -34,9 +123,479 @@ public function process(ContainerBuilder $container): void { array_keys($module_list), static fn (string $module) => !empty($parameters["$module.hooks_converted"]), ); - $collector = HookCollector::collectAllHookImplementations($module_list, $skip_procedural_modules); + $collector = HookCollectorPass::collectAllHookImplementations($module_list, $skip_procedural_modules); $collector->writeToContainer($container); } + /** + * Writes collected definitions to the container builder. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * Container builder. + */ + public function writeToContainer(ContainerBuilder $container): void { + $container->register(ProceduralCall::class, ProceduralCall::class) + ->addArgument($this->includes); + + // Gather includes for each hook_hook_info group. + // We store this in $groupIncludes so moduleHandler can ensure the files + // are included runtime when the hooks are invoked. + $groupIncludes = []; + foreach ($this->hookInfo as $function) { + foreach ($function() as $hook => $info) { + if (isset($this->groupIncludes[$info['group']])) { + $groupIncludes[$hook] = $this->groupIncludes[$info['group']]; + } + } + } + + $implementationsByHook = $this->calculateImplementations(); + + static::writeImplementationsToContainer($container, $implementationsByHook); + + // Update the module handler definition. + $definition = $container->getDefinition('module_handler'); + $definition->setArgument('$groupIncludes', $groupIncludes); + + $packed_order_operations = []; + $order_operations = $this->getOrderOperations(); + foreach (preg_grep('@_alter$@', array_keys($order_operations)) as $alter_hook) { + $packed_order_operations[$alter_hook] = array_map( + OrderOperation::pack(...), + $order_operations[$alter_hook], + ); + } + $definition->setArgument('$packedOrderOperations', $packed_order_operations); + } + + /** + * Gets implementation lists with removals already applied. + * + * @return array<string, list<string>> + * Implementations, as module names keyed by hook name and + * "$class::$method". + */ + protected function getFilteredImplementations(): array { + $implementationsByHook = []; + foreach ($this->proceduralImplementations as $hook => $procedural_modules) { + foreach ($procedural_modules as $module) { + $implementationsByHook[$hook][ProceduralCall::class . '::' . $module . '_' . $hook] = $module; + } + } + foreach ($this->oopImplementations as $hook => $oopImplementations) { + if (!isset($implementationsByHook[$hook])) { + $implementationsByHook[$hook] = $oopImplementations; + } + else { + $implementationsByHook[$hook] += $oopImplementations; + } + } + foreach ($this->removeHookIdentifiers as $hook => $identifiers_to_remove) { + foreach ($identifiers_to_remove as $identifier_to_remove) { + unset($implementationsByHook[$hook][$identifier_to_remove]); + } + if (empty($implementationsByHook[$hook])) { + unset($implementationsByHook[$hook]); + } + } + return $implementationsByHook; + } + + /** + * Calculates the ordered implementations. + * + * @return array<string, array<string, string>> + * Implementations, as module names keyed by hook name and "$class::$method" + * identifier. + */ + protected function calculateImplementations(): array { + $implementationsByHookOrig = $this->getFilteredImplementations(); + + // List of hooks and modules formatted for hook_module_implements_alter(). + $moduleImplementsMap = []; + foreach ($implementationsByHookOrig as $hook => $hookImplementations) { + foreach (array_intersect($this->modules, $hookImplementations) as $module) { + $moduleImplementsMap[$hook][$module] = ''; + } + } + + $implementationsByHook = []; + foreach ($moduleImplementsMap as $hook => $moduleImplements) { + // Process all hook_module_implements_alter() for build time ordering. + foreach ($this->moduleImplementsAlters as $alter) { + $alter($moduleImplements, $hook); + } + foreach ($moduleImplements as $module => $v) { + foreach (array_keys($implementationsByHookOrig[$hook], $module, TRUE) as $identifier) { + $implementationsByHook[$hook][$identifier] = $module; + } + } + } + + foreach ($this->getOrderOperations() as $hook => $order_operations) { + self::applyOrderOperations($implementationsByHook[$hook], $order_operations); + } + + return $implementationsByHook; + } + + /** + * Gets order operations by hook. + * + * @return array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>> + * Order operations by hook name. + */ + protected function getOrderOperations(): array { + $operations_by_hook = []; + foreach ($this->orderOperations as $hook => $order_operations_by_weight) { + ksort($order_operations_by_weight); + $operations_by_hook[$hook] = array_merge(...$order_operations_by_weight); + } + return $operations_by_hook; + } + + /** + * Applies order operations to a hook implementation list. + * + * @param array<string, string> $implementation_list + * Implementation list for one hook, as module names keyed by + * "$class::$method" identifiers. + * @param list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface> $order_operations + * A list of order operations for one hook. + */ + public static function applyOrderOperations(array &$implementation_list, array $order_operations): void { + $module_finder = $implementation_list; + $identifiers = array_keys($module_finder); + foreach ($order_operations as $order_operation) { + $order_operation->apply($identifiers, $module_finder); + assert($identifiers === array_unique($identifiers)); + $identifiers = array_values($identifiers); + } + // Clean up after bad order operations. + $identifiers = array_combine($identifiers, $identifiers); + $identifiers = array_intersect_key($identifiers, $module_finder); + $implementation_list = array_replace($identifiers, $module_finder); + } + + /** + * Writes all implementations to the container. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * The container builder. + * @param array<string, array<string, string>> $implementationsByHook + * Implementations, as module names keyed by hook name and "$class::$method" + * identifier. + */ + protected static function writeImplementationsToContainer( + ContainerBuilder $container, + array $implementationsByHook, + ): void { + $map = []; + $tagsInfoByClass = []; + foreach ($implementationsByHook as $hook => $hookImplementations) { + $priority = 0; + foreach ($hookImplementations as $class_and_method => $module) { + [$class, $method] = explode('::', $class_and_method); + $tagsInfoByClass[$class][] = [ + 'event' => "drupal_hook.$hook", + 'method' => $method, + 'priority' => $priority, + ]; + --$priority; + $map[$hook][$class][$method] = $module; + } + } + + foreach ($tagsInfoByClass as $class => $tagsInfo) { + if ($container->hasDefinition($class)) { + $definition = $container->findDefinition($class); + } + else { + $definition = $container + ->register($class, $class) + ->setAutowired(TRUE); + } + foreach ($tagsInfo as $tag_info) { + $definition->addTag('kernel.event_listener', $tag_info); + } + } + + $container->setParameter('hook_implementations_map', $map); + } + + /** + * Collects all hook implementations. + * + * @param array<string, array{pathname: string}> $module_list + * An associative array. Keys are the module names, values are relevant + * info yml file path. + * @param list<string> $skipProceduralModules + * Module names that are known to not have procedural hook implementations. + * + * @return static + * A HookCollectorPass instance holding all hook implementations and + * include file information. + * + * @internal + * This method is only used by ModuleHandler. + * + * @todo Pass only $container when ModuleHandler::add() is removed + * @see https://www.drupal.org/project/drupal/issues/3481778 + */ + public static function collectAllHookImplementations(array $module_list, array $skipProceduralModules = []): static { + $modules = array_keys($module_list); + $modules_by_length = $modules; + usort($modules_by_length, static fn ($a, $b) => strlen($b) - strlen($a)); + $known_modules_pattern = implode('|', array_map( + static fn ($x) => preg_quote($x, '/'), + $modules_by_length, + )); + $module_preg = '/^(?<function>(?<module>' . $known_modules_pattern . ')_(?!preprocess_)(?!update_\d)(?<hook>[a-zA-Z0-9_\x80-\xff]+$))/'; + $collector = new static($modules); + foreach ($module_list as $module => $info) { + $skip_procedural = in_array($module, $skipProceduralModules); + $collector->collectModuleHookImplementations(dirname($info['pathname']), $module, $module_preg, $skip_procedural); + } + return $collector; + } + + /** + * Collects procedural and Attribute hook implementations. + * + * @param string $dir + * The directory in which the module resides. + * @param string $module + * The name of the module. + * @param string $module_preg + * A regular expression matching every module, longer module names are + * matched first. + * @param bool $skip_procedural + * Skip the procedural check for the current module. + */ + protected function collectModuleHookImplementations($dir, $module, $module_preg, bool $skip_procedural): void { + $hook_file_cache = FileCacheFactory::get('hook_implementations'); + $procedural_hook_file_cache = FileCacheFactory::get('procedural_hook_implementations:' . $module_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 === 'module' || $extension === 'profile') && !$iterator->getDepth() && !$skip_procedural) { + // There is an expectation for all modules and profiles to be loaded. + // .module and .profile files are not supposed to be in subdirectories. + // These need to be loaded even if the module has no procedural hooks. + include_once $filename; + } + 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, StopProceduralHookScan::class)) { + break; + } + if (!StaticReflectionParser::hasAttribute($attributes, LegacyHook::class) && preg_match($module_preg, $function, $matches) && !StaticReflectionParser::hasAttribute($attributes, LegacyModuleImplementsAlter::class)) { + 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; + } + } + } + } + + /** + * Filter iterator callback. Allows include files and .php files in src/Hook. + */ + protected static function filterIterator(\SplFileInfo $fileInfo, $key, \RecursiveDirectoryIterator $iterator): bool { + $sub_path_name = $iterator->getSubPathname(); + $extension = $fileInfo->getExtension(); + if (str_starts_with($sub_path_name, 'src/Hook/')) { + return $iterator->isDir() || $extension === 'php'; + } + if ($iterator->isDir()) { + if ($sub_path_name === 'src' || $sub_path_name === 'src/Hook') { + return TRUE; + } + // glob() doesn't support streams but scandir() does. + return !in_array($fileInfo->getFilename(), ['tests', 'js', 'css']) && !array_filter(scandir($key), static fn ($filename) => str_ends_with($filename, '.info.yml')); + } + return in_array($extension, ['inc', 'module', 'profile', 'install']); + } + + /** + * Adds a procedural hook implementation. + * + * @param \SplFileInfo $fileinfo + * The file this procedural implementation is in. + * @param string $hook + * The name of the hook. + * @param string $module + * The module implementing the hook, or on behalf of which the hook is + * implemented. + */ + protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $hook, string $module): void { + $function = $module . '_' . $hook; + if ($hook === 'hook_info') { + $this->hookInfo[] = $function; + } + elseif ($hook === 'module_implements_alter') { + $message = "$function without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788"; + @trigger_error($message, E_USER_DEPRECATED); + $this->moduleImplementsAlters[] = $function; + } + $this->proceduralImplementations[$hook][] = $module; + if ($fileinfo->getExtension() !== 'module') { + $this->includes[$function] = $fileinfo->getPathname(); + } + } + + /** + * This method is only to be used by ModuleHandler. + * + * @todo remove when ModuleHandler::add() is removed. + * @see https://www.drupal.org/project/drupal/issues/3481778 + * + * @internal + */ + public function loadAllIncludes(): void { + foreach ($this->includes as $include) { + include_once $include; + } + } + + /** + * This method is only to be used by ModuleHandler. + * + * @return array<string, array<string, array<class-string, array<string, string>>>> + * Hook implementation method names keyed by hook, module, class and method. + * + * @todo remove when ModuleHandler::add() is removed. + * See https://www.drupal.org/project/drupal/issues/3481778 + * + * @internal + */ + public function getImplementations(): array { + $implementationsByHook = $this->getFilteredImplementations(); + + // List of modules implementing hooks with the implementation details. + $implementations = []; + + foreach ($implementationsByHook as $hook => $hookImplementations) { + foreach ($this->modules as $module) { + foreach (array_keys($hookImplementations, $module, TRUE) as $identifier) { + [$class, $method] = explode('::', $identifier); + $implementations[$hook][$module][$class][$method] = $method; + } + } + } + + return $implementations; + } + + /** + * Checks for hooks which can't be supported in classes. + * + * @param \Drupal\Core\Hook\Attribute\Hook $hookAttribute + * The hook to check. + * @param class-string $class + * The class the hook is implemented on. + */ + public static function checkForProceduralOnlyHooks(Hook $hookAttribute, string $class): void { + $staticDenyHooks = [ + 'hook_info', + 'install', + 'module_implements_alter', + 'requirements', + 'schema', + 'uninstall', + 'update_last_removed', + 'install_tasks', + 'install_tasks_alter', + ]; + + if (in_array($hookAttribute->hook, $staticDenyHooks) || preg_match('/^(post_update_|preprocess_|update_\d+$)/', $hookAttribute->hook)) { + throw new \LogicException("The hook $hookAttribute->hook on class $class does not support attributes and must remain procedural."); + } + } + + /** + * Get attribute instances from class and method reflections. + * + * @param \ReflectionClass $reflectionClass + * A reflected class. + * + * @return array<string, list<\Drupal\Core\Hook\HookAttributeInterface>> + * Lists of Hook attribute instances by method name. + */ + protected static function getAttributeInstances(\ReflectionClass $reflectionClass): array { + $attributes = []; + $reflections = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); + $reflections[] = $reflectionClass; + foreach ($reflections as $reflection) { + if ($reflectionAttributes = $reflection->getAttributes(HookAttributeInterface::class, \ReflectionAttribute::IS_INSTANCEOF)) { + $method = $reflection instanceof \ReflectionMethod ? $reflection->getName() : '__invoke'; + $attributes[$method] = array_map(static fn (\ReflectionAttribute $ra) => $ra->newInstance(), $reflectionAttributes); + } + } + return $attributes; + } + } diff --git a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php index 2bd30610cb19..0c079336c1e6 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php @@ -22,8 +22,8 @@ class HookCollectorPassTest extends UnitTestCase { use GroupIncludesTestTrait; /** - * @covers \Drupal\Core\Hook\HookCollector::collectAllHookImplementations - * @covers \Drupal\Core\Hook\HookCollector::filterIterator + * @covers \Drupal\Core\Hook\HookCollectorPass::collectAllHookImplementations + * @covers \Drupal\Core\Hook\HookCollectorPass::filterIterator */ public function testCollectAllHookImplementations(): void { vfsStream::setup('drupal_root'); @@ -72,7 +72,7 @@ function test_module_should_be_skipped(); /** * @covers ::process - * @covers \Drupal\Core\Hook\HookCollector::collectModuleHookImplementations + * @covers \Drupal\Core\Hook\HookCollectorPass::collectModuleHookImplementations */ public function testGroupIncludes(): void { $module_filenames = self::setupGroupIncludes(); -- GitLab From f57bdf59c9cb7ef5cef0a2d2106a68cdbbddd665 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 23 Mar 2025 17:19:44 +0100 Subject: [PATCH 212/268] Reorder properties to reduce the diff in HookCollectorPass. --- .../Drupal/Core/Hook/HookCollectorPass.php | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index ed46838fc025..6da58826a3a6 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -36,71 +36,71 @@ class HookCollectorPass implements CompilerPassInterface { /** - * A map of include files by function name. - * - * (This is required only for BC.) + * OOP implementation module names keyed by hook name and "$class::$method". * - * @var array<string, string> + * @var array<string, array<string, string>> */ - protected array $includes = []; + protected array $oopImplementations = []; /** - * A list of functions implementing hook_module_implements_alter(). - * - * (This is required only for BC.) + * Procedural implementation module names by hook name. * - * @var list<callable-string> + * @var array<string, list<string>> */ - protected array $moduleImplementsAlters = []; + protected array $proceduralImplementations = []; /** - * A list of functions implementing hook_hook_info(). + * Order operations grouped by hook name and weight. * - * (This is required only for BC.) + * Operations with higher weight are applied last, which means they can + * override the changes from previous operations. * - * @var list<callable-string> + * @var array<string, array<int, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>>> + * + * @todo Review how to combine operations from different hooks. */ - private array $hookInfo = []; + protected array $orderOperations = []; /** - * Include files, keyed by the $group part of "/$module.$group.inc". + * Identifiers to remove, as "$class::$method", keyed by hook name. * * @var array<string, list<string>> */ - private array $groupIncludes = []; + protected array $removeHookIdentifiers = []; /** - * OOP implementation module names keyed by hook name and "$class::$method". + * A map of include files by function name. * - * @var array<string, array<string, string>> + * (This is required only for BC.) + * + * @var array<string, string> */ - protected array $oopImplementations = []; + protected array $includes = []; /** - * Procedural implementation module names by hook name. + * A list of functions implementing hook_module_implements_alter(). * - * @var array<string, list<string>> + * (This is required only for BC.) + * + * @var list<callable-string> */ - protected array $proceduralImplementations = []; + protected array $moduleImplementsAlters = []; /** - * Order operations grouped by hook name and weight. - * - * Operations with higher weight are applied last, which means they can - * override the changes from previous operations. + * A list of functions implementing hook_hook_info(). * - * @var array<string, array<int, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>>> + * (This is required only for BC.) * - * @todo Review how to combine operations from different hooks. + * @var list<callable-string> */ - protected array $orderOperations = []; + private array $hookInfo = []; /** - * Identifiers to remove, as "$class::$method", keyed by hook name. + * Include files, keyed by the $group part of "/$module.$group.inc". * * @var array<string, list<string>> */ - protected array $removeHookIdentifiers = []; + private array $groupIncludes = []; /** * Constructor. Should not be called directly. -- GitLab From 0b3d29e27e0588d48c68bc25dcabb13b56c8c05a Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 23 Mar 2025 19:03:51 +0100 Subject: [PATCH 213/268] Use a base class for pack/unpack of order operations. Inspired by @GhostOfDrupalPast. --- .../Drupal/Core/Hook/HookCollectorPass.php | 6 ++-- .../Hook/OrderOperation/BeforeOrAfter.php | 9 +----- .../Core/Hook/OrderOperation/FirstOrLast.php | 9 +----- .../Hook/OrderOperation/OrderOperation.php | 32 ++++++++----------- 4 files changed, 19 insertions(+), 37 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 6da58826a3a6..91b9e70b3e10 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -103,13 +103,13 @@ class HookCollectorPass implements CompilerPassInterface { private array $groupIncludes = []; /** - * Constructor. Should not be called directly. + * Constructor. * * @param list<string> $modules * Names of installed modules. * When used as a compiler pass, this parameter should be omitted. */ - protected function __construct( + public function __construct( protected readonly array $modules = [], ) {} @@ -162,7 +162,7 @@ public function writeToContainer(ContainerBuilder $container): void { $order_operations = $this->getOrderOperations(); foreach (preg_grep('@_alter$@', array_keys($order_operations)) as $alter_hook) { $packed_order_operations[$alter_hook] = array_map( - OrderOperation::pack(...), + fn (OrderOperation $operation) => $operation->pack(), $order_operations[$alter_hook], ); } diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php b/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php index f04edd1cefa5..0e0533ddb212 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php @@ -9,7 +9,7 @@ * * @internal */ -class BeforeOrAfter implements OrderOperationInterface { +class BeforeOrAfter extends OrderOperation { /** * Constructor. @@ -33,13 +33,6 @@ public function __construct( protected readonly bool $isAfter, ) {} - /** - * {@inheritdoc} - */ - public function pack(): array { - return get_object_vars($this); - } - /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php index 73021ba2939e..b09d38e0f566 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php @@ -9,7 +9,7 @@ * * @internal */ -class FirstOrLast implements OrderOperationInterface { +class FirstOrLast extends OrderOperation { /** * Constructor. @@ -25,13 +25,6 @@ public function __construct( protected readonly bool $isLast, ) {} - /** - * {@inheritdoc} - */ - public function pack(): array { - return get_object_vars($this); - } - /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php index 9e556096df44..265f4b60562b 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -5,31 +5,27 @@ namespace Drupal\Core\Hook\OrderOperation; /** - * Static methods related to order operations. - * - * These should be used instead of serialize() or unserialize(), to avoid - * security issues with unserialize(), and for additional validation. + * Base class for order operations. */ -class OrderOperation { +abstract class OrderOperation implements OrderOperationInterface{ const array KNOWN_CLASSES = [ - 'absolute' => FirstOrLast::class, - 'relative' => BeforeOrAfter::class, + FirstOrLast::class, + BeforeOrAfter::class, ]; /** * Serializes an order operation object. * - * @param \Drupal\Core\Hook\OrderOperation\OrderOperationInterface $operation - * Order operation object. - * * @return array * Packed operation. */ - public static function pack(OrderOperationInterface $operation): array { - $type = array_search(get_class($operation), static::KNOWN_CLASSES) - ?: throw new \InvalidArgumentException('Unsupported order operation class ' . get_class($operation)); - return [$type, $operation->pack()]; + final public function pack(): array { + $type_index = array_search(get_class($this), self::KNOWN_CLASSES); + if ($type_index === FALSE) { + throw new \LogicException(sprintf('Unknown subclass %s of internal class %s.', static::class, self::class)); + } + return [$type_index, get_object_vars($this)]; } /** @@ -41,10 +37,10 @@ public static function pack(OrderOperationInterface $operation): array { * @return \Drupal\Core\Hook\OrderOperation\OrderOperationInterface * Unpacked operation. */ - public static function unpack(array $packed_operation): OrderOperationInterface { - [$type, $args] = $packed_operation; - $class = static::KNOWN_CLASSES[$type] - ?? throw new \InvalidArgumentException('Unsupported order operation type ' . $type); + final public static function unpack(array $packed_operation): OrderOperationInterface { + [$type_index, $args] = $packed_operation; + $class = static::KNOWN_CLASSES[$type_index] + ?? throw new \InvalidArgumentException('Unsupported order operation type index ' . $type_index); return new $class(...$args); } -- GitLab From d8e73176dd33a2d844bc4a0d9aa7e9480b3665e1 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 23 Mar 2025 19:45:54 +0100 Subject: [PATCH 214/268] Drop OrderOperationInterface. --- .../Drupal/Core/Extension/ModuleHandler.php | 4 +- .../Drupal/Core/Hook/HookCollectorPass.php | 6 +-- core/lib/Drupal/Core/Hook/Order.php | 4 +- core/lib/Drupal/Core/Hook/OrderInterface.php | 6 +-- .../Hook/OrderOperation/OrderOperation.php | 6 +-- .../OrderOperationInterface.php | 40 ------------------- .../Drupal/Core/Hook/RelativeOrderBase.php | 4 +- 7 files changed, 15 insertions(+), 55 deletions(-) delete mode 100644 core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 7a2ad28bc372..a45272ea7e18 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -86,7 +86,7 @@ class ModuleHandler implements ModuleHandlerInterface { /** * Ordering rules by hook name. * - * @var array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>> + * @var array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperation>> */ protected array $orderingRules = []; @@ -598,7 +598,7 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook * @param string $hook * Hook name. * - * @return list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface> + * @return list<\Drupal\Core\Hook\OrderOperation\OrderOperation> * List of order operations for the hook. */ protected function getHookOrderingRules(string $hook): array { diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 91b9e70b3e10..d3e72d243d37 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -55,7 +55,7 @@ class HookCollectorPass implements CompilerPassInterface { * Operations with higher weight are applied last, which means they can * override the changes from previous operations. * - * @var array<string, array<int, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>>> + * @var array<string, array<int, list<\Drupal\Core\Hook\OrderOperation\OrderOperation>>> * * @todo Review how to combine operations from different hooks. */ @@ -243,7 +243,7 @@ protected function calculateImplementations(): array { /** * Gets order operations by hook. * - * @return array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface>> + * @return array<string, list<\Drupal\Core\Hook\OrderOperation\OrderOperation>> * Order operations by hook name. */ protected function getOrderOperations(): array { @@ -261,7 +261,7 @@ protected function getOrderOperations(): array { * @param array<string, string> $implementation_list * Implementation list for one hook, as module names keyed by * "$class::$method" identifiers. - * @param list<\Drupal\Core\Hook\OrderOperation\OrderOperationInterface> $order_operations + * @param list<\Drupal\Core\Hook\OrderOperation\OrderOperation> $order_operations * A list of order operations for one hook. */ public static function applyOrderOperations(array &$implementation_list, array $order_operations): void { diff --git a/core/lib/Drupal/Core/Hook/Order.php b/core/lib/Drupal/Core/Hook/Order.php index 8efab495117c..05b274fd5587 100644 --- a/core/lib/Drupal/Core/Hook/Order.php +++ b/core/lib/Drupal/Core/Hook/Order.php @@ -5,7 +5,7 @@ namespace Drupal\Core\Hook; use Drupal\Core\Hook\OrderOperation\FirstOrLast; -use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; +use Drupal\Core\Hook\OrderOperation\OrderOperation; /** * Set this implementation to be first or last. @@ -21,7 +21,7 @@ enum Order: int implements OrderInterface { /** * {@inheritdoc} */ - public function getOperation(string $identifier): OrderOperationInterface { + public function getOperation(string $identifier): OrderOperation { return new FirstOrLast($identifier, $this === self::Last); } diff --git a/core/lib/Drupal/Core/Hook/OrderInterface.php b/core/lib/Drupal/Core/Hook/OrderInterface.php index f00bb417b260..5df6a4db13f2 100644 --- a/core/lib/Drupal/Core/Hook/OrderInterface.php +++ b/core/lib/Drupal/Core/Hook/OrderInterface.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook; -use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; +use Drupal\Core\Hook\OrderOperation\OrderOperation; /** * Interface for order specifiers used in hook attributes. @@ -18,9 +18,9 @@ interface OrderInterface { * Identifier of the implementation to move to a new position. * The format is "$class::$module". * - * @return \Drupal\Core\Hook\OrderOperation\OrderOperationInterface + * @return \Drupal\Core\Hook\OrderOperation\OrderOperation * Order operation to apply to a hook implementation list. */ - public function getOperation(string $identifier): OrderOperationInterface; + public function getOperation(string $identifier): OrderOperation; } diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php index 265f4b60562b..0655de003a08 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -7,7 +7,7 @@ /** * Base class for order operations. */ -abstract class OrderOperation implements OrderOperationInterface{ +abstract class OrderOperation { const array KNOWN_CLASSES = [ FirstOrLast::class, @@ -34,10 +34,10 @@ final public function pack(): array { * @param array $packed_operation * Packed operation. * - * @return \Drupal\Core\Hook\OrderOperation\OrderOperationInterface + * @return self * Unpacked operation. */ - final public static function unpack(array $packed_operation): OrderOperationInterface { + final public static function unpack(array $packed_operation): self { [$type_index, $args] = $packed_operation; $class = static::KNOWN_CLASSES[$type_index] ?? throw new \InvalidArgumentException('Unsupported order operation type index ' . $type_index); diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php deleted file mode 100644 index 4b3e4d08e19d..000000000000 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperationInterface.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -declare(strict_types = 1); - -namespace Drupal\Core\Hook\OrderOperation; - -/** - * Operations that changes the order of hook listeners. - * - * Note that these are operations, not constraints, and operations applied - * earlier can be overridden by implementations applied later. - * - * @internal - */ -interface OrderOperationInterface { - - /** - * Changes the order of a list of hook listeners. - * - * @param list<string> $identifiers - * Hook listener identifiers, as "$class::$method", to be changed by - * reference. - * The order operation must make sure that the array remains a list, and - * that the values are the same as before. - * @param array<string, string> $module_finder - * Lookup map to find a module name for each listener. - * This may contain more entries than $identifiers. - */ - public function apply(array &$identifiers, array $module_finder): void; - - /** - * Packs the object properties. - * - * @return array - * An array to pass as arguments to the constructor. - * Keys can be parameter names or indices. - */ - public function pack(): array; - -} diff --git a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php index f6a8bd380701..8120e897c171 100644 --- a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php +++ b/core/lib/Drupal/Core/Hook/RelativeOrderBase.php @@ -4,8 +4,8 @@ namespace Drupal\Core\Hook; -use Drupal\Core\Hook\OrderOperation\OrderOperationInterface; use Drupal\Core\Hook\OrderOperation\BeforeOrAfter; +use Drupal\Core\Hook\OrderOperation\OrderOperation; /** * Orders an implementation relative to other implementations. @@ -41,7 +41,7 @@ abstract protected function isAfter(): bool; /** * {@inheritdoc} */ - public function getOperation(string $identifier): OrderOperationInterface { + public function getOperation(string $identifier): OrderOperation { return new BeforeOrAfter( $identifier, $this->modules, -- GitLab From 7138837353e6de6fc99488e38561ed3556f76fc9 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sun, 23 Mar 2025 20:45:48 +0100 Subject: [PATCH 215/268] Fix problems with new $logger parameter for ModuleHandler::__construct(). Changes: - Make the parameter optional, and move it after required parameters. - Use a mock logger in tests. - Add deprecation as in https://www.drupal.org/about/core/policies/core-change-policies/how-to-deprecate#how-constructor-additions --- core/core.services.yml | 2 +- core/lib/Drupal/Core/Extension/ModuleHandler.php | 8 +++++++- .../KernelTests/Core/Plugin/PluginTestBase.php | 3 ++- .../Tests/Core/Extension/ModuleHandlerTest.php | 12 +++++++++--- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index b7ea7bbc6598..6c3e8abb48b5 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -653,7 +653,7 @@ services: arguments: ['@container.namespaces', '@cache.discovery', '@module_handler'] module_handler: class: Drupal\Core\Extension\ModuleHandler - arguments: ['%app.root%', '%container.modules%', '@event_dispatcher', '@logger.channel.default', '%hook_implementations_map%'] + arguments: ['%app.root%', '%container.modules%', '@event_dispatcher', '%hook_implementations_map%', '@logger.channel.default'] Drupal\Core\Extension\ModuleHandlerInterface: '@module_handler' module_installer: class: Drupal\Core\Extension\ModuleInstaller diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index a45272ea7e18..b4f65c1b2119 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -103,6 +103,8 @@ class ModuleHandler implements ModuleHandlerInterface { * The event dispatcher. * @param array<string, array<class-string, array<string, string>>> $hookImplementationsMap * An array keyed by hook, classname, method and the value is the module. + * @param \Psr\Log\LoggerInterface $logger + * A logger. * @param array<string, list<string>> $groupIncludes * Lists of *.inc file paths that contain procedural implementations, keyed * by hook name. @@ -116,8 +118,8 @@ public function __construct( $root, array $module_list, protected EventDispatcherInterface $eventDispatcher, - protected readonly LoggerInterface $logger, protected array $hookImplementationsMap, + protected ?LoggerInterface $logger = NULL, protected array $groupIncludes = [], protected array $packedOrderOperations = [], ) { @@ -126,6 +128,10 @@ public function __construct( foreach ($module_list as $name => $module) { $this->moduleList[$name] = new Extension($this->root, $module['type'], $module['pathname'], $module['filename']); } + if ($this->logger === NULL) { + @trigger_error('Calling ' . __METHOD__ . '() without the $logger argument is deprecated in drupal:11.2.0 and it will be required in drupal:12.0.0. See https://www.drupal.org/node/3515207', E_USER_DEPRECATED); + $this->logger = \Drupal::service('logger.channel.default'); + } } /** diff --git a/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php b/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php index fcac2ff73aef..2619e166752f 100644 --- a/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php +++ b/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php @@ -10,6 +10,7 @@ use Drupal\plugin_test\Plugin\MockBlockManager; use Drupal\plugin_test\Plugin\DefaultsTestPluginManager; use Drupal\Core\Extension\ModuleHandler; +use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -80,7 +81,7 @@ protected function setUp(): void { // as derivatives and ReflectionFactory. $this->testPluginManager = new TestPluginManager(); $this->mockBlockManager = new MockBlockManager(); - $module_handler = new ModuleHandler($this->root, [], $this->createMock(EventDispatcherInterface::class), []); + $module_handler = new ModuleHandler($this->root, [], $this->createMock(EventDispatcherInterface::class), [], $this->createMock(LoggerInterface::class)); $this->defaultsTestPluginManager = new DefaultsTestPluginManager($module_handler); // The expected plugin definitions within each manager. Several tests assert diff --git a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php index 7e688874bf33..1bc3b0d8dd1f 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php @@ -10,6 +10,7 @@ use Drupal\Core\Extension\ProceduralCall; use Drupal\Tests\UnitTestCase; use Drupal\Tests\Core\GroupIncludesTestTrait; +use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -71,7 +72,7 @@ protected function getModuleHandler($modules = [], $implementations = [], $loadA 'filename' => file_exists("$this->root/$path/$filename") ? $filename : NULL, ]; } - $moduleHandler = new ModuleHandler($this->root, $moduleList, $this->eventDispatcher, $implementations); + $moduleHandler = new ModuleHandler($this->root, $moduleList, $this->eventDispatcher, $implementations, $this->createMock(LoggerInterface::class)); if ($loadAll) { $moduleHandler->loadAll(); } @@ -133,6 +134,7 @@ public function testModuleReloading(): void { 'filename' => 'module_handler_test.module', ], ], $this->eventDispatcher, [], + $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['load']) ->getMock(); @@ -197,6 +199,7 @@ public function testSetModuleList(): void { $module_handler = $this->getMockBuilder(ModuleHandler::class) ->setConstructorArgs([ $this->root, [], $this->eventDispatcher, [], + $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['resetImplementations']) ->getMock(); @@ -227,6 +230,7 @@ public function testAddModule(): void { $module_handler = $this->getMockBuilder(ModuleHandler::class) ->setConstructorArgs([ $this->root, [], $this->eventDispatcher, [], + $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['resetImplementations']) ->getMock(); @@ -251,6 +255,7 @@ public function testAddProfile(): void { $module_handler = $this->getMockBuilder(ModuleHandler::class) ->setConstructorArgs([ $this->root, [], $this->eventDispatcher, [], + $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['resetImplementations']) ->getMock(); @@ -289,6 +294,7 @@ public function testLoadAllIncludes(): void { 'filename' => 'module_handler_test.module', ], ], $this->eventDispatcher, [], + $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['loadInclude']) ->getMock(); @@ -388,7 +394,7 @@ function some_method(): void { }; $implementations['some_hook'][get_class($c)]['some_method'] = 'some_module'; - $module_handler = new ModuleHandler($this->root, [], $this->eventDispatcher, $implementations, []); + $module_handler = new ModuleHandler($this->root, [], $this->eventDispatcher, $implementations, $this->createMock(LoggerInterface::class), []); $module_handler->setModuleList(['some_module' => TRUE]); $r = new \ReflectionObject($module_handler); @@ -434,7 +440,7 @@ public function testGetModuleDirectories(): void { public function testGroupIncludes(): void { self::setupGroupIncludes(); $this->expectDeprecation('Autoloading hooks in the file (vfs://drupal_root/test_module.tokens.inc) is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Move the functions in this file to either the .module file or other appropriate location. See https://www.drupal.org/node/3489765'); - $moduleHandler = new ModuleHandler('', [], new EventDispatcher(), [], self::GROUP_INCLUDES); + $moduleHandler = new ModuleHandler('', [], new EventDispatcher(), [], $this->createMock(LoggerInterface::class), self::GROUP_INCLUDES); $this->assertFalse(function_exists('_test_module_helper')); $moduleHandler->invokeAll('token_info'); $this->assertTrue(function_exists('_test_module_helper')); -- GitLab From 066bc4c2c7910844674e93ab65b95e01c000b57b Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 24 Mar 2025 21:44:24 +0100 Subject: [PATCH 216/268] Restore the old reOrderModulesForAlter(). --- .../Drupal/Core/Extension/ModuleHandler.php | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index b4f65c1b2119..ebba5b8ef32d 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -512,25 +512,14 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook } } } - // Build an array to pass to hook_module_implements_alter(). - // The initial order is the one from 'container.modules' service parameter. - $module_implements = array_fill_keys(array_intersect( - array_keys($this->moduleList), - array_keys($listeners_by_module), - ), FALSE); - // Call hook_module_implements_alter() with the main hook. - // That hook was designed in older Drupal versions where all hook - // implementations were procedural, and each module could implement each - // hook only once. - // This call to ->alter() does not cause infinite recursion, because it is - // called with only one alter type, so we don't end up in this line again. - $this->alter('module_implements', $module_implements, $main_hook); + $modules = array_keys($listeners_by_module); + $modules = $this->reOrderModulesForAlter($modules, $main_hook); // Convert the list into a different structure to pass to the hook order // operations. $listeners_by_identifier = []; $modules_by_identifier = []; $identifiers = []; - foreach (array_keys($module_implements) as $module) { + foreach ($modules as $module) { foreach ($listeners_by_module[$module] ?? [] as $listener) { $identifier = is_array($listener) ? get_class($listener[0]) . '::' . $listener[1] @@ -614,6 +603,32 @@ protected function getHookOrderingRules(string $hook): array { ); } + /** + * Reorder modules for alters. + * + * @param array $modules + * A list of modules. + * @param string $hook + * The hook being worked on, for example form_alter. + * + * @return array + * The list, potentially reordered and changed by + * hook_module_implements_alter(). + */ + protected function reOrderModulesForAlter(array $modules, string $hook): array { + // Order by module order first. + $modules = array_intersect(array_keys($this->moduleList), $modules); + // Alter expects the module list to be in the keys. + $implementations = array_fill_keys($modules, FALSE); + // Let modules adjust the order solely based on the primary hook. This + // ensures the same module order regardless of whether this block + // runs. Calling $this->alter() recursively in this way does not + // result in an infinite loop, because this call is for a single + // $type, so we won't end up in this method again. + $this->alter('module_implements', $implementations, $hook); + return array_keys($implementations); + } + /** * {@inheritdoc} */ -- GitLab From 7c46241077fbbab16fc036d2fd962ccd5ed20ce7 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Mon, 24 Mar 2025 22:25:33 +0100 Subject: [PATCH 217/268] Undo a local refactoring in ModuleHandler::add(). --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index ebba5b8ef32d..8f3af9731d8a 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -240,8 +240,7 @@ protected function add($type, $name, $path) { $filename = file_exists($php_file_path) ? "$name.$type" : NULL; $this->moduleList[$name] = new Extension($this->root, $type, $pathname, $filename); $this->resetImplementations(); - $paths = [$name => ['pathname' => $pathname]]; - $hook_collector = HookCollectorPass::collectAllHookImplementations($paths); + $hook_collector = HookCollectorPass::collectAllHookImplementations([$name => ['pathname' => $pathname]]); // A module freshly added will not be registered on the container yet. // ProceduralCall service does not yet know about it. // Note in HookCollectorPass: -- GitLab From 710c99a19cdc5a76512d09b6f9c0acb90c51e749 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 25 Mar 2025 13:15:39 +0100 Subject: [PATCH 218/268] Use trigger_error() instead of logging in ModuleHandler. --- core/core.services.yml | 2 +- .../lib/Drupal/Core/Extension/ModuleHandler.php | 17 +++++------------ .../KernelTests/Core/Plugin/PluginTestBase.php | 3 +-- .../Tests/Core/Extension/ModuleHandlerTest.php | 12 +++--------- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index 6c3e8abb48b5..55d8d2ebc984 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -653,7 +653,7 @@ services: arguments: ['@container.namespaces', '@cache.discovery', '@module_handler'] module_handler: class: Drupal\Core\Extension\ModuleHandler - arguments: ['%app.root%', '%container.modules%', '@event_dispatcher', '%hook_implementations_map%', '@logger.channel.default'] + arguments: ['%app.root%', '%container.modules%', '@event_dispatcher', '%hook_implementations_map%'] Drupal\Core\Extension\ModuleHandlerInterface: '@module_handler' module_installer: class: Drupal\Core\Extension\ModuleInstaller diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 8f3af9731d8a..4419504578e7 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -3,12 +3,12 @@ namespace Drupal\Core\Extension; use Drupal\Component\Graph\Graph; +use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\HookCollectorPass; use Drupal\Core\Hook\OrderOperation\OrderOperation; -use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -103,8 +103,6 @@ class ModuleHandler implements ModuleHandlerInterface { * The event dispatcher. * @param array<string, array<class-string, array<string, string>>> $hookImplementationsMap * An array keyed by hook, classname, method and the value is the module. - * @param \Psr\Log\LoggerInterface $logger - * A logger. * @param array<string, list<string>> $groupIncludes * Lists of *.inc file paths that contain procedural implementations, keyed * by hook name. @@ -119,7 +117,6 @@ public function __construct( array $module_list, protected EventDispatcherInterface $eventDispatcher, protected array $hookImplementationsMap, - protected ?LoggerInterface $logger = NULL, protected array $groupIncludes = [], protected array $packedOrderOperations = [], ) { @@ -128,10 +125,6 @@ public function __construct( foreach ($module_list as $name => $module) { $this->moduleList[$name] = new Extension($this->root, $module['type'], $module['pathname'], $module['filename']); } - if ($this->logger === NULL) { - @trigger_error('Calling ' . __METHOD__ . '() without the $logger argument is deprecated in drupal:11.2.0 and it will be required in drupal:12.0.0. See https://www.drupal.org/node/3515207', E_USER_DEPRECATED); - $this->logger = \Drupal::service('logger.channel.default'); - } } /** @@ -541,22 +534,22 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook // hook. // The module is mostly irrelevant for alter hooks, except for its // impact on ordering. - $this->logger->warning( + trigger_error((string) new FormattableMarkup( 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call, on behalf of different modules @module and @other_module. Only one instance will be part of the implementation list for this hook combination. For the purpose of ordering, the module @module will be used.', [ ...$log_message_replacements, '@module' => "'$module'", '@other_module' => "'$other_module'", ], - ); + ), E_USER_WARNING); } else { // There is no conflict, but probably one or more redundant #[Hook] // attributes should be removed. - $this->logger->notice( + trigger_error((string) new FormattableMarkup( 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call. Only one instance will be part of the implementation list for this hook combination.', $log_message_replacements, - ); + ), E_USER_NOTICE); } // Don't add an identifier more than once. continue; diff --git a/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php b/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php index 2619e166752f..fcac2ff73aef 100644 --- a/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php +++ b/core/tests/Drupal/KernelTests/Core/Plugin/PluginTestBase.php @@ -10,7 +10,6 @@ use Drupal\plugin_test\Plugin\MockBlockManager; use Drupal\plugin_test\Plugin\DefaultsTestPluginManager; use Drupal\Core\Extension\ModuleHandler; -use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -81,7 +80,7 @@ protected function setUp(): void { // as derivatives and ReflectionFactory. $this->testPluginManager = new TestPluginManager(); $this->mockBlockManager = new MockBlockManager(); - $module_handler = new ModuleHandler($this->root, [], $this->createMock(EventDispatcherInterface::class), [], $this->createMock(LoggerInterface::class)); + $module_handler = new ModuleHandler($this->root, [], $this->createMock(EventDispatcherInterface::class), []); $this->defaultsTestPluginManager = new DefaultsTestPluginManager($module_handler); // The expected plugin definitions within each manager. Several tests assert diff --git a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php index 1bc3b0d8dd1f..83fb1ebb5ffe 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php @@ -10,7 +10,6 @@ use Drupal\Core\Extension\ProceduralCall; use Drupal\Tests\UnitTestCase; use Drupal\Tests\Core\GroupIncludesTestTrait; -use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -72,7 +71,7 @@ protected function getModuleHandler($modules = [], $implementations = [], $loadA 'filename' => file_exists("$this->root/$path/$filename") ? $filename : NULL, ]; } - $moduleHandler = new ModuleHandler($this->root, $moduleList, $this->eventDispatcher, $implementations, $this->createMock(LoggerInterface::class)); + $moduleHandler = new ModuleHandler($this->root, $moduleList, $this->eventDispatcher, $implementations); if ($loadAll) { $moduleHandler->loadAll(); } @@ -134,7 +133,6 @@ public function testModuleReloading(): void { 'filename' => 'module_handler_test.module', ], ], $this->eventDispatcher, [], - $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['load']) ->getMock(); @@ -199,7 +197,6 @@ public function testSetModuleList(): void { $module_handler = $this->getMockBuilder(ModuleHandler::class) ->setConstructorArgs([ $this->root, [], $this->eventDispatcher, [], - $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['resetImplementations']) ->getMock(); @@ -230,7 +227,6 @@ public function testAddModule(): void { $module_handler = $this->getMockBuilder(ModuleHandler::class) ->setConstructorArgs([ $this->root, [], $this->eventDispatcher, [], - $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['resetImplementations']) ->getMock(); @@ -255,7 +251,6 @@ public function testAddProfile(): void { $module_handler = $this->getMockBuilder(ModuleHandler::class) ->setConstructorArgs([ $this->root, [], $this->eventDispatcher, [], - $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['resetImplementations']) ->getMock(); @@ -294,7 +289,6 @@ public function testLoadAllIncludes(): void { 'filename' => 'module_handler_test.module', ], ], $this->eventDispatcher, [], - $this->createMock(LoggerInterface::class), ]) ->onlyMethods(['loadInclude']) ->getMock(); @@ -394,7 +388,7 @@ function some_method(): void { }; $implementations['some_hook'][get_class($c)]['some_method'] = 'some_module'; - $module_handler = new ModuleHandler($this->root, [], $this->eventDispatcher, $implementations, $this->createMock(LoggerInterface::class), []); + $module_handler = new ModuleHandler($this->root, [], $this->eventDispatcher, $implementations); $module_handler->setModuleList(['some_module' => TRUE]); $r = new \ReflectionObject($module_handler); @@ -440,7 +434,7 @@ public function testGetModuleDirectories(): void { public function testGroupIncludes(): void { self::setupGroupIncludes(); $this->expectDeprecation('Autoloading hooks in the file (vfs://drupal_root/test_module.tokens.inc) is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Move the functions in this file to either the .module file or other appropriate location. See https://www.drupal.org/node/3489765'); - $moduleHandler = new ModuleHandler('', [], new EventDispatcher(), [], $this->createMock(LoggerInterface::class), self::GROUP_INCLUDES); + $moduleHandler = new ModuleHandler('', [], new EventDispatcher(), [], self::GROUP_INCLUDES); $this->assertFalse(function_exists('_test_module_helper')); $moduleHandler->invokeAll('token_info'); $this->assertTrue(function_exists('_test_module_helper')); -- GitLab From ad2fcecec6691bc452f17c160372e508a0c7a3d3 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Tue, 25 Mar 2025 01:59:23 +0100 Subject: [PATCH 219/268] Where did the deprecation go? --- core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php index 83fb1ebb5ffe..05a064eab7df 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php @@ -106,7 +106,6 @@ public function testLoadModule(): void { * @group legacy */ public function testLoadAllModules(): void { - $this->expectDeprecation('module_handler_test_all1_module_implements_alter without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788'); $moduleList = [ 'module_handler_test_all1' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test_all1', 'module_handler_test_all2' => 'core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test_all2', @@ -359,7 +358,6 @@ public function testImplementsHookModuleEnabled(): void { * @group legacy */ public function testInvokeAll(): void { - $this->expectDeprecation('module_handler_test_all1_module_implements_alter without a #[LegacyModuleImplementsAlter] attribute is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. See https://www.drupal.org/node/3496788'); $implementations = [ 'module_handler_test_hook' => 'module_handler_test', 'module_handler_test_all1_hook' => 'module_handler_test_all1', -- GitLab From 4e531065df42cd231a2be1200ab2c26d7fae8841 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 25 Mar 2025 23:17:56 -0400 Subject: [PATCH 220/268] Assert call order instead of using globals Fix phpcs Fix cspell Simplify --- .../src/Hook/TestHookAfter.php | 4 +- .../src/Hook/TestHookBefore.php | 9 +- .../src/Hook/TestHookFirst.php | 9 +- .../src/Hook/TestHookLast.php | 5 +- .../src/Hook/TestHookOrderExtraTypes.php | 9 +- .../src/Hook/TestHookReOrderHookFirst.php | 4 +- .../src/Hook/TestHookAfter.php | 9 +- .../src/Hook/TestHookBefore.php | 4 +- .../src/Hook/TestHookFirst.php | 4 +- .../src/Hook/TestHookLast.php | 8 +- .../src/Hook/TestHookOrderExtraTypes.php | 4 +- .../src/Hook/TestHookReOrderHookLast.php | 7 +- .../src/Hook/TestHookAfterClassMethod.php | 4 +- .../src/Hook/TestHookAfterClassMethod.php | 11 +- .../src/Hook/TestHookRemove.php | 12 +- .../Core/Hook/HookCollectorPassTest.php | 153 +++++++++--------- 16 files changed, 118 insertions(+), 138 deletions(-) diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php index 9dd41b0cbf06..2bfe61bfee65 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php @@ -25,8 +25,8 @@ class TestHookAfter { * This pair tests OrderAfter. */ #[Hook('custom_hook_test_hook_after', order: new OrderAfter(['hook_order_last_alphabetically']))] - public static function hookAfter(): void { - $GLOBALS['HookAfter'] = 'HookAfter'; + public function hookAfter(): string { + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php index 3b7ce8df15ac..c67ede46f49f 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php @@ -24,12 +24,9 @@ class TestHookBefore { * This pair tests OrderBefore. */ #[Hook('custom_hook_test_hook_before')] - public static function hookBefore(): void { - // This should be run after so HookBefore should not be set. - if (!isset($GLOBALS['HookBefore'])) { - $GLOBALS['HookOutOfOrderTestingHookBefore'] = 'HookOutOfOrderTestingHookBefore'; - } - $GLOBALS['HookRanTestingHookBefore'] = 'HookRanTestingHookBefore'; + public function hookBefore(): string { + // This should be run second, there is another hook reordering before this. + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php index 31841f75549a..99501c5a5ff5 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php @@ -24,12 +24,9 @@ class TestHookFirst { * This pair tests OrderFirst. */ #[Hook('custom_hook_test_hook_first')] - public static function hookFirst(): void { - // This should be run after so HookFirst should not be set. - if (!isset($GLOBALS['HookFirst'])) { - $GLOBALS['HookOutOfOrderTestingHookFirst'] = 'HookOutOfOrderTestingHookFirst'; - } - $GLOBALS['HookRanTestingHookFirst'] = 'HookRanTestingHookFirst'; + public function hookFirst(): string { + // This should be run second, there is another hook reordering before this. + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php index b93fd5155a62..e7e1efbb897b 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php @@ -25,8 +25,9 @@ class TestHookLast { * This pair tests OrderLast. */ #[Hook('custom_hook_test_hook_last', order: Order::Last)] - public static function hookLast(): void { - $GLOBALS['HookLast'] = 'HookLast'; + public function hookLast(): string { + // This should be run after. + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php index dbf57bf3e364..fc9d1c76ee74 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php @@ -29,12 +29,9 @@ class TestHookOrderExtraTypes { modules: ['hook_order_last_alphabetically'], ) )] - public static function customHookExtraTypes(): void { - // This should be run after so HookOrderExtraTypes should not be set. - if (!isset($GLOBALS['HookOrderExtraTypes'])) { - $GLOBALS['HookOutOfOrderTestingOrderExtraTypes'] = 'HookOutOfOrderTestingOrderExtraTypes'; - } - $GLOBALS['HookRanTestingOrderExtraTypes'] = 'HookRanTestingOrderExtraTypes'; + public function customHookExtraTypes(array &$calls): void { + // This should be run after. + $calls[] = __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php index 8e48435ebae1..e9302b4fab49 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php @@ -35,11 +35,11 @@ class: TestHookReOrderHookLast::class, classesAndMethods: [[TestHookReOrderHookFirst::class, 'customHookOverride']], ) )] - public static function customHookOverride(): void { + public function customHookOverride(): string { // This normally would run first. // We override that order in hook_order_second_alphabetically. // We override, that order here with ReOrderHook. - $GLOBALS['HookRanTestingReOrderHookFirstAlpha'] = 'HookRanTestingReOrderHookFirstAlpha'; + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php index c132f940c319..c4e9a9c35aaf 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php @@ -24,12 +24,9 @@ class TestHookAfter { * This pair tests OrderAfter. */ #[Hook('custom_hook_test_hook_after')] - public static function hookAfter(): void { - // This should be run before so HookAfter should not be set. - if (isset($GLOBALS['HookAfter'])) { - $GLOBALS['HookOutOfOrderTestingHookAfter'] = 'HookOutOfOrderTestingHookAfter'; - } - $GLOBALS['HookRanTestingHookAfter'] = 'HookRanTestingHookAfter'; + public function hookAfter(): string { + // This should be run before. + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php index 03b19b10ffe9..81f6d94660d2 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php @@ -25,8 +25,8 @@ class TestHookBefore { * This pair tests OrderBefore. */ #[Hook('custom_hook_test_hook_before', order: new OrderBefore(['hook_order_first_alphabetically']))] - public static function cacheFlush(): void { - $GLOBALS['HookBefore'] = 'HookBefore'; + public function hookBefore(): string { + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php index ee659a32585f..552397056f6e 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php @@ -25,8 +25,8 @@ class TestHookFirst { * This pair tests OrderFirst. */ #[Hook('custom_hook_test_hook_first', order: Order::First)] - public static function hookFirst(): void { - $GLOBALS['HookFirst'] = 'HookFirst'; + public function hookFirst(): string { + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php index 36b32f08678b..1868385b13e5 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php @@ -24,12 +24,8 @@ class TestHookLast { * This pair tests OrderLast. */ #[Hook('custom_hook_test_hook_last')] - public static function hookLast(): void { - // This should be run before so HookLast should not be set. - if (isset($GLOBALS['HookLast'])) { - $GLOBALS['HookOutOfOrderTestingHookLast'] = 'HookOutOfOrderTestingHookLast'; - } - $GLOBALS['HookRanTestingHookLast'] = 'HookRanTestingHookLast'; + public function hookLast(): string { + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php index 2ca8a0d93f42..09793fb16497 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php @@ -24,8 +24,8 @@ class TestHookOrderExtraTypes { * This pair tests OrderAfter with ExtraTypes. */ #[Hook('custom_hook_extra_types2_alter')] - public static function customHookExtraTypes(): void { - $GLOBALS['HookOrderExtraTypes'] = 'HookOrderExtraTypes'; + public function customHookExtraTypes(array &$calls): void { + $calls[] = __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php index a46e3583609e..859de89043ea 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php @@ -25,15 +25,12 @@ class TestHookReOrderHookLast { * This pair tests ReOrderHook. */ #[Hook('custom_hook_override', order: Order::First)] - public static function customHookOverride(): void { + public function customHookOverride(): string { // This normally would run second. // We override that order here with Order::First. // We override, that order in hook_order_first_alphabetically with // ReOrderHook. - if (!isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])) { - $GLOBALS['HookOutOfOrderTestingReOrderHook'] = 'HookOutOfOrderTestingReOrderHook'; - } - $GLOBALS['HookRanTestingReOrderHookLastAlpha'] = 'HookRanTestingReOrderHookLastAlpha'; + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php index b37339173578..989e837ae7a8 100644 --- a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -30,8 +30,8 @@ class TestHookAfterClassMethod { classesAndMethods: [[TestHookAfterClassMethodForAfter::class, 'hookAfterClassMethod']], ) )] - public static function hookAfterClassMethod(): void { - $GLOBALS['HookAfterClassMethod'] = 'HookAfterMethod'; + public static function hookAfterClassMethod(): string { + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php index d69aaa546fb0..bb8d53f8f1a0 100644 --- a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -21,15 +21,12 @@ class TestHookAfterClassMethod { /** - * This pair tests #[HookAfter]. + * This pair tests OrderAfter with a passed class and method. */ #[Hook('custom_hook_test_hook_after_class_method')] - public static function hookAfterClassMethod(): void { - // This should be run before so HookAfter should not be set. - if (isset($GLOBALS['HookAfterClassMethod'])) { - $GLOBALS['HookOutOfOrderTestingHookAfterClassMethod'] = 'HookOutOfOrderTestingHookAfterClassMethod'; - } - $GLOBALS['HookRanTestingHookAfterClassMethod'] = 'HookRanTestingHookAfterClassMethod'; + public static function hookAfterClassMethod(): string { + // This should be run first since another hook overrides the natural order. + return __METHOD__; } } diff --git a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php index 3ea922ec36ce..69cbfd21c495 100644 --- a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php +++ b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php @@ -16,21 +16,23 @@ class TestHookRemove { * This hook should not be run because the next hook replaces it. */ #[Hook('custom_hook1')] - public static function hookDoNotRun(): void { - $GLOBALS['HookShouldNotRunTestRemove'] = 'HookShouldNotRunTestRemove'; + public function hookDoNotRun(array $call): string { + // This hook should not run. + return __METHOD__; } /** * This hook should run and prevent custom_hook1. */ - #[Hook('custom_hook2')] + #[Hook('custom_hook1')] #[RemoveHook( 'custom_hook1', class: TestHookRemove::class, method: 'hookDoNotRun' )] - public static function hookDoRun(): void { - $GLOBALS['HookShouldRunTestRemove'] = 'HookShouldRunTestRemove'; + public function hookDoRun(array $call): string { + // This hook should run. + return __METHOD__; } } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 10765f3a084b..7371c12e221d 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -160,17 +160,17 @@ public function testHookAttribute(): void { */ public function testHookFirst(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookFirst'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookFirst'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingHookFirst'])); + $module_installer->install(['hook_order_first_alphabetically']); + $module_installer->install(['hook_order_last_alphabetically']); $module_handler = $this->container->get('module_handler'); - $data = ['hi']; - $module_handler->invokeAll('custom_hook_test_hook_first', $data); - $this->assertTrue(isset($GLOBALS['HookFirst'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookFirst'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingHookFirst'])); + // Last alphabetically uses the Order::First enum to place it before + // the implementation it would naturally come after. + $expected_calls = [ + 'Drupal\hook_order_last_alphabetically\Hook\TestHookFirst::hookFirst', + 'Drupal\hook_order_first_alphabetically\Hook\TestHookFirst::hookFirst', + ]; + $calls = $module_handler->invokeAll('custom_hook_test_hook_first', [[]]); + $this->assertEquals($expected_calls, $calls); } /** @@ -178,17 +178,17 @@ public function testHookFirst(): void { */ public function testHookAfter(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookAfter'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfter'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingHookAfter'])); + $module_installer->install(['hook_order_first_alphabetically']); + $module_installer->install(['hook_order_last_alphabetically']); $module_handler = $this->container->get('module_handler'); - $data = ['hi']; - $module_handler->invokeAll('custom_hook_test_hook_after', $data); - $this->assertTrue(isset($GLOBALS['HookAfter'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfter'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingHookAfter'])); + // First alphabetically uses the OrderAfter to place it after + // the implementation it would naturally come before. + $expected_calls = [ + 'Drupal\hook_order_last_alphabetically\Hook\TestHookAfter::hookAfter', + 'Drupal\hook_order_first_alphabetically\Hook\TestHookAfter::hookAfter', + ]; + $calls = $module_handler->invokeAll('custom_hook_test_hook_after', [[]]); + $this->assertEquals($expected_calls, $calls); } /** @@ -196,17 +196,17 @@ public function testHookAfter(): void { */ public function testHookAfterClassMethod(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_second_order_first_alphabetically'])); - $this->assertTrue($module_installer->install(['hook_second_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookAfterClassMethod'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfterClassMethod'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingHookAfterClassMethod'])); + $module_installer->install(['hook_second_order_first_alphabetically']); + $module_installer->install(['hook_second_order_last_alphabetically']); $module_handler = $this->container->get('module_handler'); - $data = ['hi']; - $module_handler->invokeAll('custom_hook_test_hook_after_class_method', $data); - $this->assertTrue(isset($GLOBALS['HookAfterClassMethod'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookAfterClassMethod'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingHookAfterClassMethod'])); + // First alphabetically uses the OrderAfter to place it after + // the implementation it would naturally come before using call and method. + $expected_calls = [ + 'Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod::hookAfterClassMethod', + 'Drupal\hook_second_order_first_alphabetically\Hook\TestHookAfterClassMethod::hookAfterClassMethod', + ]; + $calls = $module_handler->invokeAll('custom_hook_test_hook_after_class_method', [[]]); + $this->assertEquals($expected_calls, $calls); } /** @@ -214,17 +214,17 @@ public function testHookAfterClassMethod(): void { */ public function testHookBefore(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookBefore'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookBefore'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingHookBefore'])); + $module_installer->install(['hook_order_first_alphabetically']); + $module_installer->install(['hook_order_last_alphabetically']); $module_handler = $this->container->get('module_handler'); - $data = ['hi']; - $module_handler->invokeAll('custom_hook_test_hook_before', $data); - $this->assertTrue(isset($GLOBALS['HookBefore'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookBefore'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingHookBefore'])); + // First alphabetically uses the OrderBefore to place it before + // the implementation it would naturally come after. + $expected_calls = [ + 'Drupal\hook_order_last_alphabetically\Hook\TestHookBefore::hookBefore', + 'Drupal\hook_order_first_alphabetically\Hook\TestHookBefore::hookBefore', + ]; + $calls = $module_handler->invokeAll('custom_hook_test_hook_before', [[]]); + $this->assertEquals($expected_calls, $calls); } /** @@ -232,22 +232,23 @@ public function testHookBefore(): void { */ public function testHookOrderExtraTypes(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookOrderExtraTypes'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderExtraTypes'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingOrderExtraTypes'])); + $module_installer->install(['hook_order_first_alphabetically']); + $module_installer->install(['hook_order_last_alphabetically']); $module_handler = $this->container->get('module_handler'); + // First alphabetically uses the OrderAfter to place it after + // the implementation it would naturally come before. + $expected_calls = [ + 'Drupal\hook_order_last_alphabetically\Hook\TestHookOrderExtraTypes::customHookExtraTypes', + 'Drupal\hook_order_first_alphabetically\Hook\TestHookOrderExtraTypes::customHookExtraTypes', + ]; $hooks = [ 'custom_hook', 'custom_hook_extra_types1', 'custom_hook_extra_types2', ]; - $data = ['hi']; - $module_handler->alter($hooks, $data); - $this->assertTrue(isset($GLOBALS['HookOrderExtraTypes'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingOrderExtraTypes'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingOrderExtraTypes'])); + $calls = []; + $module_handler->alter($hooks, $calls); + $this->assertEquals($expected_calls, $calls); } /** @@ -255,17 +256,17 @@ public function testHookOrderExtraTypes(): void { */ public function testHookLast(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookLast'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookLast'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingHookLast'])); + $module_installer->install(['hook_order_first_alphabetically']); + $module_installer->install(['hook_order_last_alphabetically']); $module_handler = $this->container->get('module_handler'); - $data = ['hi']; - $module_handler->invokeAll('custom_hook_test_hook_last', $data); - $this->assertTrue(isset($GLOBALS['HookLast'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingHookLast'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingHookLast'])); + // First alphabetically uses the OrderBefore to place it before + // the implementation it would naturally come after. + $expected_calls = [ + 'Drupal\hook_order_last_alphabetically\Hook\TestHookLast::hookLast', + 'Drupal\hook_order_first_alphabetically\Hook\TestHookLast::hookLast', + ]; + $calls = $module_handler->invokeAll('custom_hook_test_hook_last', [[]]); + $this->assertEquals($expected_calls, $calls); } /** @@ -274,14 +275,14 @@ public function testHookLast(): void { public function testHookRemove(): void { $module_installer = $this->container->get('module_installer'); $this->assertTrue($module_installer->install(['hook_test_remove'])); - $this->assertFalse(isset($GLOBALS['HookShouldRunTestRemove'])); - $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestRemove'])); $module_handler = $this->container->get('module_handler'); - $data = ['hi']; - $module_handler->invokeAll('custom_hook1', $data); - $module_handler->invokeAll('custom_hook2', $data); - $this->assertTrue(isset($GLOBALS['HookShouldRunTestRemove'])); - $this->assertFalse(isset($GLOBALS['HookShouldNotRunTestRemove'])); + // There are two hooks implementing custom_hook1. + // One is removed with RemoveHook so it should not run. + $expected_calls = [ + 'Drupal\hook_test_remove\Hook\TestHookRemove::hookDoRun', + ]; + $calls = $module_handler->invokeAll('custom_hook1', [[]]); + $this->assertEquals($expected_calls, $calls); } /** @@ -289,17 +290,15 @@ public function testHookRemove(): void { */ public function testHookOverride(): void { $module_installer = $this->container->get('module_installer'); - $this->assertTrue($module_installer->install(['hook_order_first_alphabetically'])); - $this->assertTrue($module_installer->install(['hook_order_last_alphabetically'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingReOrderHook'])); - $this->assertFalse(isset($GLOBALS['HookRanTestingReOrderHookLastAlpha'])); + $module_installer->install(['hook_order_first_alphabetically']); + $module_installer->install(['hook_order_last_alphabetically']); $module_handler = $this->container->get('module_handler'); - $data = ['hi']; - $module_handler->invokeAll('custom_hook_override', $data); - $this->assertTrue(isset($GLOBALS['HookRanTestingReOrderHookFirstAlpha'])); - $this->assertFalse(isset($GLOBALS['HookOutOfOrderTestingReOrderHook'])); - $this->assertTrue(isset($GLOBALS['HookRanTestingReOrderHookLastAlpha'])); + $expected_calls = [ + 'Drupal\hook_order_first_alphabetically\Hook\TestHookReOrderHookFirst::customHookOverride', + 'Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookLast::customHookOverride', + ]; + $calls = $module_handler->invokeAll('custom_hook_override', [[]]); + $this->assertEquals($expected_calls, $calls); } } -- GitLab From 3adb4b98353a54c6a3a34996a2f0929a01b719cd Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 28 Mar 2025 19:16:06 -0400 Subject: [PATCH 221/268] Clean up test arguments --- .../hook_test_remove/src/Hook/TestHookRemove.php | 4 ++-- .../Core/Hook/HookCollectorPassTest.php | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php index 69cbfd21c495..3d4e53eba0bb 100644 --- a/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php +++ b/core/modules/system/tests/modules/hook_test_remove/src/Hook/TestHookRemove.php @@ -16,7 +16,7 @@ class TestHookRemove { * This hook should not be run because the next hook replaces it. */ #[Hook('custom_hook1')] - public function hookDoNotRun(array $call): string { + public function hookDoNotRun(): string { // This hook should not run. return __METHOD__; } @@ -30,7 +30,7 @@ public function hookDoNotRun(array $call): string { class: TestHookRemove::class, method: 'hookDoNotRun' )] - public function hookDoRun(array $call): string { + public function hookDoRun(): string { // This hook should run. return __METHOD__; } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 7371c12e221d..cb825aa5133d 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -169,7 +169,7 @@ public function testHookFirst(): void { 'Drupal\hook_order_last_alphabetically\Hook\TestHookFirst::hookFirst', 'Drupal\hook_order_first_alphabetically\Hook\TestHookFirst::hookFirst', ]; - $calls = $module_handler->invokeAll('custom_hook_test_hook_first', [[]]); + $calls = $module_handler->invokeAll('custom_hook_test_hook_first'); $this->assertEquals($expected_calls, $calls); } @@ -187,7 +187,7 @@ public function testHookAfter(): void { 'Drupal\hook_order_last_alphabetically\Hook\TestHookAfter::hookAfter', 'Drupal\hook_order_first_alphabetically\Hook\TestHookAfter::hookAfter', ]; - $calls = $module_handler->invokeAll('custom_hook_test_hook_after', [[]]); + $calls = $module_handler->invokeAll('custom_hook_test_hook_after'); $this->assertEquals($expected_calls, $calls); } @@ -205,7 +205,7 @@ public function testHookAfterClassMethod(): void { 'Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod::hookAfterClassMethod', 'Drupal\hook_second_order_first_alphabetically\Hook\TestHookAfterClassMethod::hookAfterClassMethod', ]; - $calls = $module_handler->invokeAll('custom_hook_test_hook_after_class_method', [[]]); + $calls = $module_handler->invokeAll('custom_hook_test_hook_after_class_method'); $this->assertEquals($expected_calls, $calls); } @@ -223,7 +223,7 @@ public function testHookBefore(): void { 'Drupal\hook_order_last_alphabetically\Hook\TestHookBefore::hookBefore', 'Drupal\hook_order_first_alphabetically\Hook\TestHookBefore::hookBefore', ]; - $calls = $module_handler->invokeAll('custom_hook_test_hook_before', [[]]); + $calls = $module_handler->invokeAll('custom_hook_test_hook_before'); $this->assertEquals($expected_calls, $calls); } @@ -265,7 +265,7 @@ public function testHookLast(): void { 'Drupal\hook_order_last_alphabetically\Hook\TestHookLast::hookLast', 'Drupal\hook_order_first_alphabetically\Hook\TestHookLast::hookLast', ]; - $calls = $module_handler->invokeAll('custom_hook_test_hook_last', [[]]); + $calls = $module_handler->invokeAll('custom_hook_test_hook_last'); $this->assertEquals($expected_calls, $calls); } @@ -281,7 +281,7 @@ public function testHookRemove(): void { $expected_calls = [ 'Drupal\hook_test_remove\Hook\TestHookRemove::hookDoRun', ]; - $calls = $module_handler->invokeAll('custom_hook1', [[]]); + $calls = $module_handler->invokeAll('custom_hook1'); $this->assertEquals($expected_calls, $calls); } @@ -297,7 +297,7 @@ public function testHookOverride(): void { 'Drupal\hook_order_first_alphabetically\Hook\TestHookReOrderHookFirst::customHookOverride', 'Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookLast::customHookOverride', ]; - $calls = $module_handler->invokeAll('custom_hook_override', [[]]); + $calls = $module_handler->invokeAll('custom_hook_override'); $this->assertEquals($expected_calls, $calls); } -- GitLab From beefbab3dc97d41fcc7e008ae24d395fc8f27317 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Fri, 28 Mar 2025 23:21:10 +0100 Subject: [PATCH 222/268] Enhance docs for hook attributes. --- .../lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 14 +++++++++----- core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 11 +++++++---- .../Drupal/Core/Hook/HookAttributeInterface.php | 3 +++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index 0f22afc322c0..f5025afb844b 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -10,6 +10,9 @@ /** * Set the order of an already existing implementation. * + * The effect of this attribute is independent from the specific class or method + * on which it is placed. + * * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -19,14 +22,15 @@ class ReOrderHook implements HookAttributeInterface { * Constructs a ReOrderHook object. * * @param string $hook - * The hook parameter of the #Hook being modified. + * The hook for which to reorder a listener method. * @param class-string $class - * The class the implementation to modify is in. + * The class of the targeted hook listener. * @param string $method - * The method name of the #Hook being modified. If the hook attribute is - * on a class and does not have method set, then use __invoke. + * The method name of the targeted hook listener. + * If the class instance itself is the listener, this should be '__invoke'. * @param \Drupal\Core\Hook\OrderInterface $order - * Set the order of the implementation. + * Specifies a new position for the targeted hook listener relative to other + * listeners. */ public function __construct( public string $hook, diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index e75baa8960cb..c9e0ab29b53d 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -9,6 +9,9 @@ /** * Attribute for removing an implementation. * + * The effect of this attribute is independent from the specific class or method + * on which it is placed. + * * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] @@ -18,12 +21,12 @@ class RemoveHook implements HookAttributeInterface { * Constructs a RemoveHook object. * * @param string $hook - * The hook parameter of the #Hook being modified. + * The hook name from which to remove the target hook listener. * @param class-string $class - * The class the implementation to modify is in. + * The class name of the target hook listener. * @param string $method - * The method name of the #Hook being modified. If the hook attribute is - * on a class and does not have method set, then use __invoke. + * The method name of the target hook listener. + * If the class instance itself is the listener, this should be '__invoke'. */ public function __construct( public readonly string $hook, diff --git a/core/lib/Drupal/Core/Hook/HookAttributeInterface.php b/core/lib/Drupal/Core/Hook/HookAttributeInterface.php index d96db307ea14..16decb6ca09d 100644 --- a/core/lib/Drupal/Core/Hook/HookAttributeInterface.php +++ b/core/lib/Drupal/Core/Hook/HookAttributeInterface.php @@ -7,6 +7,9 @@ /** * Common interface for attributes used for hook discovery. * + * This does not imply any shared behavior, it is only used to collect all + * hook-related attributes in the same call. + * * @internal */ interface HookAttributeInterface { -- GitLab From 68dfade6534d586bb17d85d2e9293ab5e9a9d398 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 00:25:20 +0100 Subject: [PATCH 223/268] Let getCombinedListeners() not use getHookListeners(). --- .../Drupal/Core/Extension/ModuleHandler.php | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 4419504578e7..5ceec48ed4d8 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -483,27 +483,36 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { * List of implementation callables. */ protected function getCombinedListeners(string $main_hook, string ...$extra_hooks): array { - $extra_listeners_by_hook = $extra_hooks - ? array_filter(array_map( - $this->getHookListeners(...), - array_combine($extra_hooks, $extra_hooks), - )) - : []; + $main_hook_listeners = $this->getFlatHookListeners($main_hook); + if (!$extra_hooks) { + // No additional hooks were provided in the call. + return $main_hook_listeners; + } + $extra_listeners_by_hook = []; + foreach ($extra_hooks as $extra_hook) { + $extra_listeners_by_hook[$extra_hook] = $this->getFlatHookListeners($extra_hook); + } + $extra_listeners_by_hook = array_filter($extra_listeners_by_hook); if (!$extra_listeners_by_hook) { - // No extra hooks were provided in the call, or none of them has any - // listeners. - return $this->getFlatHookListeners($main_hook); + // None of the extra hooks has any listeners. + // The listeners for the main hook are already correctly ordered. + return $main_hook_listeners; } // Combine the listeners from all hooks that are part of the ->alter() call. - // At first they need to be grouped by module. - $listeners_by_module = $this->getHookListeners($main_hook); - foreach ($extra_listeners_by_hook as $extra_listeners_by_module) { - foreach ($extra_listeners_by_module as $module => $extra_listeners) { - foreach ($extra_listeners as $extra_listener) { - $listeners_by_module[$module][] = $extra_listener; - } + $listeners_by_hook = [ + $main_hook => $main_hook_listeners, + ...$extra_listeners_by_hook, + ]; + // Group the listeners by module. + $listeners_by_module = []; + foreach ($listeners_by_hook as $hook => $listeners) { + foreach ($listeners as $i => $listener) { + $module = $this->modulesByHook[$hook][$i]; + $listeners_by_module[$module][] = $listener; } } + // Order the modules by module list order and using + // hook_module_implements_alter(). $modules = array_keys($listeners_by_module); $modules = $this->reOrderModulesForAlter($modules, $main_hook); // Convert the list into a different structure to pass to the hook order -- GitLab From 4132138e468461bccf81358762d94117eafcd13d Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 01:12:09 +0100 Subject: [PATCH 224/268] Talk about implementations instead of listeners. --- core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 13 +++++++------ core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index f5025afb844b..2e2cbd34d162 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -22,15 +22,16 @@ class ReOrderHook implements HookAttributeInterface { * Constructs a ReOrderHook object. * * @param string $hook - * The hook for which to reorder a listener method. + * The hook for which to reorder an implementation. * @param class-string $class - * The class of the targeted hook listener. + * The class of the targeted hook implementation. * @param string $method - * The method name of the targeted hook listener. - * If the class instance itself is the listener, this should be '__invoke'. + * The method name of the targeted hook implementation. + * If the #[Hook] attribute is on the class itself, this should be + * '__invoke'. * @param \Drupal\Core\Hook\OrderInterface $order - * Specifies a new position for the targeted hook listener relative to other - * listeners. + * Specifies a new position for the targeted hook implementation relative to + * other implementations. */ public function __construct( public string $hook, diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index c9e0ab29b53d..7a1296f2587e 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -21,11 +21,11 @@ class RemoveHook implements HookAttributeInterface { * Constructs a RemoveHook object. * * @param string $hook - * The hook name from which to remove the target hook listener. + * The hook name from which to remove the target implementation. * @param class-string $class - * The class name of the target hook listener. + * The class name of the target hook implementation. * @param string $method - * The method name of the target hook listener. + * The method name of the target hook implementation. * If the class instance itself is the listener, this should be '__invoke'. */ public function __construct( -- GitLab From fd893af7dd920d77e1e6a4a8ada0c9a78511a37b Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 01:18:15 +0100 Subject: [PATCH 225/268] Rename $i to $index. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 5ceec48ed4d8..17dee4dd82d5 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -348,8 +348,8 @@ public function hasImplementations(string $hook, $modules = NULL): bool { * {@inheritdoc} */ public function invokeAllWith(string $hook, callable $callback): void { - foreach ($this->getFlatHookListeners($hook) as $i => $listener) { - $module = $this->modulesByHook[$hook][$i]; + foreach ($this->getFlatHookListeners($hook) as $index => $listener) { + $module = $this->modulesByHook[$hook][$index]; $callback($listener, $module); } } @@ -506,8 +506,8 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook // Group the listeners by module. $listeners_by_module = []; foreach ($listeners_by_hook as $hook => $listeners) { - foreach ($listeners as $i => $listener) { - $module = $this->modulesByHook[$hook][$i]; + foreach ($listeners as $index => $listener) { + $module = $this->modulesByHook[$hook][$index]; $listeners_by_module[$module][] = $listener; } } @@ -696,8 +696,8 @@ public function writeCache() { protected function getHookListeners(string $hook): array { if (!isset($this->invokeMap[$hook])) { $this->invokeMap[$hook] = []; - foreach ($this->getFlatHookListeners($hook) as $i => $listener) { - $module = $this->modulesByHook[$hook][$i]; + foreach ($this->getFlatHookListeners($hook) as $index => $listener) { + $module = $this->modulesByHook[$hook][$index]; $this->invokeMap[$hook][$module][] = $listener; } } -- GitLab From 0fab8505c43d83e5dbf7dd90c58087e482e63551 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Fri, 28 Mar 2025 21:10:43 -0400 Subject: [PATCH 226/268] Update OrderOperation pack and unpack --- .../Hook/OrderOperation/OrderOperation.php | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php index 0655de003a08..e5074faa58ab 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -9,27 +9,22 @@ */ abstract class OrderOperation { - const array KNOWN_CLASSES = [ - FirstOrLast::class, - BeforeOrAfter::class, - ]; - /** - * Serializes an order operation object. + * Packs an order operation object. * * @return array * Packed operation. */ final public function pack(): array { - $type_index = array_search(get_class($this), self::KNOWN_CLASSES); - if ($type_index === FALSE) { - throw new \LogicException(sprintf('Unknown subclass %s of internal class %s.', static::class, self::class)); - } - return [$type_index, get_object_vars($this)]; + $is_before_or_after = match(get_class($this)) { + BeforeOrAfter::class => TRUE, + FirstOrLast::class => FALSE, + }; + return [$is_before_or_after, get_object_vars($this)]; } /** - * Unserializes an order operation object. + * Unpacks an order operation object. * * @param array $packed_operation * Packed operation. @@ -38,9 +33,8 @@ final public function pack(): array { * Unpacked operation. */ final public static function unpack(array $packed_operation): self { - [$type_index, $args] = $packed_operation; - $class = static::KNOWN_CLASSES[$type_index] - ?? throw new \InvalidArgumentException('Unsupported order operation type index ' . $type_index); + [$is_before_or_after, $args] = $packed_operation; + $class = $is_before_or_after ? BeforeOrAfter::class : FirstOrLast::class; return new $class(...$args); } -- GitLab From 3ff0da467354b9a0fc11b5bc072ddde416716b30 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Fri, 28 Mar 2025 22:12:16 -0400 Subject: [PATCH 227/268] Move HookAttributeInterface --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 1 - .../Core/Hook/{ => Attribute}/HookAttributeInterface.php | 6 ++---- core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 1 - core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 2 -- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 3 ++- 5 files changed, 4 insertions(+), 9 deletions(-) rename core/lib/Drupal/Core/Hook/{ => Attribute}/HookAttributeInterface.php (76%) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index ef3fa1fbe651..69a2841523e4 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -4,7 +4,6 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookAttributeInterface; use Drupal\Core\Hook\OrderInterface; /** diff --git a/core/lib/Drupal/Core/Hook/HookAttributeInterface.php b/core/lib/Drupal/Core/Hook/Attribute/HookAttributeInterface.php similarity index 76% rename from core/lib/Drupal/Core/Hook/HookAttributeInterface.php rename to core/lib/Drupal/Core/Hook/Attribute/HookAttributeInterface.php index 16decb6ca09d..8a2f2413b20f 100644 --- a/core/lib/Drupal/Core/Hook/HookAttributeInterface.php +++ b/core/lib/Drupal/Core/Hook/Attribute/HookAttributeInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Core\Hook; +namespace Drupal\Core\Hook\Attribute; /** * Common interface for attributes used for hook discovery. @@ -12,6 +12,4 @@ * * @internal */ -interface HookAttributeInterface { - -} +interface HookAttributeInterface {} diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index 2e2cbd34d162..7bf7bf60ec5a 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -4,7 +4,6 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookAttributeInterface; use Drupal\Core\Hook\OrderInterface; /** diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index 7a1296f2587e..bc0a55270345 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -4,8 +4,6 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\HookAttributeInterface; - /** * Attribute for removing an implementation. * diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index d3e72d243d37..66c3db25f27f 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -8,6 +8,7 @@ use Drupal\Component\Annotation\Reflection\MockFileFinder; use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Core\Extension\ProceduralCall; +use Drupal\Core\Hook\Attribute\HookAttributeInterface; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter; @@ -582,7 +583,7 @@ public static function checkForProceduralOnlyHooks(Hook $hookAttribute, string $ * @param \ReflectionClass $reflectionClass * A reflected class. * - * @return array<string, list<\Drupal\Core\Hook\HookAttributeInterface>> + * @return array<string, list<\Drupal\Core\Hook\Attribute\HookAttributeInterface>> * Lists of Hook attribute instances by method name. */ protected static function getAttributeInstances(\ReflectionClass $reflectionClass): array { -- GitLab From 7e48af7bfd465762509897aac9199c99c19fa573 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Fri, 28 Mar 2025 22:22:27 -0400 Subject: [PATCH 228/268] Move order namespace --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 4 ++-- core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 4 ++-- core/lib/Drupal/Core/Hook/{ => Order}/Order.php | 2 +- core/lib/Drupal/Core/Hook/{ => Order}/OrderAfter.php | 2 +- core/lib/Drupal/Core/Hook/{ => Order}/OrderBefore.php | 2 +- core/lib/Drupal/Core/Hook/{ => Order}/OrderInterface.php | 2 +- core/lib/Drupal/Core/Hook/{ => Order}/RelativeOrderBase.php | 2 +- core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php | 2 +- .../content_translation/src/Hook/ContentTranslationHooks.php | 2 +- core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php | 2 +- .../layout_builder_test/src/Hook/LayoutBuilderTestHooks.php | 2 +- core/modules/navigation/src/Hook/NavigationHooks.php | 2 +- .../modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php | 2 +- .../tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php | 4 ++-- .../tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php | 2 +- .../tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php | 2 +- .../modules/HookOrder/hk_extra_test/src/Hook/Ordering.php | 2 +- .../src/Hook/TestHookAfter.php | 2 +- .../hook_order_first_alphabetically/src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderExtraTypes.php | 2 +- .../src/Hook/TestHookReOrderHookFirst.php | 2 +- .../src/Hook/TestHookBefore.php | 2 +- .../hook_order_last_alphabetically/src/Hook/TestHookFirst.php | 2 +- .../src/Hook/TestHookReOrderHookLast.php | 2 +- .../src/Hook/TestHookAfterClassMethod.php | 2 +- core/modules/workspaces/src/Hook/EntityOperations.php | 4 ++-- 26 files changed, 30 insertions(+), 30 deletions(-) rename core/lib/Drupal/Core/Hook/{ => Order}/Order.php (93%) rename core/lib/Drupal/Core/Hook/{ => Order}/OrderAfter.php (87%) rename core/lib/Drupal/Core/Hook/{ => Order}/OrderBefore.php (87%) rename core/lib/Drupal/Core/Hook/{ => Order}/OrderInterface.php (94%) rename core/lib/Drupal/Core/Hook/{ => Order}/RelativeOrderBase.php (97%) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 69a2841523e4..a9e845c1f363 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\OrderInterface; +use Drupal\Core\Hook\Order\OrderInterface; /** * Attribute for defining a class method as a hook implementation. @@ -115,7 +115,7 @@ class Hook implements HookAttributeInterface { * (optional) The module this implementation is for. This allows one module * to implement a hook on behalf of another module. Defaults to the module * the implementation is in. - * @param \Drupal\Core\Hook\OrderInterface|null $order + * @param \Drupal\Core\Hook\Order\OrderInterface|null $order * (optional) Set the order of the implementation. */ public function __construct( diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index 7bf7bf60ec5a..c7d6d991a071 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -4,7 +4,7 @@ namespace Drupal\Core\Hook\Attribute; -use Drupal\Core\Hook\OrderInterface; +use Drupal\Core\Hook\Order\OrderInterface; /** * Set the order of an already existing implementation. @@ -28,7 +28,7 @@ class ReOrderHook implements HookAttributeInterface { * The method name of the targeted hook implementation. * If the #[Hook] attribute is on the class itself, this should be * '__invoke'. - * @param \Drupal\Core\Hook\OrderInterface $order + * @param \Drupal\Core\Hook\Order\OrderInterface $order * Specifies a new position for the targeted hook implementation relative to * other implementations. */ diff --git a/core/lib/Drupal/Core/Hook/Order.php b/core/lib/Drupal/Core/Hook/Order/Order.php similarity index 93% rename from core/lib/Drupal/Core/Hook/Order.php rename to core/lib/Drupal/Core/Hook/Order/Order.php index 05b274fd5587..76640fa4b78e 100644 --- a/core/lib/Drupal/Core/Hook/Order.php +++ b/core/lib/Drupal/Core/Hook/Order/Order.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Core\Hook; +namespace Drupal\Core\Hook\Order; use Drupal\Core\Hook\OrderOperation\FirstOrLast; use Drupal\Core\Hook\OrderOperation\OrderOperation; diff --git a/core/lib/Drupal/Core/Hook/OrderAfter.php b/core/lib/Drupal/Core/Hook/Order/OrderAfter.php similarity index 87% rename from core/lib/Drupal/Core/Hook/OrderAfter.php rename to core/lib/Drupal/Core/Hook/Order/OrderAfter.php index 2a8ae3b09c92..73dfd9264755 100644 --- a/core/lib/Drupal/Core/Hook/OrderAfter.php +++ b/core/lib/Drupal/Core/Hook/Order/OrderAfter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Core\Hook; +namespace Drupal\Core\Hook\Order; /** * Set this implementation to be after others. diff --git a/core/lib/Drupal/Core/Hook/OrderBefore.php b/core/lib/Drupal/Core/Hook/Order/OrderBefore.php similarity index 87% rename from core/lib/Drupal/Core/Hook/OrderBefore.php rename to core/lib/Drupal/Core/Hook/Order/OrderBefore.php index 7b223be62142..cc79560a3d5a 100644 --- a/core/lib/Drupal/Core/Hook/OrderBefore.php +++ b/core/lib/Drupal/Core/Hook/Order/OrderBefore.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Core\Hook; +namespace Drupal\Core\Hook\Order; /** * Set this implementation to be before others. diff --git a/core/lib/Drupal/Core/Hook/OrderInterface.php b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php similarity index 94% rename from core/lib/Drupal/Core/Hook/OrderInterface.php rename to core/lib/Drupal/Core/Hook/Order/OrderInterface.php index 5df6a4db13f2..09c52e242edd 100644 --- a/core/lib/Drupal/Core/Hook/OrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Drupal\Core\Hook; +namespace Drupal\Core\Hook\Order; use Drupal\Core\Hook\OrderOperation\OrderOperation; diff --git a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php b/core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php similarity index 97% rename from core/lib/Drupal/Core/Hook/RelativeOrderBase.php rename to core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php index 8120e897c171..ebc4182c8f0e 100644 --- a/core/lib/Drupal/Core/Hook/RelativeOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Core\Hook; +namespace Drupal\Core\Hook\Order; use Drupal\Core\Hook\OrderOperation\BeforeOrAfter; use Drupal\Core\Hook\OrderOperation\OrderOperation; diff --git a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php index fdc1d7fcf423..7eb7601e3d8b 100644 --- a/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php +++ b/core/modules/ckeditor5/src/Hook/Ckeditor5Hooks.php @@ -2,7 +2,7 @@ namespace Drupal\ckeditor5\Hook; -use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Order\OrderAfter; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Render\Element; diff --git a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php index 089b0a5ffba3..c681b07c0596 100644 --- a/core/modules/content_translation/src/Hook/ContentTranslationHooks.php +++ b/core/modules/content_translation/src/Hook/ContentTranslationHooks.php @@ -17,7 +17,7 @@ use Drupal\Core\Url; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; /** * Hook implementations for content_translation. diff --git a/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php b/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php index 5be94e5e80ee..28159482d316 100644 --- a/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php +++ b/core/modules/layout_builder/src/Hook/LayoutBuilderHooks.php @@ -26,7 +26,7 @@ use Drupal\Core\Url; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; /** * Hook implementations for layout_builder. diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php index 5a2a09729bcd..397eedc8dad7 100644 --- a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Hook/LayoutBuilderTestHooks.php @@ -11,7 +11,7 @@ use Drupal\Core\Entity\Display\EntityFormDisplayInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderBefore; +use Drupal\Core\Hook\Order\OrderBefore; /** * Hook implementations for layout_builder_test. diff --git a/core/modules/navigation/src/Hook/NavigationHooks.php b/core/modules/navigation/src/Hook/NavigationHooks.php index da19ec2dc768..3b94bc845733 100644 --- a/core/modules/navigation/src/Hook/NavigationHooks.php +++ b/core/modules/navigation/src/Hook/NavigationHooks.php @@ -8,7 +8,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\RemoveHook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php index bb4eb3466c2f..b51af9543118 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php @@ -5,7 +5,7 @@ namespace Drupal\hk_a_test\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Order\OrderAfter; /** * Hooks for testing ordering. diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php index b00ad46f5d58..c9f2ce2fd9dc 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php @@ -5,8 +5,8 @@ namespace Drupal\hk_a_test\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Order; -use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Order\Order; +use Drupal\Core\Hook\Order\OrderAfter; /** * Hooks for testing ordering. diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php index 70130ca3064e..0c03cfae5765 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php @@ -5,7 +5,7 @@ namespace Drupal\hk_c_test\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; /** * Hooks for testing ordering. diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php index 052063d67d61..a6d6940b77d0 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php @@ -7,7 +7,7 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; use Drupal\hk_c_test\Hook\CHooks; /** diff --git a/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php b/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php index fec032ed59e3..230d31a8383d 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php +++ b/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php @@ -6,7 +6,7 @@ use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\Core\Hook\OrderBefore; +use Drupal\Core\Hook\Order\OrderBefore; /** * Hooks for testing ordering. diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php index 2bfe61bfee65..facdee6c591b 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Order\OrderAfter; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php index e7e1efbb897b..4600cdde5573 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php index fc9d1c76ee74..72a00327fb87 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Order\OrderAfter; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php index e9302b4fab49..e7202b42580e 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Order\OrderAfter; use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookLast; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php index 81f6d94660d2..fa602398d4d7 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderBefore; +use Drupal\Core\Hook\Order\OrderBefore; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php index 552397056f6e..cd7b9dffef64 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php index 859de89043ea..6c82bd1562c0 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php +++ b/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php @@ -5,7 +5,7 @@ namespace Drupal\hook_order_last_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Order; +use Drupal\Core\Hook\Order\Order; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php index 989e837ae7a8..634788e22066 100644 --- a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php @@ -5,7 +5,7 @@ namespace Drupal\hook_second_order_first_alphabetically\Hook; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\OrderAfter; +use Drupal\Core\Hook\Order\OrderAfter; use Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; /** diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index 915499f685eb..7a9883bccdae 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -13,8 +13,8 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\Core\Hook\Order; -use Drupal\Core\Hook\OrderBefore; +use Drupal\Core\Hook\Order\Order; +use Drupal\Core\Hook\Order\OrderBefore; use Drupal\content_moderation\Hook\ContentModerationHooks; use Drupal\workspaces\WorkspaceAssociationInterface; use Drupal\workspaces\WorkspaceInformationInterface; -- GitLab From e868a9865f9e5be4108319f2400fc843ea7cf543 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Fri, 28 Mar 2025 22:52:37 -0400 Subject: [PATCH 229/268] Clarify class comments --- .../Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php | 2 +- core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 2 +- core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php index 0940fdc012bd..ee7558b43bde 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php @@ -5,7 +5,7 @@ namespace Drupal\Core\Hook\Attribute; /** - * Defines a LegacyModuleImplementsAlter object. + * Prevents procedural hook_module_implements_alter from executing. * * This allows contrib and core to maintain legacy hook_module_implements_alter * alongside the new attribute-based ordering. This means that a contrib module diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index c7d6d991a071..d1f794cc6c49 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -7,7 +7,7 @@ use Drupal\Core\Hook\Order\OrderInterface; /** - * Set the order of an already existing implementation. + * Sets the order of an already existing implementation. * * The effect of this attribute is independent from the specific class or method * on which it is placed. diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index bc0a55270345..909fae18783a 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -5,7 +5,7 @@ namespace Drupal\Core\Hook\Attribute; /** - * Attribute for removing an implementation. + * Removes an already existing implementation. * * The effect of this attribute is independent from the specific class or method * on which it is placed. -- GitLab From 4e06883191ca88ab8fb0c2095d10481862faa893 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 14:15:49 +0100 Subject: [PATCH 230/268] Make getFlatHookListeners() internal. --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 17dee4dd82d5..23baf8721e35 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -713,6 +713,8 @@ protected function getHookListeners(string $hook): array { * * @return list<callable> * A list of hook implementation callables. + * + * @internal */ protected function getFlatHookListeners(string $hook): array { if (!isset($this->listenersByHook[$hook])) { -- GitLab From 46c2614ea8001d931de92acfd3e3be0742ad6f12 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 15:48:51 +0100 Subject: [PATCH 231/268] Extract triggerErrorForDuplicateAlterHookListener() from getCombinedListeners(). --- .../Drupal/Core/Extension/ModuleHandler.php | 87 ++++++++++++------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 23baf8721e35..a13868b1eb65 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -530,36 +530,13 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook // both of these hooks are part of the same ->alter() call, that is // almost always by mistake. if ($other_module = $modules_by_identifier[$identifier] ?? NULL) { - $log_message_replacements = [ - '@implementation' => is_array($listener) - ? ('method ' . $identifier . '()') - : ('function ' . $listener[1] . '()'), - '@hooks' => "['" . implode("', '", [$main_hook, ...$extra_hooks]) . "']", - ]; - if ($other_module !== $module) { - // There is conflicting information about on behalf of which module - // this implementation is registered. At this point we cannot even - // be sure if the module is the one from the main hook or the extra - // hook. - // The module is mostly irrelevant for alter hooks, except for its - // impact on ordering. - trigger_error((string) new FormattableMarkup( - 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call, on behalf of different modules @module and @other_module. Only one instance will be part of the implementation list for this hook combination. For the purpose of ordering, the module @module will be used.', - [ - ...$log_message_replacements, - '@module' => "'$module'", - '@other_module' => "'$other_module'", - ], - ), E_USER_WARNING); - } - else { - // There is no conflict, but probably one or more redundant #[Hook] - // attributes should be removed. - trigger_error((string) new FormattableMarkup( - 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call. Only one instance will be part of the implementation list for this hook combination.', - $log_message_replacements, - ), E_USER_NOTICE); - } + $this->triggerErrorForDuplicateAlterHookListener( + [$main_hook, ...$extra_hooks], + $module, + $other_module, + $listener, + $identifier, + ); // Don't add an identifier more than once. continue; } @@ -588,6 +565,56 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook ); } + /** + * Triggers an error on duplicate alter listeners. + * + * This is called when the same method is registered for multiple hooks, which + * are now part of the same alter call. + * + * @param list<string> $hooks + * Hook names from the ->alter() call. + * @param string $module + * The module name for one of the hook implementations. + * @param string $other_module + * The module name for another hook implementation. + * @param callable $listener + * The hook listener. + * @param string $identifier + * String identifier of the hook listener. + */ + protected function triggerErrorForDuplicateAlterHookListener(array $hooks, string $module, string $other_module, callable $listener, string $identifier): void { + $log_message_replacements = [ + '@implementation' => is_array($listener) + ? ('method ' . $identifier . '()') + : ('function ' . $listener[1] . '()'), + '@hooks' => "['" . implode("', '", $hooks) . "']", + ]; + if ($other_module !== $module) { + // There is conflicting information about on behalf of which module + // this implementation is registered. At this point we cannot even + // be sure if the module is the one from the main hook or the extra + // hook. + // The module is mostly irrelevant for alter hooks, except for its + // impact on ordering. + trigger_error((string) new FormattableMarkup( + 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call, on behalf of different modules @module and @other_module. Only one instance will be part of the implementation list for this hook combination. For the purpose of ordering, the module @module will be used.', + [ + ...$log_message_replacements, + '@module' => "'$module'", + '@other_module' => "'$other_module'", + ], + ), E_USER_WARNING); + } + else { + // There is no conflict, but probably one or more redundant #[Hook] + // attributes should be removed. + trigger_error((string) new FormattableMarkup( + 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call. Only one instance will be part of the implementation list for this hook combination.', + $log_message_replacements, + ), E_USER_NOTICE); + } + } + /** * Gets ordering rules for a hook. * -- GitLab From 67ea1ada16e5bf7c1014ad54d74c3887263c44e5 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 15:54:18 +0100 Subject: [PATCH 232/268] Drop unnecessary array_values(array_unique(.)) call in getCombinedListeners(). --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index a13868b1eb65..78b451d99181 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -558,7 +558,6 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook assert(!array_diff(array_keys($modules_by_identifier), $identifiers)); } } - $identifiers = array_values(array_unique($identifiers)); return array_map( static fn (string $identifier) => $listeners_by_identifier[$identifier], $identifiers, -- GitLab From 390174857e4fd3b63cb1108d150c105a088755cd Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 16:11:56 +0100 Subject: [PATCH 233/268] Simplify comment in getCombinedListeners(). --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 78b451d99181..209adb0e39c8 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -498,7 +498,7 @@ protected function getCombinedListeners(string $main_hook, string ...$extra_hook // The listeners for the main hook are already correctly ordered. return $main_hook_listeners; } - // Combine the listeners from all hooks that are part of the ->alter() call. + // Collect the listeners from each hook. $listeners_by_hook = [ $main_hook => $main_hook_listeners, ...$extra_listeners_by_hook, -- GitLab From 6c422129a1db0b2cbb66dab9c00d36b149fc5bae Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 29 Mar 2025 16:15:41 +0100 Subject: [PATCH 234/268] Simplify the signature of and collection logic in getCombinedListeners(). --- .../Drupal/Core/Extension/ModuleHandler.php | 84 ++++++++----------- 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 209adb0e39c8..e59350adbbc0 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -464,7 +464,7 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $hooks = is_array($type) ? array_map(static fn (string $type) => $type . '_alter', $type) : [$type . '_alter']; - $this->alterEventListeners[$cid] = $this->getCombinedListeners(...$hooks); + $this->alterEventListeners[$cid] = $this->getCombinedListeners($hooks); } foreach ($this->alterEventListeners[$cid] as $listener) { $listener($data, $context1, $context2); @@ -474,78 +474,64 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { /** * Builds a list of listeners for an alter hook. * - * @param string $main_hook - * The primary alter hook, e.g. 'form_alter'. - * @param string ...$extra_hooks - * Additional alter hooks, e.g. 'form_FORM_ID_alter'. + * @param list<string> $hooks + * The hooks passed to the ->alter() call. * * @return list<callable> * List of implementation callables. */ - protected function getCombinedListeners(string $main_hook, string ...$extra_hooks): array { - $main_hook_listeners = $this->getFlatHookListeners($main_hook); - if (!$extra_hooks) { - // No additional hooks were provided in the call. - return $main_hook_listeners; - } - $extra_listeners_by_hook = []; - foreach ($extra_hooks as $extra_hook) { - $extra_listeners_by_hook[$extra_hook] = $this->getFlatHookListeners($extra_hook); + protected function getCombinedListeners(array $hooks): array { + // Get implementation lists for each hook. + $listener_lists = array_map($this->getFlatHookListeners(...), $hooks); + // Remove empty lists. + $listener_lists = array_filter($listener_lists); + if (!$listener_lists) { + // No implementations exist. + return []; } - $extra_listeners_by_hook = array_filter($extra_listeners_by_hook); - if (!$extra_listeners_by_hook) { - // None of the extra hooks has any listeners. - // The listeners for the main hook are already correctly ordered. - return $main_hook_listeners; + if (array_keys($listener_lists) === [0]) { + // Only the main hook has implementations. + return $listener_lists[0]; } - // Collect the listeners from each hook. - $listeners_by_hook = [ - $main_hook => $main_hook_listeners, - ...$extra_listeners_by_hook, - ]; + // Collect the lists from each hook. // Group the listeners by module. - $listeners_by_module = []; - foreach ($listeners_by_hook as $hook => $listeners) { - foreach ($listeners as $index => $listener) { - $module = $this->modulesByHook[$hook][$index]; - $listeners_by_module[$module][] = $listener; - } - } - // Order the modules by module list order and using - // hook_module_implements_alter(). - $modules = array_keys($listeners_by_module); - $modules = $this->reOrderModulesForAlter($modules, $main_hook); - // Convert the list into a different structure to pass to the hook order - // operations. $listeners_by_identifier = []; $modules_by_identifier = []; - $identifiers = []; - foreach ($modules as $module) { - foreach ($listeners_by_module[$module] ?? [] as $listener) { + $identifiers_by_module = []; + foreach ($listener_lists as $i_hook => $listeners) { + $hook = $hooks[$i_hook]; + foreach ($listeners as $i_listener => $listener) { + $module = $this->modulesByHook[$hook][$i_listener]; $identifier = is_array($listener) ? get_class($listener[0]) . '::' . $listener[1] : ProceduralCall::class . '::' . $listener; - // Detect if the implementation is already part of the list. - // In general, a method can implement more than one hook. However, if - // both of these hooks are part of the same ->alter() call, that is - // almost always by mistake. - if ($other_module = $modules_by_identifier[$identifier] ?? NULL) { + $other_module = $modules_by_identifier[$identifier] ?? NULL; + if ($other_module !== NULL) { $this->triggerErrorForDuplicateAlterHookListener( - [$main_hook, ...$extra_hooks], + $hooks, $module, $other_module, $listener, $identifier, ); - // Don't add an identifier more than once. + // Don't add the same listener more than once. continue; } $listeners_by_identifier[$identifier] = $listener; $modules_by_identifier[$identifier] = $module; - $identifiers[] = $identifier; + $identifiers_by_module[$module][] = $identifier; } } - foreach ([$main_hook, ...$extra_hooks] as $hook) { + // Order the modules by module list order and using + // hook_module_implements_alter(). + $modules = array_keys($identifiers_by_module); + $modules = $this->reOrderModulesForAlter($modules, $hooks[0]); + // Create a flat list of identifiers, using the new module order. + $identifiers = array_merge(...array_map( + fn (string $module) => $identifiers_by_module[$module], + $modules, + )); + foreach ($hooks as $hook) { foreach ($this->getHookOrderingRules($hook) as $rule) { $rule->apply($identifiers, $modules_by_identifier); // Order operations must not: -- GitLab From 4638e9f60d89217ebbb22baa58aec88dc1d77daf Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 3 Apr 2025 12:54:50 -0400 Subject: [PATCH 235/268] Address comment suggestions --- .../Drupal/Core/Extension/ModuleHandler.php | 21 ++++++++++--------- core/lib/Drupal/Core/Extension/module.api.php | 11 ++++------ core/lib/Drupal/Core/Hook/Attribute/Hook.php | 7 +++---- .../Attribute/LegacyModuleImplementsAlter.php | 8 +++---- .../Drupal/Core/Hook/HookCollectorPass.php | 16 +++++++------- core/lib/Drupal/Core/Hook/Order/Order.php | 4 ++-- .../Drupal/Core/Hook/Order/OrderInterface.php | 15 +++++++++++++ .../Core/Hook/Order/RelativeOrderBase.php | 4 ++-- .../Hook/OrderOperation/OrderOperation.php | 4 ++-- 9 files changed, 50 insertions(+), 40 deletions(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index e59350adbbc0..59d914cf3e27 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -490,11 +490,10 @@ protected function getCombinedListeners(array $hooks): array { return []; } if (array_keys($listener_lists) === [0]) { - // Only the main hook has implementations. + // Only the first hook has implementations. return $listener_lists[0]; } - // Collect the lists from each hook. - // Group the listeners by module. + // Collect the lists from each hook and group the listeners by module. $listeners_by_identifier = []; $modules_by_identifier = []; $identifiers_by_module = []; @@ -522,8 +521,9 @@ protected function getCombinedListeners(array $hooks): array { $identifiers_by_module[$module][] = $identifier; } } - // Order the modules by module list order and using - // hook_module_implements_alter(). + // First we get the the modules in moduleList order, this order is module + // weight then alphabetical. Then we apply legacy ordering using + // hook_module_implements_alter(). Finally we order using order attributes. $modules = array_keys($identifiers_by_module); $modules = $this->reOrderModulesForAlter($modules, $hooks[0]); // Create a flat list of identifiers, using the new module order. @@ -575,12 +575,13 @@ protected function triggerErrorForDuplicateAlterHookListener(array $hooks, strin '@hooks' => "['" . implode("', '", $hooks) . "']", ]; if ($other_module !== $module) { - // There is conflicting information about on behalf of which module - // this implementation is registered. At this point we cannot even + // There is conflicting information about which module this + // implementation is registered for. At this point we cannot even // be sure if the module is the one from the main hook or the extra - // hook. - // The module is mostly irrelevant for alter hooks, except for its - // impact on ordering. + // hook. This means that ordering may not work as expected and it is + // unclear if the intention is to execute the code multiple times. This + // can be resolved by using a separate method for alter hooks that + // implement on behalf of other modules. trigger_error((string) new FormattableMarkup( 'The @implementation is registered for more than one of the alter hooks @hooks from the current ->alter() call, on behalf of different modules @module and @other_module. Only one instance will be part of the implementation list for this hook combination. For the purpose of ordering, the module @module will be used.', [ diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index de01dd91844a..9053ec15d169 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -94,13 +94,10 @@ function hook_hook_info(): array { /** * Alter the registry of modules implementing a hook. * - * This hook will be removed in 12.0.0. - * It has been intentionally not deprecated because custom code and contributed - * modules will still need to maintain implementations with the - * #[LegacyModuleImplementsAlter] attribute in order to support drupal versions - * older than 11.2.0. - * - * @link https://www.drupal.org/node/3496788 + * This hook will be removed in 12.0.0. It is not deprecated in order to + * support the "#[LegacyModuleImplementsAlter]" attribute, used prior to Drupal + * 11.2.0. + * See https://www.drupal.org/node/3496788. * * * Only procedural implementations are supported for this hook. diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index a9e845c1f363..0ad60877e7a2 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -12,7 +12,6 @@ * Hook implementations in classes need to be marked with this attribute, * using one of the following techniques: * - On a method, use this attribute with the hook name: - * * @code * #[Hook('user_cancel')] * public function userCancel(...) {} @@ -34,13 +33,13 @@ * @endcode * * Ordering hook implementations can be done by using the order parameter. - * - * @see https://www.drupal.org/node/3493962 + * See Drupal\Core\Hook\Order\OrderInterface for more information. * * Removing hook implementations can be done by using the attribute * \Drupal\Core\Hook\Attribute\RemoveHook. * - * @see https://www.drupal.org/node/3496786 + * Ordering hook implementations in other modules can be done by using the + * attribute \Drupal\Core\Hook\Attribute\ReOrderHook. * * Classes that use this annotation on the class or on their methods are * automatically registered as autowired services with the class name as the diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php index ee7558b43bde..5028d1b48e9d 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php @@ -7,9 +7,9 @@ /** * Prevents procedural hook_module_implements_alter from executing. * - * This allows contrib and core to maintain legacy hook_module_implements_alter - * alongside the new attribute-based ordering. This means that a contrib module - * can simultaneously support Drupal 11.2 and older versions of Drupal. + * This allows the use of the legacy hook_module_implements_alter alongside the + * new attribute-based ordering.Providing support for versions of Drupal older + * than 11.2.0. * * Marking hook_module_implements_alter as #LegacyModuleImplementsAlter will * prevent hook_module_implements_alter from running when attribute-based @@ -18,7 +18,7 @@ * On older versions of Drupal which are not aware of attribute-based ordering, * only the legacy hook implementation is executed. * - * For more information, see https://www.drupal.org/node/3496788. + * @see https://www.drupal.org/node/3496788. * * @internal */ diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index 66c3db25f27f..a05f52468afc 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -139,9 +139,9 @@ public function writeToContainer(ContainerBuilder $container): void { $container->register(ProceduralCall::class, ProceduralCall::class) ->addArgument($this->includes); - // Gather includes for each hook_hook_info group. - // We store this in $groupIncludes so moduleHandler can ensure the files - // are included runtime when the hooks are invoked. + // Gather includes for each hook_hook_info group. Store this in + // $groupIncludes so the module handler includes the files at runtime when + // the hooks are invoked. $groupIncludes = []; foreach ($this->hookInfo as $function) { foreach ($function() as $hook => $info) { @@ -341,8 +341,8 @@ protected static function writeImplementationsToContainer( * @internal * This method is only used by ModuleHandler. * - * @todo Pass only $container when ModuleHandler::add() is removed - * @see https://www.drupal.org/project/drupal/issues/3481778 + * @todo Pass only $container when ModuleHandler::add() is removed in Drupal + * 12.0.0. */ public static function collectAllHookImplementations(array $module_list, array $skipProceduralModules = []): static { $modules = array_keys($module_list); @@ -511,8 +511,7 @@ protected function addProceduralImplementation(\SplFileInfo $fileinfo, string $h /** * This method is only to be used by ModuleHandler. * - * @todo remove when ModuleHandler::add() is removed. - * @see https://www.drupal.org/project/drupal/issues/3481778 + * @todo Remove when ModuleHandler::add() is removed in Drupal 12.0.0. * * @internal */ @@ -528,8 +527,7 @@ public function loadAllIncludes(): void { * @return array<string, array<string, array<class-string, array<string, string>>>> * Hook implementation method names keyed by hook, module, class and method. * - * @todo remove when ModuleHandler::add() is removed. - * See https://www.drupal.org/project/drupal/issues/3481778 + * @todo Remove when ModuleHandler::add() is removed in Drupal 12.0.0. * * @internal */ diff --git a/core/lib/Drupal/Core/Hook/Order/Order.php b/core/lib/Drupal/Core/Hook/Order/Order.php index 76640fa4b78e..6a7934df7d2a 100644 --- a/core/lib/Drupal/Core/Hook/Order/Order.php +++ b/core/lib/Drupal/Core/Hook/Order/Order.php @@ -12,10 +12,10 @@ */ enum Order: int implements OrderInterface { - // This implementation should fire first. + // This implementation should execute first. case First = 1; - // This implementation should fire last. + // This implementation should execute last. case Last = 0; /** diff --git a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php index 09c52e242edd..70f597ac1fbf 100644 --- a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php @@ -8,6 +8,21 @@ /** * Interface for order specifiers used in hook attributes. + * + * Objects implementing this interface allow for relative ordering of hooks. + * These objects are passed as an order parameter to a Hook or ReOrderHook + * attribute. + * Order::First and Order::Last are simple order operations that move the hook + * implementation to the first or last position of hooks at the time the order + * directive is executed. + * @code + * #[Hook('custom_hook', order: Order::First)] + * @endcode + * OrderBefore and OrderAfter take additional parameters + * for ordering. See Drupal\Core\Hook\Order\RelativeOrderBase. + * @code + * #[Hook('custom_hook', order: new OrderBefore(['other_module']))] + * @endcode */ interface OrderInterface { diff --git a/core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php b/core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php index ebc4182c8f0e..eaf6eade668d 100644 --- a/core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php +++ b/core/lib/Drupal/Core/Hook/Order/RelativeOrderBase.php @@ -16,7 +16,7 @@ * Constructor. * * @param list<string> $modules - * A list of modules the implementations of which to order against. + * A list of modules the implementations should order against. * @param list<array{class-string, string}> $classesAndMethods * A list of implementations to order against, as [$class, $method]. */ @@ -33,7 +33,7 @@ public function __construct( * Specifies the ordering direction. * * @return bool - * TRUE, if the ordered implementation should be inserted _after_ the + * TRUE, if the ordered implementation should be inserted after the * implementations specified in the constructor. */ abstract protected function isAfter(): bool; diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php index e5074faa58ab..617b05c7e72b 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/OrderOperation.php @@ -10,7 +10,7 @@ abstract class OrderOperation { /** - * Packs an order operation object. + * Converts the operation to a structure that can be stored in the container. * * @return array * Packed operation. @@ -24,7 +24,7 @@ final public function pack(): array { } /** - * Unpacks an order operation object. + * Converts the stored operation to objects that can apply ordering rules. * * @param array $packed_operation * Packed operation. -- GitLab From fe52f263a83429c0ff566ee420541016b3b58b12 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 3 Apr 2025 13:01:36 -0400 Subject: [PATCH 236/268] Rename hk_modules --- .../aaa_hook_test.info.yml} | 0 .../aaa_hook_test.module} | 12 +-- .../src/Hook/AAlterHooks.php | 4 +- .../src/Hook/AHooks.php | 4 +- .../src/Hook/ModuleImplementsAlter.php | 2 +- .../bbb_hook_test.info.yml} | 0 .../bbb_hook_test.module} | 6 +- .../src/Hook/BAlterHooks.php | 2 +- .../src/Hook/BHooks.php | 2 +- .../ccc_hook_test.info.yml} | 0 .../ccc_hook_test.module} | 8 +- .../src/Hook/CAlterHooks.php | 2 +- .../src/Hook/CHooks.php | 6 +- .../ddd_hook_test.info.yml} | 0 .../ddd_hook_test.module} | 2 +- .../src/Hook/DAlterHooks.php | 2 +- .../src/Hook/DHooks.php | 4 +- .../eee_hook_test.info.yml} | 0 .../src/Hook/Ordering.php | 4 +- .../Core/Hook/HookAlterOrderTest.php | 78 +++++++++---------- .../KernelTests/Core/Hook/HookOrderTest.php | 28 +++---- 21 files changed, 83 insertions(+), 83 deletions(-) rename core/modules/system/tests/modules/HookOrder/{hk_a_test/hk_a_test.info.yml => aaa_hook_test/aaa_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{hk_a_test/hk_a_test.module => aaa_hook_test/aaa_hook_test.module} (57%) rename core/modules/system/tests/modules/HookOrder/{hk_a_test => aaa_hook_test}/src/Hook/AAlterHooks.php (77%) rename core/modules/system/tests/modules/HookOrder/{hk_a_test => aaa_hook_test}/src/Hook/AHooks.php (84%) rename core/modules/system/tests/modules/HookOrder/{hk_a_test => aaa_hook_test}/src/Hook/ModuleImplementsAlter.php (96%) rename core/modules/system/tests/modules/HookOrder/{hk_b_test/hk_b_test.info.yml => bbb_hook_test/bbb_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{hk_b_test/hk_b_test.module => bbb_hook_test/bbb_hook_test.module} (64%) rename core/modules/system/tests/modules/HookOrder/{hk_b_test => bbb_hook_test}/src/Hook/BAlterHooks.php (87%) rename core/modules/system/tests/modules/HookOrder/{hk_b_test => bbb_hook_test}/src/Hook/BHooks.php (89%) rename core/modules/system/tests/modules/HookOrder/{hk_c_test/hk_c_test.info.yml => ccc_hook_test/ccc_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{hk_c_test/hk_c_test.module => ccc_hook_test/ccc_hook_test.module} (63%) rename core/modules/system/tests/modules/HookOrder/{hk_c_test => ccc_hook_test}/src/Hook/CAlterHooks.php (90%) rename core/modules/system/tests/modules/HookOrder/{hk_c_test => ccc_hook_test}/src/Hook/CHooks.php (84%) rename core/modules/system/tests/modules/HookOrder/{hk_d_test/hk_d_test.info.yml => ddd_hook_test/ddd_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{hk_d_test/hk_d_test.module => ddd_hook_test/ddd_hook_test.module} (78%) rename core/modules/system/tests/modules/HookOrder/{hk_d_test => ddd_hook_test}/src/Hook/DAlterHooks.php (90%) rename core/modules/system/tests/modules/HookOrder/{hk_d_test => ddd_hook_test}/src/Hook/DHooks.php (88%) rename core/modules/system/tests/modules/HookOrder/{hk_extra_test/hk_extra_test.info.yml => eee_hook_test/eee_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{hk_extra_test => eee_hook_test}/src/Hook/Ordering.php (56%) diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.info.yml b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.info.yml rename to core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.module similarity index 57% rename from core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module rename to core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.module index d9ffedc61674..97a3a98d649d 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/hk_a_test.module +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.module @@ -7,39 +7,39 @@ declare(strict_types=1); -use Drupal\hk_a_test\Hook\ModuleImplementsAlter; +use Drupal\aaa_hook_test\Hook\ModuleImplementsAlter; /** * Implements hook_test_hook(). */ -function hk_a_test_test_hook(): string { +function aaa_hook_test_test_hook(): string { return __FUNCTION__; } /** * Implements hook_sparse_test_hook(). */ -function hk_a_test_sparse_test_hook(): string { +function aaa_hook_test_sparse_test_hook(): string { return __FUNCTION__; } /** * Implements hook_procedural_alter(). */ -function hk_a_test_procedural_alter(array &$calls): void { +function aaa_hook_test_procedural_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_procedural_subtype_alter(). */ -function hk_a_test_procedural_subtype_alter(array &$calls): void { +function aaa_hook_test_procedural_subtype_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_module_implements_alter(). */ -function hk_a_test_module_implements_alter(array &$implementations, string $hook): void { +function aaa_hook_test_module_implements_alter(array &$implementations, string $hook): void { ModuleImplementsAlter::call($implementations, $hook); } diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AAlterHooks.php similarity index 77% rename from core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AAlterHooks.php index b51af9543118..ab054e269312 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_a_test\Hook; +namespace Drupal\aaa_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; @@ -12,7 +12,7 @@ */ class AAlterHooks { - #[Hook('test_alter', order: new OrderAfter(modules: ['hk_c_test']))] + #[Hook('test_alter', order: new OrderAfter(modules: ['ccc_hook_test']))] public function testAlterAfterC(array &$calls): void { $calls[] = __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AHooks.php similarity index 84% rename from core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php rename to core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AHooks.php index c9f2ce2fd9dc..56a14392a453 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/AHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_a_test\Hook; +namespace Drupal\aaa_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; @@ -28,7 +28,7 @@ public function testHookLast(): string { return __METHOD__; } - #[Hook('test_hook', order: new OrderAfter(modules: ['hk_b_test']))] + #[Hook('test_hook', order: new OrderAfter(modules: ['bbb_hook_test']))] public function testHookAfterB(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/ModuleImplementsAlter.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/ModuleImplementsAlter.php similarity index 96% rename from core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/ModuleImplementsAlter.php rename to core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/ModuleImplementsAlter.php index 082950e7aa5a..ba1c84d96424 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_a_test/src/Hook/ModuleImplementsAlter.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/ModuleImplementsAlter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_a_test\Hook; +namespace Drupal\aaa_hook_test\Hook; /** * Contains a replaceable callback for hook_module_implements_alter(). diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.info.yml b/core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.info.yml rename to core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module b/core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.module similarity index 64% rename from core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module rename to core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.module index bc0b1c03e247..51bc06948541 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/hk_b_test.module +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.module @@ -10,20 +10,20 @@ /** * Implements hook_test_hook(). */ -function hk_b_test_test_hook(): string { +function bbb_hook_test_test_hook(): string { return __FUNCTION__; } /** * Implements hook_procedural_alter(). */ -function hk_b_test_procedural_alter(array &$calls): void { +function bbb_hook_test_procedural_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_procedural_subtype_alter(). */ -function hk_b_test_procedural_subtype_alter(array &$calls): void { +function bbb_hook_test_procedural_subtype_alter(array &$calls): void { $calls[] = __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BAlterHooks.php similarity index 87% rename from core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BAlterHooks.php index b84361a08d94..648e75e30aa7 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_b_test\Hook; +namespace Drupal\bbb_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BHooks.php similarity index 89% rename from core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php rename to core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BHooks.php index dc0d451b2978..939f9d0d28fa 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_b_test/src/Hook/BHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_b_test\Hook; +namespace Drupal\bbb_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.info.yml b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.info.yml rename to core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.module similarity index 63% rename from core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module rename to core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.module index d012e670d008..8b36d92e2d2c 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/hk_c_test.module +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.module @@ -10,27 +10,27 @@ /** * Implements hook_test_hook(). */ -function hk_c_test_test_hook(): string { +function ccc_hook_test_test_hook(): string { return __FUNCTION__; } /** * Implements hook_sparse_test_hook(). */ -function hk_c_test_sparse_test_hook(): string { +function ccc_hook_test_sparse_test_hook(): string { return __FUNCTION__; } /** * Implements hook_procedural_alter(). */ -function hk_c_test_procedural_alter(array &$calls): void { +function ccc_hook_test_procedural_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_procedural_subtype_alter(). */ -function hk_c_test_procedural_subtype_alter(array &$calls): void { +function ccc_hook_test_procedural_subtype_alter(array &$calls): void { $calls[] = __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CAlterHooks.php similarity index 90% rename from core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CAlterHooks.php index 93eb6c5fb7ba..ab2f3f363915 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_c_test\Hook; +namespace Drupal\ccc_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php similarity index 84% rename from core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php rename to core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php index 0c03cfae5765..e22903bba60e 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_c_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_c_test\Hook; +namespace Drupal\ccc_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; @@ -25,7 +25,7 @@ public function testHookFirst(): string { /** * This implementation is reordered from elsewhere. * - * @see \Drupal\hk_d_test\Hook\DHooks + * @see \Drupal\ddd_hook_test\Hook\DHooks */ #[Hook('test_hook')] public function testHookReOrderFirst(): string { @@ -35,7 +35,7 @@ public function testHookReOrderFirst(): string { /** * This implementation is removed from elsewhere. * - * @see \Drupal\hk_d_test\Hook\DHooks + * @see \Drupal\ddd_hook_test\Hook\DHooks */ #[Hook('test_hook')] public function testHookRemoved(): string { diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.info.yml b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.info.yml rename to core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.module similarity index 78% rename from core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module rename to core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.module index ab79c190ffa5..0ed724ab50d1 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/hk_d_test.module +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.module @@ -10,6 +10,6 @@ /** * Implements hook_test_hook(). */ -function hk_d_test_test_hook(): string { +function ddd_hook_test_test_hook(): string { return __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DAlterHooks.php similarity index 90% rename from core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DAlterHooks.php index d47443a55968..5586ddea5c8c 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_d_test\Hook; +namespace Drupal\ddd_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php similarity index 88% rename from core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php rename to core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php index a6d6940b77d0..06606878106f 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_d_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Drupal\hk_d_test\Hook; +namespace Drupal\ddd_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Attribute\ReOrderHook; use Drupal\Core\Hook\Order\Order; -use Drupal\hk_c_test\Hook\CHooks; +use Drupal\ccc_hook_test\Hook\CHooks; /** * Hooks for testing ordering. diff --git a/core/modules/system/tests/modules/HookOrder/hk_extra_test/hk_extra_test.info.yml b/core/modules/system/tests/modules/HookOrder/eee_hook_test/eee_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/hk_extra_test/hk_extra_test.info.yml rename to core/modules/system/tests/modules/HookOrder/eee_hook_test/eee_hook_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php b/core/modules/system/tests/modules/HookOrder/eee_hook_test/src/Hook/Ordering.php similarity index 56% rename from core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php rename to core/modules/system/tests/modules/HookOrder/eee_hook_test/src/Hook/Ordering.php index 230d31a8383d..748918945c81 100644 --- a/core/modules/system/tests/modules/HookOrder/hk_extra_test/src/Hook/Ordering.php +++ b/core/modules/system/tests/modules/HookOrder/eee_hook_test/src/Hook/Ordering.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hk_extra_test\Hook; +namespace Drupal\eee_hook_test\Hook; use Drupal\Core\Extension\ProceduralCall; use Drupal\Core\Hook\Attribute\ReOrderHook; @@ -11,7 +11,7 @@ /** * Hooks for testing ordering. */ -#[ReOrderHook('procedural_alter', ProceduralCall::class, 'hk_a_test_procedural_alter', new OrderBefore(['hk_b_test'], []))] +#[ReOrderHook('procedural_alter', ProceduralCall::class, 'aaa_hook_test_procedural_alter', new OrderBefore(['bbb_hook_test'], []))] class Ordering { } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php index 188611f492c1..583e88317e1f 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php @@ -4,11 +4,11 @@ namespace Drupal\KernelTests\Core\Hook; -use Drupal\hk_a_test\Hook\AAlterHooks; -use Drupal\hk_a_test\Hook\ModuleImplementsAlter; -use Drupal\hk_b_test\Hook\BAlterHooks; -use Drupal\hk_c_test\Hook\CAlterHooks; -use Drupal\hk_d_test\Hook\DAlterHooks; +use Drupal\aaa_hook_test\Hook\AAlterHooks; +use Drupal\aaa_hook_test\Hook\ModuleImplementsAlter; +use Drupal\bbb_hook_test\Hook\BAlterHooks; +use Drupal\ccc_hook_test\Hook\CAlterHooks; +use Drupal\ddd_hook_test\Hook\DAlterHooks; use Drupal\KernelTests\KernelTestBase; use PHPUnit\Framework\Attributes\IgnoreDeprecations; @@ -24,41 +24,41 @@ class HookAlterOrderTest extends KernelTestBase { * {@inheritdoc} */ protected static $modules = [ - 'hk_a_test', - 'hk_b_test', - 'hk_c_test', - 'hk_d_test', + 'aaa_hook_test', + 'bbb_hook_test', + 'ccc_hook_test', + 'ddd_hook_test', ]; public function testProceduralModuleImplementsAlterOrder(): void { $this->assertAlterCallOrder($main_unaltered = [ - 'hk_a_test_procedural_alter', - 'hk_b_test_procedural_alter', - 'hk_c_test_procedural_alter', + 'aaa_hook_test_procedural_alter', + 'bbb_hook_test_procedural_alter', + 'ccc_hook_test_procedural_alter', ], 'procedural'); $this->assertAlterCallOrder($sub_unaltered = [ - 'hk_a_test_procedural_subtype_alter', - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_subtype_alter', + 'aaa_hook_test_procedural_subtype_alter', + 'bbb_hook_test_procedural_subtype_alter', + 'ccc_hook_test_procedural_subtype_alter', ], 'procedural_subtype'); $this->assertAlterCallOrder($combined_unaltered = [ - 'hk_a_test_procedural_alter', - 'hk_a_test_procedural_subtype_alter', - 'hk_b_test_procedural_alter', - 'hk_b_test_procedural_subtype_alter', - 'hk_c_test_procedural_alter', - 'hk_c_test_procedural_subtype_alter', + 'aaa_hook_test_procedural_alter', + 'aaa_hook_test_procedural_subtype_alter', + 'bbb_hook_test_procedural_alter', + 'bbb_hook_test_procedural_subtype_alter', + 'ccc_hook_test_procedural_alter', + 'ccc_hook_test_procedural_subtype_alter', ], ['procedural', 'procedural_subtype']); $move_b_down = function (array &$implementations): void { // Move B to the end, no matter which hook. - $group = $implementations['hk_b_test']; - unset($implementations['hk_b_test']); - $implementations['hk_b_test'] = $group; + $group = $implementations['bbb_hook_test']; + unset($implementations['bbb_hook_test']); + $implementations['bbb_hook_test'] = $group; }; - $modules = ['hk_a_test', 'hk_b_test', 'hk_c_test']; + $modules = ['aaa_hook_test', 'bbb_hook_test', 'ccc_hook_test']; // Test with module B moved to the end for both hooks. ModuleImplementsAlter::set( @@ -73,27 +73,27 @@ function (array &$implementations, string $hook) use ($modules, $move_b_down): v \Drupal::service('kernel')->rebuildContainer(); $this->assertAlterCallOrder($main_altered = [ - 'hk_a_test_procedural_alter', - 'hk_c_test_procedural_alter', + 'aaa_hook_test_procedural_alter', + 'ccc_hook_test_procedural_alter', // The implementation of B has been moved. - 'hk_b_test_procedural_alter', + 'bbb_hook_test_procedural_alter', ], 'procedural'); $this->assertAlterCallOrder($sub_altered = [ - 'hk_a_test_procedural_subtype_alter', - 'hk_c_test_procedural_subtype_alter', + 'aaa_hook_test_procedural_subtype_alter', + 'ccc_hook_test_procedural_subtype_alter', // The implementation of B has been moved. - 'hk_b_test_procedural_subtype_alter', + 'bbb_hook_test_procedural_subtype_alter', ], 'procedural_subtype'); $this->assertAlterCallOrder($combined_altered = [ - 'hk_a_test_procedural_alter', - 'hk_a_test_procedural_subtype_alter', - 'hk_c_test_procedural_alter', - 'hk_c_test_procedural_subtype_alter', + 'aaa_hook_test_procedural_alter', + 'aaa_hook_test_procedural_subtype_alter', + 'ccc_hook_test_procedural_alter', + 'ccc_hook_test_procedural_subtype_alter', // The implementation of B has been moved. - 'hk_b_test_procedural_alter', - 'hk_b_test_procedural_subtype_alter', + 'bbb_hook_test_procedural_alter', + 'bbb_hook_test_procedural_subtype_alter', ], ['procedural', 'procedural_subtype']); // If the altered hook is not the first one, implementations are back in @@ -142,7 +142,7 @@ function (array &$implementations, string $hook) use ($modules, $move_b_down): v } public function testProceduralOrderSideEffect(): void { - $this->enableModules(['hk_extra_test']); + $this->enableModules(['eee_hook_test']); // The previous test should behave exactly the same. $this->testProceduralModuleImplementsAlterOrder(); } @@ -172,7 +172,7 @@ public function testAlterOrder(): void { DAlterHooks::class . '::testSubtypeAlter', ], ['test', 'test_subtype']); - $this->disableModules(['hk_b_test']); + $this->disableModules(['bbb_hook_test']); $this->assertAlterCallOrder([ CAlterHooks::class . '::testAlter', diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index a1bf82a567ab..9d3cda146a07 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -4,10 +4,10 @@ namespace Drupal\KernelTests\Core\Hook; -use Drupal\hk_a_test\Hook\AHooks; -use Drupal\hk_b_test\Hook\BHooks; -use Drupal\hk_c_test\Hook\CHooks; -use Drupal\hk_d_test\Hook\DHooks; +use Drupal\aaa_hook_test\Hook\AHooks; +use Drupal\bbb_hook_test\Hook\BHooks; +use Drupal\ccc_hook_test\Hook\CHooks; +use Drupal\ddd_hook_test\Hook\DHooks; use Drupal\KernelTests\KernelTestBase; use PHPUnit\Framework\Attributes\IgnoreDeprecations; @@ -23,10 +23,10 @@ class HookOrderTest extends KernelTestBase { * {@inheritdoc} */ protected static $modules = [ - 'hk_a_test', - 'hk_b_test', - 'hk_c_test', - 'hk_d_test', + 'aaa_hook_test', + 'bbb_hook_test', + 'ccc_hook_test', + 'ddd_hook_test', ]; public function testHookOrder(): void { @@ -35,14 +35,14 @@ public function testHookOrder(): void { CHooks::class . '::testHookReOrderFirst', CHooks::class . '::testHookFirst', AHooks::class . '::testHookFirst', - 'hk_a_test_test_hook', + 'aaa_hook_test_test_hook', AHooks::class . '::testHook', - 'hk_b_test_test_hook', + 'bbb_hook_test_test_hook', BHooks::class . '::testHook', AHooks::class . '::testHookAfterB', - 'hk_c_test_test_hook', + 'ccc_hook_test_test_hook', CHooks::class . '::testHook', - 'hk_d_test_test_hook', + 'ddd_hook_test_test_hook', DHooks::class . '::testHook', AHooks::class . '::testHookLast', ], @@ -62,9 +62,9 @@ public function testSparseHookOrder(): void { [ // OOP and procedural listeners are correctly intermixed by module // order. - 'hk_a_test_sparse_test_hook', + 'aaa_hook_test_sparse_test_hook', BHooks::class . '::sparseTestHook', - 'hk_c_test_sparse_test_hook', + 'ccc_hook_test_sparse_test_hook', DHooks::class . '::sparseTestHook', ], \Drupal::moduleHandler()->invokeAll('sparse_test_hook'), -- GitLab From 4ab4eaed2174786b9330885878c94f96a69cc78d Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 3 Apr 2025 13:06:10 -0400 Subject: [PATCH 237/268] Rename order modules --- .../fff_hook_test/fff_hook_test.info.yml} | 0 .../fff_hook_test}/src/Hook/TestHookAfter.php | 4 +- .../src/Hook/TestHookBefore.php | 2 +- .../fff_hook_test}/src/Hook/TestHookFirst.php | 2 +- .../fff_hook_test}/src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderExtraTypes.php | 4 +- .../src/Hook/TestHookReOrderHookFirst.php | 4 +- .../ggg_hook_test/ggg_hook_test.info.yml} | 0 .../ggg_hook_test}/src/Hook/TestHookAfter.php | 2 +- .../src/Hook/TestHookBefore.php | 4 +- .../ggg_hook_test}/src/Hook/TestHookFirst.php | 2 +- .../ggg_hook_test}/src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderExtraTypes.php | 2 +- .../src/Hook/TestHookReOrderHookLast.php | 4 +- .../hhh_hook_test/hhh_hook_test.info.yml} | 0 .../src/Hook/TestHookAfterClassMethod.php | 4 +- .../iii_hook_test/iii_hook_test.info.yml} | 0 .../src/Hook/TestHookAfterClassMethod.php | 2 +- .../Core/Hook/HookCollectorPassTest.php | 56 +++++++++---------- 19 files changed, 48 insertions(+), 48 deletions(-) rename core/modules/system/tests/modules/{hook_order_first_alphabetically/hook_order_first_alphabetically.info.yml => HookOrder/fff_hook_test/fff_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/{hook_order_first_alphabetically => HookOrder/fff_hook_test}/src/Hook/TestHookAfter.php (89%) rename core/modules/system/tests/modules/{hook_order_first_alphabetically => HookOrder/fff_hook_test}/src/Hook/TestHookBefore.php (93%) rename core/modules/system/tests/modules/{hook_order_first_alphabetically => HookOrder/fff_hook_test}/src/Hook/TestHookFirst.php (93%) rename core/modules/system/tests/modules/{hook_order_first_alphabetically => HookOrder/fff_hook_test}/src/Hook/TestHookLast.php (93%) rename core/modules/system/tests/modules/{hook_order_first_alphabetically => HookOrder/fff_hook_test}/src/Hook/TestHookOrderExtraTypes.php (89%) rename core/modules/system/tests/modules/{hook_order_first_alphabetically => HookOrder/fff_hook_test}/src/Hook/TestHookReOrderHookFirst.php (90%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically/hook_order_last_alphabetically.info.yml => HookOrder/ggg_hook_test/ggg_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically => HookOrder/ggg_hook_test}/src/Hook/TestHookAfter.php (93%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically => HookOrder/ggg_hook_test}/src/Hook/TestHookBefore.php (89%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically => HookOrder/ggg_hook_test}/src/Hook/TestHookFirst.php (93%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically => HookOrder/ggg_hook_test}/src/Hook/TestHookLast.php (93%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically => HookOrder/ggg_hook_test}/src/Hook/TestHookOrderExtraTypes.php (93%) rename core/modules/system/tests/modules/{hook_order_last_alphabetically => HookOrder/ggg_hook_test}/src/Hook/TestHookReOrderHookLast.php (87%) rename core/modules/system/tests/modules/{hook_second_order_first_alphabetically/hook_second_order_first_alphabetically.info.yml => HookOrder/hhh_hook_test/hhh_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/{hook_second_order_first_alphabetically => HookOrder/hhh_hook_test}/src/Hook/TestHookAfterClassMethod.php (84%) rename core/modules/system/tests/modules/{hook_second_order_last_alphabetically/hook_second_order_last_alphabetically.info.yml => HookOrder/iii_hook_test/iii_hook_test.info.yml} (100%) rename core/modules/system/tests/modules/{hook_second_order_last_alphabetically => HookOrder/iii_hook_test}/src/Hook/TestHookAfterClassMethod.php (93%) diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/hook_order_first_alphabetically.info.yml b/core/modules/system/tests/modules/HookOrder/fff_hook_test/fff_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/hook_order_first_alphabetically.info.yml rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/fff_hook_test.info.yml diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookAfter.php similarity index 89% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookAfter.php index facdee6c591b..c91b1bfb4e31 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookAfter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically\Hook; +namespace Drupal\fff_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; @@ -24,7 +24,7 @@ class TestHookAfter { /** * This pair tests OrderAfter. */ - #[Hook('custom_hook_test_hook_after', order: new OrderAfter(['hook_order_last_alphabetically']))] + #[Hook('custom_hook_test_hook_after', order: new OrderAfter(['ggg_hook_test']))] public function hookAfter(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookBefore.php similarity index 93% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookBefore.php index c67ede46f49f..f2205df4c668 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookBefore.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically\Hook; +namespace Drupal\fff_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookFirst.php similarity index 93% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookFirst.php index 99501c5a5ff5..3b1b53bbb835 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookFirst.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically\Hook; +namespace Drupal\fff_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookLast.php similarity index 93% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookLast.php index 4600cdde5573..4b31f5fb553c 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookLast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically\Hook; +namespace Drupal\fff_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookOrderExtraTypes.php similarity index 89% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookOrderExtraTypes.php index 72a00327fb87..69431ceb0bf8 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookOrderExtraTypes.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically\Hook; +namespace Drupal\fff_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; @@ -26,7 +26,7 @@ class TestHookOrderExtraTypes { */ #[Hook('custom_hook_extra_types1_alter', order: new OrderAfter( - modules: ['hook_order_last_alphabetically'], + modules: ['ggg_hook_test'], ) )] public function customHookExtraTypes(array &$calls): void { diff --git a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php similarity index 90% rename from core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php index e7202b42580e..8983c6b7e76b 100644 --- a/core/modules/system/tests/modules/hook_order_first_alphabetically/src/Hook/TestHookReOrderHookFirst.php +++ b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Drupal\hook_order_first_alphabetically\Hook; +namespace Drupal\fff_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookLast; +use Drupal\ggg_hook_test\Hook\TestHookReOrderHookLast; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/hook_order_last_alphabetically.info.yml b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/ggg_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/hook_order_last_alphabetically.info.yml rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/ggg_hook_test.info.yml diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookAfter.php similarity index 93% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookAfter.php index c4e9a9c35aaf..d78ed1b88911 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookAfter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically\Hook; +namespace Drupal\ggg_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookBefore.php similarity index 89% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookBefore.php index fa602398d4d7..22fec07649ea 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookBefore.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically\Hook; +namespace Drupal\ggg_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderBefore; @@ -24,7 +24,7 @@ class TestHookBefore { /** * This pair tests OrderBefore. */ - #[Hook('custom_hook_test_hook_before', order: new OrderBefore(['hook_order_first_alphabetically']))] + #[Hook('custom_hook_test_hook_before', order: new OrderBefore(['fff_hook_test']))] public function hookBefore(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookFirst.php similarity index 93% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookFirst.php index cd7b9dffef64..078a643df4ec 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookFirst.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically\Hook; +namespace Drupal\ggg_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookLast.php similarity index 93% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookLast.php index 1868385b13e5..a40bfa4ab42a 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookLast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically\Hook; +namespace Drupal\ggg_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookOrderExtraTypes.php similarity index 93% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookOrderExtraTypes.php index 09793fb16497..299549153501 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookOrderExtraTypes.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically\Hook; +namespace Drupal\ggg_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php similarity index 87% rename from core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php index 6c82bd1562c0..0dceeaff4f33 100644 --- a/core/modules/system/tests/modules/hook_order_last_alphabetically/src/Hook/TestHookReOrderHookLast.php +++ b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_order_last_alphabetically\Hook; +namespace Drupal\ggg_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; @@ -28,7 +28,7 @@ class TestHookReOrderHookLast { public function customHookOverride(): string { // This normally would run second. // We override that order here with Order::First. - // We override, that order in hook_order_first_alphabetically with + // We override, that order in fff_hook_test with // ReOrderHook. return __METHOD__; } diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/hook_second_order_first_alphabetically.info.yml b/core/modules/system/tests/modules/HookOrder/hhh_hook_test/hhh_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/hook_second_order_first_alphabetically/hook_second_order_first_alphabetically.info.yml rename to core/modules/system/tests/modules/HookOrder/hhh_hook_test/hhh_hook_test.info.yml diff --git a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookOrder/hhh_hook_test/src/Hook/TestHookAfterClassMethod.php similarity index 84% rename from core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php rename to core/modules/system/tests/modules/HookOrder/hhh_hook_test/src/Hook/TestHookAfterClassMethod.php index 634788e22066..b081b9d6a610 100644 --- a/core/modules/system/tests/modules/hook_second_order_first_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookOrder/hhh_hook_test/src/Hook/TestHookAfterClassMethod.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Drupal\hook_second_order_first_alphabetically\Hook; +namespace Drupal\hhh_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; -use Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; +use Drupal\iii_hook_test\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/hook_second_order_last_alphabetically.info.yml b/core/modules/system/tests/modules/HookOrder/iii_hook_test/iii_hook_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/hook_second_order_last_alphabetically/hook_second_order_last_alphabetically.info.yml rename to core/modules/system/tests/modules/HookOrder/iii_hook_test/iii_hook_test.info.yml diff --git a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookOrder/iii_hook_test/src/Hook/TestHookAfterClassMethod.php similarity index 93% rename from core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php rename to core/modules/system/tests/modules/HookOrder/iii_hook_test/src/Hook/TestHookAfterClassMethod.php index bb8d53f8f1a0..02b9cb15f61a 100644 --- a/core/modules/system/tests/modules/hook_second_order_last_alphabetically/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookOrder/iii_hook_test/src/Hook/TestHookAfterClassMethod.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\hook_second_order_last_alphabetically\Hook; +namespace Drupal\iii_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index cb825aa5133d..7ce711bd5e17 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -160,14 +160,14 @@ public function testHookAttribute(): void { */ public function testHookFirst(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hook_order_first_alphabetically']); - $module_installer->install(['hook_order_last_alphabetically']); + $module_installer->install(['fff_hook_test']); + $module_installer->install(['ggg_hook_test']); $module_handler = $this->container->get('module_handler'); // Last alphabetically uses the Order::First enum to place it before // the implementation it would naturally come after. $expected_calls = [ - 'Drupal\hook_order_last_alphabetically\Hook\TestHookFirst::hookFirst', - 'Drupal\hook_order_first_alphabetically\Hook\TestHookFirst::hookFirst', + 'Drupal\ggg_hook_test\Hook\TestHookFirst::hookFirst', + 'Drupal\fff_hook_test\Hook\TestHookFirst::hookFirst', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_first'); $this->assertEquals($expected_calls, $calls); @@ -178,14 +178,14 @@ public function testHookFirst(): void { */ public function testHookAfter(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hook_order_first_alphabetically']); - $module_installer->install(['hook_order_last_alphabetically']); + $module_installer->install(['fff_hook_test']); + $module_installer->install(['ggg_hook_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderAfter to place it after // the implementation it would naturally come before. $expected_calls = [ - 'Drupal\hook_order_last_alphabetically\Hook\TestHookAfter::hookAfter', - 'Drupal\hook_order_first_alphabetically\Hook\TestHookAfter::hookAfter', + 'Drupal\ggg_hook_test\Hook\TestHookAfter::hookAfter', + 'Drupal\fff_hook_test\Hook\TestHookAfter::hookAfter', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_after'); $this->assertEquals($expected_calls, $calls); @@ -196,14 +196,14 @@ public function testHookAfter(): void { */ public function testHookAfterClassMethod(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hook_second_order_first_alphabetically']); - $module_installer->install(['hook_second_order_last_alphabetically']); + $module_installer->install(['hhh_hook_test']); + $module_installer->install(['iii_hook_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderAfter to place it after // the implementation it would naturally come before using call and method. $expected_calls = [ - 'Drupal\hook_second_order_last_alphabetically\Hook\TestHookAfterClassMethod::hookAfterClassMethod', - 'Drupal\hook_second_order_first_alphabetically\Hook\TestHookAfterClassMethod::hookAfterClassMethod', + 'Drupal\iii_hook_test\Hook\TestHookAfterClassMethod::hookAfterClassMethod', + 'Drupal\hhh_hook_test\Hook\TestHookAfterClassMethod::hookAfterClassMethod', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_after_class_method'); $this->assertEquals($expected_calls, $calls); @@ -214,14 +214,14 @@ public function testHookAfterClassMethod(): void { */ public function testHookBefore(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hook_order_first_alphabetically']); - $module_installer->install(['hook_order_last_alphabetically']); + $module_installer->install(['fff_hook_test']); + $module_installer->install(['ggg_hook_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderBefore to place it before // the implementation it would naturally come after. $expected_calls = [ - 'Drupal\hook_order_last_alphabetically\Hook\TestHookBefore::hookBefore', - 'Drupal\hook_order_first_alphabetically\Hook\TestHookBefore::hookBefore', + 'Drupal\ggg_hook_test\Hook\TestHookBefore::hookBefore', + 'Drupal\fff_hook_test\Hook\TestHookBefore::hookBefore', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_before'); $this->assertEquals($expected_calls, $calls); @@ -232,14 +232,14 @@ public function testHookBefore(): void { */ public function testHookOrderExtraTypes(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hook_order_first_alphabetically']); - $module_installer->install(['hook_order_last_alphabetically']); + $module_installer->install(['fff_hook_test']); + $module_installer->install(['ggg_hook_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderAfter to place it after // the implementation it would naturally come before. $expected_calls = [ - 'Drupal\hook_order_last_alphabetically\Hook\TestHookOrderExtraTypes::customHookExtraTypes', - 'Drupal\hook_order_first_alphabetically\Hook\TestHookOrderExtraTypes::customHookExtraTypes', + 'Drupal\ggg_hook_test\Hook\TestHookOrderExtraTypes::customHookExtraTypes', + 'Drupal\fff_hook_test\Hook\TestHookOrderExtraTypes::customHookExtraTypes', ]; $hooks = [ 'custom_hook', @@ -256,14 +256,14 @@ public function testHookOrderExtraTypes(): void { */ public function testHookLast(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hook_order_first_alphabetically']); - $module_installer->install(['hook_order_last_alphabetically']); + $module_installer->install(['fff_hook_test']); + $module_installer->install(['ggg_hook_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderBefore to place it before // the implementation it would naturally come after. $expected_calls = [ - 'Drupal\hook_order_last_alphabetically\Hook\TestHookLast::hookLast', - 'Drupal\hook_order_first_alphabetically\Hook\TestHookLast::hookLast', + 'Drupal\ggg_hook_test\Hook\TestHookLast::hookLast', + 'Drupal\fff_hook_test\Hook\TestHookLast::hookLast', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_last'); $this->assertEquals($expected_calls, $calls); @@ -290,12 +290,12 @@ public function testHookRemove(): void { */ public function testHookOverride(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hook_order_first_alphabetically']); - $module_installer->install(['hook_order_last_alphabetically']); + $module_installer->install(['fff_hook_test']); + $module_installer->install(['ggg_hook_test']); $module_handler = $this->container->get('module_handler'); $expected_calls = [ - 'Drupal\hook_order_first_alphabetically\Hook\TestHookReOrderHookFirst::customHookOverride', - 'Drupal\hook_order_last_alphabetically\Hook\TestHookReOrderHookLast::customHookOverride', + 'Drupal\fff_hook_test\Hook\TestHookReOrderHookFirst::customHookOverride', + 'Drupal\ggg_hook_test\Hook\TestHookReOrderHookLast::customHookOverride', ]; $calls = $module_handler->invokeAll('custom_hook_override'); $this->assertEquals($expected_calls, $calls); -- GitLab From e24902dda26dce444bd03d228874c64eba0a3129 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 3 Apr 2025 13:08:04 -0400 Subject: [PATCH 238/268] Remove todo we cannot do --- core/lib/Drupal/Core/Extension/ModuleHandler.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 59d914cf3e27..53cf3c95aa5f 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -246,7 +246,6 @@ protected function add($type, $name, $path) { foreach ($hook_collector->getImplementations() as $hook => $moduleImplements) { foreach ($moduleImplements as $module => $classImplements) { foreach ($classImplements[ProceduralCall::class] ?? [] as $method) { - // @todo Reorder these after adding! $this->listenersByHook[$hook][] = $method; $this->modulesByHook[$hook][] = $module; $this->invokeMap[$hook][$module][] = $method; -- GitLab From 78e9457dba372566df85005043e819e264ff01d0 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Tue, 8 Apr 2025 13:56:18 -0400 Subject: [PATCH 239/268] Add documentation to core.api.php --- core/core.api.php | 91 ++++++++++++++++++- core/lib/Drupal/Core/Extension/module.api.php | 4 +- .../Attribute/LegacyModuleImplementsAlter.php | 2 - 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/core/core.api.php b/core/core.api.php index 0dbec836754e..c66306a4d7b1 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1657,7 +1657,7 @@ * Legacy meta hooks: * - hook_hook_info() * - hook_module_implements_alter() - * @see https://www.drupal.org/node/3496788 + * See \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter * * Install hooks: * - hook_install() @@ -2767,3 +2767,92 @@ function hook_validation_constraint_alter(array &$definitions) { * longer needed (see @ref sec_intro above). * @} */ + +/** + * @defgroup ordering_hooks Ordering Hooks + * @{ + * Order hooks using the order parameter on the #[Hook] attribute. + * @see \Drupal\Core\Hook\Attribute\Hook + * + * The order parameter accepts the following simple ordering options. + * Order::First and Order::Last. + * @see \Drupal\Core\Hook\Order\Order + * + * The order parameter also accepts the following complex ordering options. + * OrderBefore() and OrderAfter(). + * @see \Drupal\Core\Hook\Order\OrderBefore + * @see \Drupal\Core\Hook\Order\OrderAfter + * + * Example ordering the hook first: + * @code + * #[Hook('somehook', order: Order::First)] + * @endcode + * + * Example ordering the hook last: + * @code + * #[Hook('somehook', order: Order::Last)] + * @endcode + * + * Example ordering the hook before another module's implementations: + * @code + * #[Hook('somehook', order: new OrderBefore(['othermodule']))] + * @endcode + * + * Example ordering the hook after another module's implementations: + * @code + * #[Hook('somehook', order: new OrderAfter(['othermodule']))] + * @endcode + * + * You can also specify the class and method instead of a module for both + * OrderBefore and OrderAfter: + * @code + * #[Hook('somehook', + * order: new OrderBefore( + * classesAndMethods: [ + * [Foo::class, 'someMethod'], + * [Bar::class, 'someOtherMethod'], + * ] + * ) + * )] + * @endcode + * @} + */ + +/** + * @defgroup ordering_other_module_hooks Ordering Other Module Hooks + * @{ + * Order hooks implemented in other modules with the #[ReOrderHook] attribute. + * + * @see \Drupal\Core\Hook\Attribute\ReOrderHook + * + * ReOrderHook takes the hook, class and method that is being overridden. The + * order parameter allows the same options as the parameter on the #[Hook] + * attribute. Ordering rules passed in ReOrder execute after rules defined in + * the #[Hook] attribute. + * + * @code + * #[ReOrderHook('entity_presave', + * class: ContentModerationHooks::class, + * method: 'entityPresave', + * order: new OrderBefore(['workspaces']) + * )] + * @endcode + * + * @} + */ + +/** + * @defgroup removing_hooks Removing Hooks + * @{ + * Removing hook implementations. + * + * Hooks implemented by other modules can be removed using the #[RemoveHook] + * attribute. + * + * @see \Drupal\Core\Hook\Attribute\RemoveHook + * + * @code + * #[RemoveHook('help', class: LayoutBuilderHooks::class, method: 'help')] + * @endcode + * @} + */ diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index 9053ec15d169..e9a3bd5dd5b4 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -97,7 +97,7 @@ function hook_hook_info(): array { * This hook will be removed in 12.0.0. It is not deprecated in order to * support the "#[LegacyModuleImplementsAlter]" attribute, used prior to Drupal * 11.2.0. - * See https://www.drupal.org/node/3496788. + * See \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter * * * Only procedural implementations are supported for this hook. @@ -122,7 +122,7 @@ function hook_hook_info(): array { * @param string $hook * The name of the module hook being implemented. * - * @see https://www.drupal.org/node/3496788 + * @see \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter */ function hook_module_implements_alter(&$implementations, $hook) { if ($hook == 'form_alter') { diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php index 5028d1b48e9d..4b2a4fd205d1 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php @@ -18,8 +18,6 @@ * On older versions of Drupal which are not aware of attribute-based ordering, * only the legacy hook implementation is executed. * - * @see https://www.drupal.org/node/3496788. - * * @internal */ #[\Attribute(\Attribute::TARGET_FUNCTION)] -- GitLab From 9b338d43c9d2e7dbb3e0d670ef87730a26ec1f40 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:07:28 +0200 Subject: [PATCH 240/268] Remove left-over module_test.implementations.inc. --- .../module_test/module_test.implementations.inc | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 core/modules/system/tests/modules/module_test/module_test.implementations.inc diff --git a/core/modules/system/tests/modules/module_test/module_test.implementations.inc b/core/modules/system/tests/modules/module_test/module_test.implementations.inc deleted file mode 100644 index b971af58c8c1..000000000000 --- a/core/modules/system/tests/modules/module_test/module_test.implementations.inc +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @file - * Include file for test module. - */ - -declare(strict_types=1); - -/** - * Implements hook_altered_test_hook(). - * - * @see module_test_module_implements_alter() - */ -function module_test_altered_test_hook(): string { - return __FUNCTION__; -} -- GitLab From 1fef62bde03b33aacd7612a8a8dc389a793afece Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:07:45 +0200 Subject: [PATCH 241/268] Remove left-over blank line in ModuleImplementsAlterTest. --- .../KernelTests/Core/Extension/ModuleImplementsAlterTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php b/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php index 2e9447c49913..1b3eb7db79d9 100644 --- a/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php +++ b/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php @@ -60,7 +60,6 @@ public function testModuleImplementsAlter(): void { // Assert that module_implements_alter_test.implementations.inc was included as part of the process. $this->assertTrue(function_exists('module_implements_alter_test_altered_test_hook'), 'The file module_implements_alter_test.implementations.inc was included.'); - } } -- GitLab From f74a22d22a58ab2428641b9d6bd1121ca1042e85 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:08:02 +0200 Subject: [PATCH 242/268] Fix indentation in ModuleImplementsAlterTest. --- .../KernelTests/Core/Extension/ModuleImplementsAlterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php b/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php index 1b3eb7db79d9..2a8ad684b538 100644 --- a/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php +++ b/core/tests/Drupal/KernelTests/Core/Extension/ModuleImplementsAlterTest.php @@ -42,10 +42,10 @@ public function testModuleImplementsAlter(): void { // Assert that module_implements_alter_test.module is now included. $this->assertTrue(function_exists('test_auto_include'), - 'The file module_implements_alter_test.module was successfully included.'); + 'The file module_implements_alter_test.module was successfully included.'); $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('module_implements_alter', 'module_implements_alter_test'), - 'module_implements_alter_test implements hook_module_implements_alter().'); + 'module_implements_alter_test implements hook_module_implements_alter().'); // Assert that module_implements_alter_test.implementations.inc is not included yet. $this->assertFalse(function_exists('module_implements_alter_test_altered_test_hook'), -- GitLab From bb715529ad99bf8d54901bd0ff0dac866c5ea396 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:08:15 +0200 Subject: [PATCH 243/268] Restore file mode in user_hooks_test.info.yml. --- .../user/tests/modules/user_hooks_test/user_hooks_test.info.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 core/modules/user/tests/modules/user_hooks_test/user_hooks_test.info.yml diff --git a/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.info.yml b/core/modules/user/tests/modules/user_hooks_test/user_hooks_test.info.yml old mode 100755 new mode 100644 -- GitLab From 0a3b63e6b43ec583bd2f241763cd362dd37656bb Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:09:04 +0200 Subject: [PATCH 244/268] Remove the `@internal` from attribute classes. --- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 2 -- core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php | 2 -- core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php | 2 -- 3 files changed, 6 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 0ad60877e7a2..409b69eabc33 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -94,8 +94,6 @@ * the procedural hook implementations. * * See \Drupal\Core\Hook\Attribute\LegacyHook for additional information. - * - * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Hook implements HookAttributeInterface { diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php index 4b2726a635c2..ee6501d7b42e 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyHook.php @@ -19,8 +19,6 @@ * only the legacy hook implementation is executed. * * For more information, see https://www.drupal.org/node/3442349. - * - * @internal */ #[\Attribute(\Attribute::TARGET_FUNCTION)] class LegacyHook { diff --git a/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php b/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php index cc41fff51533..73f0ce6915bd 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php +++ b/core/lib/Drupal/Core/Hook/Attribute/StopProceduralHookScan.php @@ -9,8 +9,6 @@ * * This allows contrib and core to mark when a file has no more * procedural hooks. - * - * @internal */ #[\Attribute(\Attribute::TARGET_FUNCTION)] class StopProceduralHookScan { -- GitLab From bc3b21126f7670a132154ddf9694f70bc4219797 Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:09:31 +0200 Subject: [PATCH 245/268] Remove unnecesary FQCN class name in a @covers doc. --- core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php index 0c079336c1e6..2d6fba3b451f 100644 --- a/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/Tests/Core/Hook/HookCollectorPassTest.php @@ -22,8 +22,8 @@ class HookCollectorPassTest extends UnitTestCase { use GroupIncludesTestTrait; /** - * @covers \Drupal\Core\Hook\HookCollectorPass::collectAllHookImplementations - * @covers \Drupal\Core\Hook\HookCollectorPass::filterIterator + * @covers ::collectAllHookImplementations + * @covers ::filterIterator */ public function testCollectAllHookImplementations(): void { vfsStream::setup('drupal_root'); @@ -72,7 +72,7 @@ function test_module_should_be_skipped(); /** * @covers ::process - * @covers \Drupal\Core\Hook\HookCollectorPass::collectModuleHookImplementations + * @covers ::collectModuleHookImplementations */ public function testGroupIncludes(): void { $module_filenames = self::setupGroupIncludes(); -- GitLab From 9c6945bc63c6bd8a55e0cf3b64dec848e8edb77b Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:09:53 +0200 Subject: [PATCH 246/268] Remove a pointless line break. --- .../module_implements_alter_test.module | 1 - 1 file changed, 1 deletion(-) diff --git a/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module index 6e7452988600..c379d30c360d 100644 --- a/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module +++ b/core/modules/system/tests/modules/module_implements_alter_test/module_implements_alter_test.module @@ -38,5 +38,4 @@ function module_implements_alter_test_module_implements_alter(&$implementations, unset($implementations['block']); $implementations['block'] = $group; } - } -- GitLab From 7894a8a7926baa6fd7991df9f8eb5461c9e4d00b Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Thu, 10 Apr 2025 01:10:37 +0200 Subject: [PATCH 247/268] Remove HookAlterOrderTest::testProceduralOrderSideEffect(). --- .../eee_hook_test/eee_hook_test.info.yml | 6 ------ .../eee_hook_test/src/Hook/Ordering.php | 17 ----------------- .../Core/Hook/HookAlterOrderTest.php | 6 ------ 3 files changed, 29 deletions(-) delete mode 100644 core/modules/system/tests/modules/HookOrder/eee_hook_test/eee_hook_test.info.yml delete mode 100644 core/modules/system/tests/modules/HookOrder/eee_hook_test/src/Hook/Ordering.php diff --git a/core/modules/system/tests/modules/HookOrder/eee_hook_test/eee_hook_test.info.yml b/core/modules/system/tests/modules/HookOrder/eee_hook_test/eee_hook_test.info.yml deleted file mode 100644 index a710a069b4f8..000000000000 --- a/core/modules/system/tests/modules/HookOrder/eee_hook_test/eee_hook_test.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: Hook Extra Test -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -core_version_requirement: '*' diff --git a/core/modules/system/tests/modules/HookOrder/eee_hook_test/src/Hook/Ordering.php b/core/modules/system/tests/modules/HookOrder/eee_hook_test/src/Hook/Ordering.php deleted file mode 100644 index 748918945c81..000000000000 --- a/core/modules/system/tests/modules/HookOrder/eee_hook_test/src/Hook/Ordering.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\eee_hook_test\Hook; - -use Drupal\Core\Extension\ProceduralCall; -use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\Core\Hook\Order\OrderBefore; - -/** - * Hooks for testing ordering. - */ -#[ReOrderHook('procedural_alter', ProceduralCall::class, 'aaa_hook_test_procedural_alter', new OrderBefore(['bbb_hook_test'], []))] -class Ordering { - -} diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php index 583e88317e1f..b3ffade5f36a 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php @@ -141,12 +141,6 @@ function (array &$implementations, string $hook) use ($modules, $move_b_down): v $this->assertAlterCallOrder($combined_unaltered, ['procedural', 'procedural_subtype']); } - public function testProceduralOrderSideEffect(): void { - $this->enableModules(['eee_hook_test']); - // The previous test should behave exactly the same. - $this->testProceduralModuleImplementsAlterOrder(); - } - public function testAlterOrder(): void { $this->assertAlterCallOrder([ CAlterHooks::class . '::testAlter', -- GitLab From d5b40aaee14aaee757159d5b52ea6af101ead1a7 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Wed, 9 Apr 2025 23:15:28 -0400 Subject: [PATCH 248/268] move ordering docs to hook group --- core/core.api.php | 168 ++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 89 deletions(-) diff --git a/core/core.api.php b/core/core.api.php index c66306a4d7b1..a4eace32caec 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1685,6 +1685,85 @@ * - Edit the body of the function, substituting in what you need your module * to do. * + * @section ordering_hooks Ordering Hooks + * + * Order hooks using the order parameter on the #[Hook] attribute. + * @see \Drupal\Core\Hook\Attribute\Hook + * + * The order parameter accepts the following simple ordering options. + * Order::First and Order::Last. + * @see \Drupal\Core\Hook\Order\Order + * + * The order parameter also accepts the following complex ordering options. + * OrderBefore() and OrderAfter(). + * @see \Drupal\Core\Hook\Order\OrderBefore + * @see \Drupal\Core\Hook\Order\OrderAfter + * + * Example ordering the hook first: + * @code + * #[Hook('somehook', order: Order::First)] + * @endcode + * + * Example ordering the hook last: + * @code + * #[Hook('somehook', order: Order::Last)] + * @endcode + * + * Example ordering the hook before another module's implementations: + * @code + * #[Hook('somehook', order: new OrderBefore(['othermodule']))] + * @endcode + * + * Example ordering the hook after another module's implementations: + * @code + * #[Hook('somehook', order: new OrderAfter(['othermodule']))] + * @endcode + * + * You can also specify the class and method instead of a module for both + * OrderBefore and OrderAfter: + * @code + * #[Hook('somehook', + * order: new OrderBefore( + * classesAndMethods: [ + * [Foo::class, 'someMethod'], + * [Bar::class, 'someOtherMethod'], + * ] + * ) + * )] + * @endcode + * + * @section ordering_other_module_hooks Ordering Other Module Hooks + * + * Order hooks implemented in other modules with the #[ReOrderHook] attribute. + * + * @see \Drupal\Core\Hook\Attribute\ReOrderHook + * + * ReOrderHook takes the hook, class and method that is being overridden. The + * order parameter allows the same options as the parameter on the #[Hook] + * attribute. Ordering rules passed in ReOrder execute after rules defined in + * the #[Hook] attribute. + * + * @code + * #[ReOrderHook('entity_presave', + * class: ContentModerationHooks::class, + * method: 'entityPresave', + * order: new OrderBefore(['workspaces']) + * )] + * @endcode + * + * @section removing_hooks Removing Hooks + * + * Removing hook implementations. + * + * Hooks implemented by other modules can be removed using the #[RemoveHook] + * attribute. + * + * @see \Drupal\Core\Hook\Attribute\RemoveHook + * + * @code + * #[RemoveHook('help', class: LayoutBuilderHooks::class, method: 'help')] + * @endcode + * * @section defining Defining a hook * * To define a hook: @@ -2767,92 +2846,3 @@ function hook_validation_constraint_alter(array &$definitions) { * longer needed (see @ref sec_intro above). * @} */ - -/** - * @defgroup ordering_hooks Ordering Hooks - * @{ - * Order hooks using the order parameter on the #[Hook] attribute. - * @see \Drupal\Core\Hook\Attribute\Hook - * - * The order parameter accepts the following simple ordering options. - * Order::First and Order::Last. - * @see \Drupal\Core\Hook\Order\Order - * - * The order parameter also accepts the following complex ordering options. - * OrderBefore() and OrderAfter(). - * @see \Drupal\Core\Hook\Order\OrderBefore - * @see \Drupal\Core\Hook\Order\OrderAfter - * - * Example ordering the hook first: - * @code - * #[Hook('somehook', order: Order::First)] - * @endcode - * - * Example ordering the hook last: - * @code - * #[Hook('somehook', order: Order::Last)] - * @endcode - * - * Example ordering the hook before another module's implementations: - * @code - * #[Hook('somehook', order: new OrderBefore(['othermodule']))] - * @endcode - * - * Example ordering the hook after another module's implementations: - * @code - * #[Hook('somehook', order: new OrderAfter(['othermodule']))] - * @endcode - * - * You can also specify the class and method instead of a module for both - * OrderBefore and OrderAfter: - * @code - * #[Hook('somehook', - * order: new OrderBefore( - * classesAndMethods: [ - * [Foo::class, 'someMethod'], - * [Bar::class, 'someOtherMethod'], - * ] - * ) - * )] - * @endcode - * @} - */ - -/** - * @defgroup ordering_other_module_hooks Ordering Other Module Hooks - * @{ - * Order hooks implemented in other modules with the #[ReOrderHook] attribute. - * - * @see \Drupal\Core\Hook\Attribute\ReOrderHook - * - * ReOrderHook takes the hook, class and method that is being overridden. The - * order parameter allows the same options as the parameter on the #[Hook] - * attribute. Ordering rules passed in ReOrder execute after rules defined in - * the #[Hook] attribute. - * - * @code - * #[ReOrderHook('entity_presave', - * class: ContentModerationHooks::class, - * method: 'entityPresave', - * order: new OrderBefore(['workspaces']) - * )] - * @endcode - * - * @} - */ - -/** - * @defgroup removing_hooks Removing Hooks - * @{ - * Removing hook implementations. - * - * Hooks implemented by other modules can be removed using the #[RemoveHook] - * attribute. - * - * @see \Drupal\Core\Hook\Attribute\RemoveHook - * - * @code - * #[RemoveHook('help', class: LayoutBuilderHooks::class, method: 'help')] - * @endcode - * @} - */ -- GitLab From 84045fa0a3220c99c0301db3ace4a8bfb2e68744 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Wed, 9 Apr 2025 23:20:11 -0400 Subject: [PATCH 249/268] Fix variable name --- core/lib/Drupal/Core/Hook/Order/OrderInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php index 70f597ac1fbf..f50169c15f5b 100644 --- a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php @@ -31,7 +31,7 @@ interface OrderInterface { * * @param string $identifier * Identifier of the implementation to move to a new position. - * The format is "$class::$module". + * The format is "$class::$method". * * @return \Drupal\Core\Hook\OrderOperation\OrderOperation * Order operation to apply to a hook implementation list. -- GitLab From 2dc5b80198c2ab4d6fefe9f7e6ff8ee3f5a1e92f Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Wed, 9 Apr 2025 23:21:17 -0400 Subject: [PATCH 250/268] See to @see --- core/core.api.php | 2 +- core/lib/Drupal/Core/Extension/module.api.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/core.api.php b/core/core.api.php index a4eace32caec..562714393547 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1657,7 +1657,7 @@ * Legacy meta hooks: * - hook_hook_info() * - hook_module_implements_alter() - * See \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter + * @see \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter * * Install hooks: * - hook_install() diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index e9a3bd5dd5b4..69c6f38699ee 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -97,7 +97,7 @@ function hook_hook_info(): array { * This hook will be removed in 12.0.0. It is not deprecated in order to * support the "#[LegacyModuleImplementsAlter]" attribute, used prior to Drupal * 11.2.0. - * See \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter + * @see \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter * * * Only procedural implementations are supported for this hook. -- GitLab From a2abb96f35060c7d04790e6a647033f572c7c1ce Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Wed, 9 Apr 2025 23:23:05 -0400 Subject: [PATCH 251/268] Fix order --- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index a05f52468afc..e3a37e08ef87 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -8,8 +8,8 @@ use Drupal\Component\Annotation\Reflection\MockFileFinder; use Drupal\Component\FileCache\FileCacheFactory; use Drupal\Core\Extension\ProceduralCall; -use Drupal\Core\Hook\Attribute\HookAttributeInterface; use Drupal\Core\Hook\Attribute\Hook; +use Drupal\Core\Hook\Attribute\HookAttributeInterface; use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter; use Drupal\Core\Hook\Attribute\RemoveHook; -- GitLab From e35a482c7c514693262636220697b462aa909c73 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Wed, 9 Apr 2025 23:42:58 -0400 Subject: [PATCH 252/268] Remove internal now that BC policy is clarified --- .../Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php | 2 -- core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php | 2 -- core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php | 2 -- 3 files changed, 6 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php index 4b2a4fd205d1..30929be33bc4 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php +++ b/core/lib/Drupal/Core/Hook/Attribute/LegacyModuleImplementsAlter.php @@ -17,8 +17,6 @@ * * On older versions of Drupal which are not aware of attribute-based ordering, * only the legacy hook implementation is executed. - * - * @internal */ #[\Attribute(\Attribute::TARGET_FUNCTION)] class LegacyModuleImplementsAlter {} diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php index d1f794cc6c49..a20143fe8e00 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php @@ -11,8 +11,6 @@ * * The effect of this attribute is independent from the specific class or method * on which it is placed. - * - * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class ReOrderHook implements HookAttributeInterface { diff --git a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php index 909fae18783a..28d4c9456cae 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/RemoveHook.php @@ -9,8 +9,6 @@ * * The effect of this attribute is independent from the specific class or method * on which it is placed. - * - * @internal */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class RemoveHook implements HookAttributeInterface { -- GitLab From 770bfa148e4372277a3c30ba017abb0cd30a80c1 Mon Sep 17 00:00:00 2001 From: nicxvan <29861-nicxvan@users.noreply.drupalcode.org> Date: Thu, 10 Apr 2025 12:55:09 +0000 Subject: [PATCH 253/268] core api clarifications --- core/core.api.php | 79 ++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/core/core.api.php b/core/core.api.php index 562714393547..f90127f538c8 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1687,42 +1687,40 @@ * * @section ordering_hooks Ordering Hooks * - * Order hooks using the order parameter on the #[Hook] attribute. - * @see \Drupal\Core\Hook\Attribute\Hook - * - * The order parameter accepts the following simple ordering options. - * Order::First and Order::Last. - * @see \Drupal\Core\Hook\Order\Order - * - * The order parameter also accepts the following complex ordering options. - * OrderBefore() and OrderAfter(). - * @see \Drupal\Core\Hook\Order\OrderBefore - * @see \Drupal\Core\Hook\Order\OrderAfter - * - * Example ordering the hook first: + * The order in which hooks are executed can be altered. A hook can be placed + * first or last in the order of execution. It can also be placed before or + * after the execution of another module's implementation of the same hook. When + * changing the order of execution in relation to a specific module either the + * module name or the class and method can be used. + * + * Use the order argument of the Order attribute to order the execution of + * hooks. + * + * Example of executing 'entity_type_alter' of my_module first: * @code - * #[Hook('somehook', order: Order::First)] + * #[Hook('entity_type_alter', order: Order::First)] * @endcode * - * Example ordering the hook last: + * Example of executing 'entity_type_alter' of my_module last: * @code - * #[Hook('somehook', order: Order::Last)] + * #[Hook('entity_type_alter', order: Order::Last)] * @endcode * - * Example ordering the hook before another module's implementations: + * Example of executing 'entity_type_alter' before the execution of the + * implementation in the Foo Module: * @code - * #[Hook('somehook', order: new OrderBefore(['othermodule']))] + * #[Hook('entity_type_alter', order: new OrderBefore(['foo']))] * @endcode * - * Example ordering the hook after another module's implementations: + * Example of executing 'entity_type_alter' after the execution of the + * implementation in the Foo Module: * @code - * #[Hook('somehook', order: new OrderAfter(['othermodule']))] + * #[Hook('entity_type_alter', order: new OrderAfter(['foo']))] * @endcode * - * You can also specify the class and method instead of a module for both - * OrderBefore and OrderAfter: + * Example of executing 'entity_type_alter' before two methods. * @code - * #[Hook('somehook', + * #[Hook('entity_type_alter', * order: new OrderBefore( * classesAndMethods: [ * [Foo::class, 'someMethod'], @@ -1732,17 +1730,19 @@ * )] * @endcode * + * @see \Drupal\Core\Hook\Attribute\Hook + * @see \Drupal\Core\Hook\Order\Order + * @see \Drupal\Core\Hook\Order\OrderBefore + * @see \Drupal\Core\Hook\Order\OrderAfter * @section ordering_other_module_hooks Ordering Other Module Hooks * - * Order hooks implemented in other modules with the #[ReOrderHook] attribute. - * - * @see \Drupal\Core\Hook\Attribute\ReOrderHook - * - * ReOrderHook takes the hook, class and method that is being overridden. The - * order parameter allows the same options as the parameter on the #[Hook] - * attribute. Ordering rules passed in ReOrder execute after rules defined in - * the #[Hook] attribute. + * The order in which hooks implemented in other modules are executed can be + * reordered. The reordering of the targeted hook is done relative to other + * implementations. The reordering process executes after the ordering defined + * in the Hook attribute. * + * Example of reordering the execution of the 'entity_presave' hook so that + * Content Moderation module hook executes before the Workspaces module hook. * @code * #[ReOrderHook('entity_presave', * class: ContentModerationHooks::class, @@ -1751,19 +1751,22 @@ * )] * @endcode * + * @see \Drupal\Core\Hook\Attribute\ReOrderHook * @section removing_hooks Removing Hooks * - * Removing hook implementations. - * - * Hooks implemented by other modules can be removed using the #[RemoveHook] - * attribute. - * - * @see \Drupal\Core\Hook\Attribute\RemoveHook + * The execution of a hooks implemented by other modules can be skipped. This + * is done by removing the targeted hook, use the RemoveHook attribute. * + * Example of removing the 'help' hook of the Layout Builder module. * @code - * #[RemoveHook('help', class: LayoutBuilderHooks::class, method: 'help')] + * #[RemoveHook('help', + * class: LayoutBuilderHooks::class, + * method: 'help' + * )] * @endcode * + * @see \Drupal\Core\Hook\Attribute\RemoveHook + * * @section defining Defining a hook * * To define a hook: -- GitLab From 52d840bc4eb85acad39a9d86149014ced7d43d72 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 10 Apr 2025 08:57:15 -0400 Subject: [PATCH 254/268] Identifier comments --- core/lib/Drupal/Core/Hook/Order/OrderInterface.php | 5 +++-- core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php | 5 +++-- core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php index f50169c15f5b..0f0d9a23ace4 100644 --- a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php @@ -30,8 +30,9 @@ interface OrderInterface { * Gets order operations specified by this object. * * @param string $identifier - * Identifier of the implementation to move to a new position. - * The format is "$class::$method". + * Identifier of the implementation to move to a new position. The format + * is the class followed by "::" then the method name. For example, + * "Drupal\my_module\Hook\MyModuleHooks::methodName". * * @return \Drupal\Core\Hook\OrderOperation\OrderOperation * Order operation to apply to a hook implementation list. diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php b/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php index 0e0533ddb212..e87f661fc392 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/BeforeOrAfter.php @@ -15,8 +15,9 @@ class BeforeOrAfter extends OrderOperation { * Constructor. * * @param string $identifier - * Identifier of the hook listener to move to a new position. - * The format is "$class::$method". + * Identifier of the implementation to move to a new position. The format + * is the class followed by "::" then the method name. For example, + * "Drupal\my_module\Hook\MyModuleHooks::methodName". * @param list<string> $modulesToOrderAgainst * Module names of listeners to order against. * @param list<string> $identifiersToOrderAgainst diff --git a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php index b09d38e0f566..2169e533891d 100644 --- a/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php +++ b/core/lib/Drupal/Core/Hook/OrderOperation/FirstOrLast.php @@ -15,8 +15,9 @@ class FirstOrLast extends OrderOperation { * Constructor. * * @param string $identifier - * Identifier of the hook listener to move to a new position. - * The format is "$class::$method". + * Identifier of the implementation to move to a new position. The format + * is the class followed by "::" then the method name. For example, + * "Drupal\my_module\Hook\MyModuleHooks::methodName". * @param bool $isLast * TRUE to move to the end, FALSE to move to the start. */ -- GitLab From 23f4d6638e84f6944701d5c5d8b2bc19861675a3 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 10 Apr 2025 08:59:02 -0400 Subject: [PATCH 255/268] Codesniff --- core/core.api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/core.api.php b/core/core.api.php index f90127f538c8..5144825bad09 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1692,7 +1692,7 @@ * after the execution of another module's implementation of the same hook. When * changing the order of execution in relation to a specific module either the * module name or the class and method can be used. - * + * * Use the order argument of the Order attribute to order the execution of * hooks. * -- GitLab From ea2ce52d7e90063f655a038419a2076174dead85 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 10 Apr 2025 09:00:49 -0400 Subject: [PATCH 256/268] Rename ReOrderHook to ReorderHook --- core/core.api.php | 4 ++-- core/lib/Drupal/Core/Hook/Attribute/Hook.php | 2 +- .../{ReOrderHook.php => ReorderHook.php} | 4 ++-- core/lib/Drupal/Core/Hook/HookCollectorPass.php | 4 ++-- .../Drupal/Core/Hook/Order/OrderInterface.php | 2 +- .../HookOrder/ddd_hook_test/src/Hook/DHooks.php | 4 ++-- .../src/Hook/TestHookReOrderHookFirst.php | 16 ++++++++-------- .../src/Hook/TestHookReOrderHookLast.php | 6 +++--- .../workspaces/src/Hook/EntityOperations.php | 4 ++-- .../Core/Hook/HookCollectorPassTest.php | 4 ++-- 10 files changed, 25 insertions(+), 25 deletions(-) rename core/lib/Drupal/Core/Hook/Attribute/{ReOrderHook.php => ReorderHook.php} (92%) diff --git a/core/core.api.php b/core/core.api.php index 5144825bad09..ac5f0aec5962 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1744,14 +1744,14 @@ * Example of reordering the execution of the 'entity_presave' hook so that * Content Moderation module hook executes before the Workspaces module hook. * @code - * #[ReOrderHook('entity_presave', + * #[ReorderHook('entity_presave', * class: ContentModerationHooks::class, * method: 'entityPresave', * order: new OrderBefore(['workspaces']) * )] * @endcode * - * @see \Drupal\Core\Hook\Attribute\ReOrderHook + * @see \Drupal\Core\Hook\Attribute\ReorderHook * @section removing_hooks Removing Hooks * * The execution of a hooks implemented by other modules can be skipped. This diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php index 409b69eabc33..33da9558b512 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php @@ -39,7 +39,7 @@ * \Drupal\Core\Hook\Attribute\RemoveHook. * * Ordering hook implementations in other modules can be done by using the - * attribute \Drupal\Core\Hook\Attribute\ReOrderHook. + * attribute \Drupal\Core\Hook\Attribute\ReorderHook. * * Classes that use this annotation on the class or on their methods are * automatically registered as autowired services with the class name as the diff --git a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php b/core/lib/Drupal/Core/Hook/Attribute/ReorderHook.php similarity index 92% rename from core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php rename to core/lib/Drupal/Core/Hook/Attribute/ReorderHook.php index a20143fe8e00..920552ec448e 100644 --- a/core/lib/Drupal/Core/Hook/Attribute/ReOrderHook.php +++ b/core/lib/Drupal/Core/Hook/Attribute/ReorderHook.php @@ -13,10 +13,10 @@ * on which it is placed. */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class ReOrderHook implements HookAttributeInterface { +class ReorderHook implements HookAttributeInterface { /** - * Constructs a ReOrderHook object. + * Constructs a ReorderHook object. * * @param string $hook * The hook for which to reorder an implementation. diff --git a/core/lib/Drupal/Core/Hook/HookCollectorPass.php b/core/lib/Drupal/Core/Hook/HookCollectorPass.php index e3a37e08ef87..df2f9f39cfa6 100644 --- a/core/lib/Drupal/Core/Hook/HookCollectorPass.php +++ b/core/lib/Drupal/Core/Hook/HookCollectorPass.php @@ -13,7 +13,7 @@ use Drupal\Core\Hook\Attribute\LegacyHook; use Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter; use Drupal\Core\Hook\Attribute\RemoveHook; -use Drupal\Core\Hook\Attribute\ReOrderHook; +use Drupal\Core\Hook\Attribute\ReorderHook; use Drupal\Core\Hook\Attribute\StopProceduralHookScan; use Drupal\Core\Hook\OrderOperation\OrderOperation; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -421,7 +421,7 @@ protected function collectModuleHookImplementations($dir, $module, $module_preg, $this->orderOperations[$attribute->hook][0][] = $attribute->order->getOperation("$class::$method"); } } - elseif ($attribute instanceof ReOrderHook) { + 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); diff --git a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php index 0f0d9a23ace4..85fc820a5c61 100644 --- a/core/lib/Drupal/Core/Hook/Order/OrderInterface.php +++ b/core/lib/Drupal/Core/Hook/Order/OrderInterface.php @@ -10,7 +10,7 @@ * Interface for order specifiers used in hook attributes. * * Objects implementing this interface allow for relative ordering of hooks. - * These objects are passed as an order parameter to a Hook or ReOrderHook + * These objects are passed as an order parameter to a Hook or ReorderHook * attribute. * Order::First and Order::Last are simple order operations that move the hook * implementation to the first or last position of hooks at the time the order diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php index 06606878106f..adf5a89c21be 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php @@ -6,14 +6,14 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\RemoveHook; -use Drupal\Core\Hook\Attribute\ReOrderHook; +use Drupal\Core\Hook\Attribute\ReorderHook; use Drupal\Core\Hook\Order\Order; use Drupal\ccc_hook_test\Hook\CHooks; /** * Hooks for testing ordering. */ -#[ReOrderHook('test_hook', CHooks::class, 'testHookReOrderFirst', Order::First)] +#[ReorderHook('test_hook', CHooks::class, 'testHookReOrderFirst', Order::First)] #[RemoveHook('test_hook', CHooks::class, 'testHookRemoved')] class DHooks { diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php index 8983c6b7e76b..3f65f6dfb38a 100644 --- a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php +++ b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php @@ -6,8 +6,8 @@ use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; -use Drupal\Core\Hook\Attribute\ReOrderHook; -use Drupal\ggg_hook_test\Hook\TestHookReOrderHookLast; +use Drupal\Core\Hook\Attribute\ReorderHook; +use Drupal\ggg_hook_test\Hook\TestHookReorderHookLast; /** * Hook implementations for verifying ordering hooks by attributes. @@ -21,24 +21,24 @@ * * Each method pair tests one hook ordering permutation. */ -class TestHookReOrderHookFirst { +class TestHookReorderHookFirst { /** - * This pair tests ReOrderHook. + * This pair tests ReorderHook. */ #[Hook('custom_hook_override')] - #[ReOrderHook( + #[ReorderHook( 'custom_hook_override', - class: TestHookReOrderHookLast::class, + class: TestHookReorderHookLast::class, method: 'customHookOverride', order: new OrderAfter( - classesAndMethods: [[TestHookReOrderHookFirst::class, 'customHookOverride']], + classesAndMethods: [[TestHookReorderHookFirst::class, 'customHookOverride']], ) )] public function customHookOverride(): string { // This normally would run first. // We override that order in hook_order_second_alphabetically. - // We override, that order here with ReOrderHook. + // We override, that order here with ReorderHook. return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php index 0dceeaff4f33..479f88b91311 100644 --- a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php +++ b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php @@ -19,17 +19,17 @@ * * Each method pair tests one hook ordering permutation. */ -class TestHookReOrderHookLast { +class TestHookReorderHookLast { /** - * This pair tests ReOrderHook. + * This pair tests ReorderHook. */ #[Hook('custom_hook_override', order: Order::First)] public function customHookOverride(): string { // This normally would run second. // We override that order here with Order::First. // We override, that order in fff_hook_test with - // ReOrderHook. + // ReorderHook. return __METHOD__; } diff --git a/core/modules/workspaces/src/Hook/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php index 7a9883bccdae..377ea62b2e88 100644 --- a/core/modules/workspaces/src/Hook/EntityOperations.php +++ b/core/modules/workspaces/src/Hook/EntityOperations.php @@ -12,7 +12,7 @@ use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Hook\Attribute\Hook; -use Drupal\Core\Hook\Attribute\ReOrderHook; +use Drupal\Core\Hook\Attribute\ReorderHook; use Drupal\Core\Hook\Order\Order; use Drupal\Core\Hook\Order\OrderBefore; use Drupal\content_moderation\Hook\ContentModerationHooks; @@ -77,7 +77,7 @@ public function entityPreload(array $ids, string $entity_type_id): array { * Implements hook_entity_presave(). */ #[Hook('entity_presave', order: Order::First)] - #[ReOrderHook('entity_presave', + #[ReorderHook('entity_presave', class: ContentModerationHooks::class, method: 'entityPresave', order: new OrderBefore(['workspaces']) diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 7ce711bd5e17..5e0a0332f4d8 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -294,8 +294,8 @@ public function testHookOverride(): void { $module_installer->install(['ggg_hook_test']); $module_handler = $this->container->get('module_handler'); $expected_calls = [ - 'Drupal\fff_hook_test\Hook\TestHookReOrderHookFirst::customHookOverride', - 'Drupal\ggg_hook_test\Hook\TestHookReOrderHookLast::customHookOverride', + 'Drupal\fff_hook_test\Hook\TestHookReorderHookFirst::customHookOverride', + 'Drupal\ggg_hook_test\Hook\TestHookReorderHookLast::customHookOverride', ]; $calls = $module_handler->invokeAll('custom_hook_override'); $this->assertEquals($expected_calls, $calls); -- GitLab From a49a1b7c3625cbcfff1dac273414b64d50f21f12 Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 10 Apr 2025 09:10:56 -0400 Subject: [PATCH 257/268] Codesniff --- core/lib/Drupal/Core/Extension/module.api.php | 2 -- ...estHookReOrderHookFirst.php => TestHookReorderHookFirst.php} | 0 ...{TestHookReOrderHookLast.php => TestHookReorderHookLast.php} | 0 3 files changed, 2 deletions(-) rename core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/{TestHookReOrderHookFirst.php => TestHookReorderHookFirst.php} (100%) rename core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/{TestHookReOrderHookLast.php => TestHookReorderHookLast.php} (100%) diff --git a/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php index 69c6f38699ee..a5f160a8f92a 100644 --- a/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -97,8 +97,6 @@ function hook_hook_info(): array { * This hook will be removed in 12.0.0. It is not deprecated in order to * support the "#[LegacyModuleImplementsAlter]" attribute, used prior to Drupal * 11.2.0. - * @see \Drupal\Core\Hook\Attribute\LegacyModuleImplementsAlter - * * * Only procedural implementations are supported for this hook. * diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php b/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReorderHookFirst.php similarity index 100% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReOrderHookFirst.php rename to core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReorderHookFirst.php diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php b/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReorderHookLast.php similarity index 100% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReOrderHookLast.php rename to core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReorderHookLast.php -- GitLab From 27191ba33184a6129c3f12916573101ac74eb7a1 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Thu, 10 Apr 2025 21:02:04 -0400 Subject: [PATCH 258/268] Use hook implementation in docs --- core/core.api.php | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/core/core.api.php b/core/core.api.php index ac5f0aec5962..095b443535d4 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1606,6 +1606,14 @@ * modules that they interact with. Your modules can also define their own * hooks, in order to let other modules interact with them. * + * Hook implementations will execute in the following order. + * order. + * - Module weight. + * - Alphabetical by module name. + * - This order can be modified by using the order parameter on the #[Hook] + * attribute, using the #[ReorderHook] attribute, or implementing the legacy + * hook_module_implements_alter. + * * @section implementing Implementing a hook * * There are two ways to implement a hook: @@ -1685,15 +1693,15 @@ * - Edit the body of the function, substituting in what you need your module * to do. * - * @section ordering_hooks Ordering Hooks + * @section ordering_hooks Ordering hook implementations * - * The order in which hooks are executed can be altered. A hook can be placed - * first or last in the order of execution. It can also be placed before or - * after the execution of another module's implementation of the same hook. When - * changing the order of execution in relation to a specific module either the - * module name or the class and method can be used. + * The order in which hook implementations are executed can be modified. A hook + * can be placed first or last in the order of execution. It can also be placed + * before or after the execution of another module's implementation of the same + * hook. When changing the order of execution in relation to a specific module + * either the module name or the class and method can be used. * - * Use the order argument of the Order attribute to order the execution of + * Use the order argument of the Hook attribute to order the execution of * hooks. * * Example of executing 'entity_type_alter' of my_module first: @@ -1707,18 +1715,19 @@ * @endcode * * Example of executing 'entity_type_alter' before the execution of the - * implementation in the Foo Module: + * implementation in the foo module: * @code * #[Hook('entity_type_alter', order: new OrderBefore(['foo']))] * @endcode * * Example of executing 'entity_type_alter' after the execution of the - * implementation in the Foo Module: + * implementation in the foo module: * @code * #[Hook('entity_type_alter', order: new OrderAfter(['foo']))] * @endcode * - * Example of executing 'entity_type_alter' before two methods. + * Example of executing 'entity_type_alter' before two methods. One in the Foo + * class and one in the Bar class. * @code * #[Hook('entity_type_alter', * order: new OrderBefore( @@ -1734,7 +1743,7 @@ * @see \Drupal\Core\Hook\Order\Order * @see \Drupal\Core\Hook\Order\OrderBefore * @see \Drupal\Core\Hook\Order\OrderAfter - * @section ordering_other_module_hooks Ordering Other Module Hooks + * @section ordering_other_module_hooks Ordering other module hook implementations * * The order in which hooks implemented in other modules are executed can be * reordered. The reordering of the targeted hook is done relative to other @@ -1752,7 +1761,7 @@ * @endcode * * @see \Drupal\Core\Hook\Attribute\ReorderHook - * @section removing_hooks Removing Hooks + * @section removing_hooks Removing hook implementations * * The execution of a hooks implemented by other modules can be skipped. This * is done by removing the targeted hook, use the RemoveHook attribute. -- GitLab From 97d370c1b0ba7bbfbca976a5fc70ce064831576e Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Thu, 10 Apr 2025 23:52:59 -0400 Subject: [PATCH 259/268] Organize order documentation --- core/core.api.php | 53 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/core/core.api.php b/core/core.api.php index 095b443535d4..e93fa0bb5b5c 100644 --- a/core/core.api.php +++ b/core/core.api.php @@ -1693,6 +1693,31 @@ * - Edit the body of the function, substituting in what you need your module * to do. * + * @section defining Defining a hook + * + * To define a hook: + * - Choose a unique name for your hook. It should start with "hook_", followed + * by your module's short name. + * - Provide documentation in a *.api.php file in your module's main + * directory. See the "implementing" section above for details of what this + * should contain (parameters, return value, and sample function body). + * - Invoke the hook in your module's code. + * + * @section invoking Invoking a hook + * + * To invoke a hook, use methods on + * \Drupal\Core\Extension\ModuleHandlerInterface such as alter(), invoke(), + * and invokeAll(). You can obtain a module handler by calling + * \Drupal::moduleHandler(), or getting the 'module_handler' service on an + * injected container. + * + * @see extending + * @see themeable + * @see callbacks + * @see \Drupal\Core\Extension\ModuleHandlerInterface + * @see \Drupal\Core\Hook\Attribute\Hook + * @see \Drupal::moduleHandler() + * * @section ordering_hooks Ordering hook implementations * * The order in which hook implementations are executed can be modified. A hook @@ -1743,6 +1768,7 @@ * @see \Drupal\Core\Hook\Order\Order * @see \Drupal\Core\Hook\Order\OrderBefore * @see \Drupal\Core\Hook\Order\OrderAfter + * * @section ordering_other_module_hooks Ordering other module hook implementations * * The order in which hooks implemented in other modules are executed can be @@ -1761,6 +1787,7 @@ * @endcode * * @see \Drupal\Core\Hook\Attribute\ReorderHook + * * @section removing_hooks Removing hook implementations * * The execution of a hooks implemented by other modules can be skipped. This @@ -1775,32 +1802,6 @@ * @endcode * * @see \Drupal\Core\Hook\Attribute\RemoveHook - * - * @section defining Defining a hook - * - * To define a hook: - * - Choose a unique name for your hook. It should start with "hook_", followed - * by your module's short name. - * - Provide documentation in a *.api.php file in your module's main - * directory. See the "implementing" section above for details of what this - * should contain (parameters, return value, and sample function body). - * - Invoke the hook in your module's code. - * - * @section invoking Invoking a hook - * - * To invoke a hook, use methods on - * \Drupal\Core\Extension\ModuleHandlerInterface such as alter(), invoke(), - * and invokeAll(). You can obtain a module handler by calling - * \Drupal::moduleHandler(), or getting the 'module_handler' service on an - * injected container. - * - * @see extending - * @see themeable - * @see callbacks - * @see \Drupal\Core\Extension\ModuleHandlerInterface - * @see \Drupal\Core\Hook\Attribute\Hook - * @see \Drupal::moduleHandler() - * * @} */ -- GitLab From 4649a753888a2768ac2479ccbe6c23b8c71b48ba Mon Sep 17 00:00:00 2001 From: Andreas Hennings <andreas@dqxtech.net> Date: Sat, 12 Apr 2025 15:14:23 +0200 Subject: [PATCH 260/268] Rename testHookReOrderFirst -> testHookReorderFirst. --- .../tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php | 2 +- .../tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php | 2 +- core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php index e22903bba60e..617d9a84b8a5 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php @@ -28,7 +28,7 @@ public function testHookFirst(): string { * @see \Drupal\ddd_hook_test\Hook\DHooks */ #[Hook('test_hook')] - public function testHookReOrderFirst(): string { + public function testHookReorderFirst(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php index adf5a89c21be..8c0372cae961 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php @@ -13,7 +13,7 @@ /** * Hooks for testing ordering. */ -#[ReorderHook('test_hook', CHooks::class, 'testHookReOrderFirst', Order::First)] +#[ReorderHook('test_hook', CHooks::class, 'testHookReorderFirst', Order::First)] #[RemoveHook('test_hook', CHooks::class, 'testHookRemoved')] class DHooks { diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index 9d3cda146a07..b4c42e712028 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -32,7 +32,7 @@ class HookOrderTest extends KernelTestBase { public function testHookOrder(): void { $this->assertSameCallList( [ - CHooks::class . '::testHookReOrderFirst', + CHooks::class . '::testHookReorderFirst', CHooks::class . '::testHookFirst', AHooks::class . '::testHookFirst', 'aaa_hook_test_test_hook', -- GitLab From 133a544090bb7f92009b0cc1a713aaa08f781a50 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 15 Apr 2025 21:04:46 -0400 Subject: [PATCH 261/268] Rename hook test modules to be more descriptive Missed rename --- .../aaa_hook_order_test.info.yml} | 0 .../aaa_hook_order_test.module} | 12 +-- .../src/Hook/AAlterHooks.php | 4 +- .../src/Hook/AHooks.php | 4 +- .../src/Hook/ModuleImplementsAlter.php | 2 +- .../bbb_hook_order_test.info.yml} | 0 .../bbb_hook_order_test.module} | 6 +- .../src/Hook/BAlterHooks.php | 2 +- .../src/Hook/BHooks.php | 2 +- .../ccc_hook_order_test.info.yml} | 0 .../ccc_hook_order_test.module} | 8 +- .../src/Hook/CAlterHooks.php | 2 +- .../src/Hook/CHooks.php | 6 +- .../ddd_hook_order_test.info.yml} | 0 .../ddd_hook_order_test.module} | 2 +- .../src/Hook/DAlterHooks.php | 2 +- .../src/Hook/DHooks.php | 4 +- .../Core/Hook/HookAlterOrderTest.php | 76 +++++++++---------- .../KernelTests/Core/Hook/HookOrderTest.php | 28 +++---- 19 files changed, 80 insertions(+), 80 deletions(-) rename core/modules/system/tests/modules/HookOrder/{aaa_hook_test/aaa_hook_test.info.yml => aaa_hook_order_test/aaa_hook_order_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{aaa_hook_test/aaa_hook_test.module => aaa_hook_order_test/aaa_hook_order_test.module} (55%) rename core/modules/system/tests/modules/HookOrder/{aaa_hook_test => aaa_hook_order_test}/src/Hook/AAlterHooks.php (88%) rename core/modules/system/tests/modules/HookOrder/{aaa_hook_test => aaa_hook_order_test}/src/Hook/AHooks.php (91%) rename core/modules/system/tests/modules/HookOrder/{aaa_hook_test => aaa_hook_order_test}/src/Hook/ModuleImplementsAlter.php (96%) rename core/modules/system/tests/modules/HookOrder/{bbb_hook_test/bbb_hook_test.info.yml => bbb_hook_order_test/bbb_hook_order_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{bbb_hook_test/bbb_hook_test.module => bbb_hook_order_test/bbb_hook_order_test.module} (62%) rename core/modules/system/tests/modules/HookOrder/{bbb_hook_test => bbb_hook_order_test}/src/Hook/BAlterHooks.php (85%) rename core/modules/system/tests/modules/HookOrder/{bbb_hook_test => bbb_hook_order_test}/src/Hook/BHooks.php (88%) rename core/modules/system/tests/modules/HookOrder/{ccc_hook_test/ccc_hook_test.info.yml => ccc_hook_order_test/ccc_hook_order_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{ccc_hook_test/ccc_hook_test.module => ccc_hook_order_test/ccc_hook_order_test.module} (60%) rename core/modules/system/tests/modules/HookOrder/{ccc_hook_test => ccc_hook_order_test}/src/Hook/CAlterHooks.php (89%) rename core/modules/system/tests/modules/HookOrder/{ccc_hook_test => ccc_hook_order_test}/src/Hook/CHooks.php (83%) rename core/modules/system/tests/modules/HookOrder/{ddd_hook_test/ddd_hook_test.info.yml => ddd_hook_order_test/ddd_hook_order_test.info.yml} (100%) rename core/modules/system/tests/modules/HookOrder/{ddd_hook_test/ddd_hook_test.module => ddd_hook_order_test/ddd_hook_order_test.module} (75%) rename core/modules/system/tests/modules/HookOrder/{ddd_hook_test => ddd_hook_order_test}/src/Hook/DAlterHooks.php (89%) rename core/modules/system/tests/modules/HookOrder/{ddd_hook_test => ddd_hook_order_test}/src/Hook/DHooks.php (87%) diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.info.yml b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.info.yml rename to core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.module b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.module similarity index 55% rename from core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.module rename to core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.module index 97a3a98d649d..513a72c21f44 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/aaa_hook_test.module +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.module @@ -7,39 +7,39 @@ declare(strict_types=1); -use Drupal\aaa_hook_test\Hook\ModuleImplementsAlter; +use Drupal\aaa_hook_order_test\Hook\ModuleImplementsAlter; /** * Implements hook_test_hook(). */ -function aaa_hook_test_test_hook(): string { +function aaa_hook_order_test_test_hook(): string { return __FUNCTION__; } /** * Implements hook_sparse_test_hook(). */ -function aaa_hook_test_sparse_test_hook(): string { +function aaa_hook_order_test_sparse_test_hook(): string { return __FUNCTION__; } /** * Implements hook_procedural_alter(). */ -function aaa_hook_test_procedural_alter(array &$calls): void { +function aaa_hook_order_test_procedural_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_procedural_subtype_alter(). */ -function aaa_hook_test_procedural_subtype_alter(array &$calls): void { +function aaa_hook_order_test_procedural_subtype_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_module_implements_alter(). */ -function aaa_hook_test_module_implements_alter(array &$implementations, string $hook): void { +function aaa_hook_order_test_module_implements_alter(array &$implementations, string $hook): void { ModuleImplementsAlter::call($implementations, $hook); } diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AAlterHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php similarity index 88% rename from core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php index ab054e269312..3c6384897e76 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\aaa_hook_test\Hook; +namespace Drupal\aaa_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; @@ -12,7 +12,7 @@ */ class AAlterHooks { - #[Hook('test_alter', order: new OrderAfter(modules: ['ccc_hook_test']))] + #[Hook('test_alter', order: new OrderAfter(modules: ['ccc_hook_order_test']))] public function testAlterAfterC(array &$calls): void { $calls[] = __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php similarity index 91% rename from core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AHooks.php rename to core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php index 56a14392a453..bf10c4cf4b92 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/AHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\aaa_hook_test\Hook; +namespace Drupal\aaa_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; @@ -28,7 +28,7 @@ public function testHookLast(): string { return __METHOD__; } - #[Hook('test_hook', order: new OrderAfter(modules: ['bbb_hook_test']))] + #[Hook('test_hook', order: new OrderAfter(modules: ['bbb_hook_order_test']))] public function testHookAfterB(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/ModuleImplementsAlter.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/ModuleImplementsAlter.php similarity index 96% rename from core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/ModuleImplementsAlter.php rename to core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/ModuleImplementsAlter.php index ba1c84d96424..76d3912bdebe 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_test/src/Hook/ModuleImplementsAlter.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/ModuleImplementsAlter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\aaa_hook_test\Hook; +namespace Drupal\aaa_hook_order_test\Hook; /** * Contains a replaceable callback for hook_module_implements_alter(). diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.info.yml b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.info.yml rename to core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.module b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.module similarity index 62% rename from core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.module rename to core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.module index 51bc06948541..c2ac51b2f29c 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_test/bbb_hook_test.module +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.module @@ -10,20 +10,20 @@ /** * Implements hook_test_hook(). */ -function bbb_hook_test_test_hook(): string { +function bbb_hook_order_test_test_hook(): string { return __FUNCTION__; } /** * Implements hook_procedural_alter(). */ -function bbb_hook_test_procedural_alter(array &$calls): void { +function bbb_hook_order_test_procedural_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_procedural_subtype_alter(). */ -function bbb_hook_test_procedural_subtype_alter(array &$calls): void { +function bbb_hook_order_test_procedural_subtype_alter(array &$calls): void { $calls[] = __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BAlterHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php similarity index 85% rename from core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php index 648e75e30aa7..3e66ed2ca5d3 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\bbb_hook_test\Hook; +namespace Drupal\bbb_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php similarity index 88% rename from core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BHooks.php rename to core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php index 939f9d0d28fa..e35e992f2db1 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_test/src/Hook/BHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\bbb_hook_test\Hook; +namespace Drupal\bbb_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.info.yml b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.info.yml rename to core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.module b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.module similarity index 60% rename from core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.module rename to core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.module index 8b36d92e2d2c..3c6246298b72 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/ccc_hook_test.module +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.module @@ -10,27 +10,27 @@ /** * Implements hook_test_hook(). */ -function ccc_hook_test_test_hook(): string { +function ccc_hook_order_test_test_hook(): string { return __FUNCTION__; } /** * Implements hook_sparse_test_hook(). */ -function ccc_hook_test_sparse_test_hook(): string { +function ccc_hook_order_test_sparse_test_hook(): string { return __FUNCTION__; } /** * Implements hook_procedural_alter(). */ -function ccc_hook_test_procedural_alter(array &$calls): void { +function ccc_hook_order_test_procedural_alter(array &$calls): void { $calls[] = __FUNCTION__; } /** * Implements hook_procedural_subtype_alter(). */ -function ccc_hook_test_procedural_subtype_alter(array &$calls): void { +function ccc_hook_order_test_procedural_subtype_alter(array &$calls): void { $calls[] = __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php similarity index 89% rename from core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php index ab2f3f363915..451c14eae54e 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ccc_hook_test\Hook; +namespace Drupal\ccc_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php similarity index 83% rename from core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php rename to core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php index 617d9a84b8a5..023e87bf68a4 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ccc_hook_test\Hook; +namespace Drupal\ccc_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; @@ -25,7 +25,7 @@ public function testHookFirst(): string { /** * This implementation is reordered from elsewhere. * - * @see \Drupal\ddd_hook_test\Hook\DHooks + * @see \Drupal\ddd_hook_order_test\Hook\DHooks */ #[Hook('test_hook')] public function testHookReorderFirst(): string { @@ -35,7 +35,7 @@ public function testHookReorderFirst(): string { /** * This implementation is removed from elsewhere. * - * @see \Drupal\ddd_hook_test\Hook\DHooks + * @see \Drupal\ddd_hook_order_test\Hook\DHooks */ #[Hook('test_hook')] public function testHookRemoved(): string { diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.info.yml b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.info.yml rename to core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.module b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.module similarity index 75% rename from core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.module rename to core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.module index 0ed724ab50d1..82cccc7ba6c3 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/ddd_hook_test.module +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.module @@ -10,6 +10,6 @@ /** * Implements hook_test_hook(). */ -function ddd_hook_test_test_hook(): string { +function ddd_hook_order_test_test_hook(): string { return __FUNCTION__; } diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php similarity index 89% rename from core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DAlterHooks.php rename to core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php index 5586ddea5c8c..b1745f427c97 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ddd_hook_test\Hook; +namespace Drupal\ddd_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php similarity index 87% rename from core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php rename to core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php index 8c0372cae961..9dc2c42a0337 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Drupal\ddd_hook_test\Hook; +namespace Drupal\ddd_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Attribute\RemoveHook; use Drupal\Core\Hook\Attribute\ReorderHook; use Drupal\Core\Hook\Order\Order; -use Drupal\ccc_hook_test\Hook\CHooks; +use Drupal\ccc_hook_order_test\Hook\CHooks; /** * Hooks for testing ordering. diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php index b3ffade5f36a..a96040e90356 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookAlterOrderTest.php @@ -4,11 +4,11 @@ namespace Drupal\KernelTests\Core\Hook; -use Drupal\aaa_hook_test\Hook\AAlterHooks; -use Drupal\aaa_hook_test\Hook\ModuleImplementsAlter; -use Drupal\bbb_hook_test\Hook\BAlterHooks; -use Drupal\ccc_hook_test\Hook\CAlterHooks; -use Drupal\ddd_hook_test\Hook\DAlterHooks; +use Drupal\aaa_hook_order_test\Hook\AAlterHooks; +use Drupal\aaa_hook_order_test\Hook\ModuleImplementsAlter; +use Drupal\bbb_hook_order_test\Hook\BAlterHooks; +use Drupal\ccc_hook_order_test\Hook\CAlterHooks; +use Drupal\ddd_hook_order_test\Hook\DAlterHooks; use Drupal\KernelTests\KernelTestBase; use PHPUnit\Framework\Attributes\IgnoreDeprecations; @@ -24,41 +24,41 @@ class HookAlterOrderTest extends KernelTestBase { * {@inheritdoc} */ protected static $modules = [ - 'aaa_hook_test', - 'bbb_hook_test', - 'ccc_hook_test', - 'ddd_hook_test', + 'aaa_hook_order_test', + 'bbb_hook_order_test', + 'ccc_hook_order_test', + 'ddd_hook_order_test', ]; public function testProceduralModuleImplementsAlterOrder(): void { $this->assertAlterCallOrder($main_unaltered = [ - 'aaa_hook_test_procedural_alter', - 'bbb_hook_test_procedural_alter', - 'ccc_hook_test_procedural_alter', + 'aaa_hook_order_test_procedural_alter', + 'bbb_hook_order_test_procedural_alter', + 'ccc_hook_order_test_procedural_alter', ], 'procedural'); $this->assertAlterCallOrder($sub_unaltered = [ - 'aaa_hook_test_procedural_subtype_alter', - 'bbb_hook_test_procedural_subtype_alter', - 'ccc_hook_test_procedural_subtype_alter', + 'aaa_hook_order_test_procedural_subtype_alter', + 'bbb_hook_order_test_procedural_subtype_alter', + 'ccc_hook_order_test_procedural_subtype_alter', ], 'procedural_subtype'); $this->assertAlterCallOrder($combined_unaltered = [ - 'aaa_hook_test_procedural_alter', - 'aaa_hook_test_procedural_subtype_alter', - 'bbb_hook_test_procedural_alter', - 'bbb_hook_test_procedural_subtype_alter', - 'ccc_hook_test_procedural_alter', - 'ccc_hook_test_procedural_subtype_alter', + 'aaa_hook_order_test_procedural_alter', + 'aaa_hook_order_test_procedural_subtype_alter', + 'bbb_hook_order_test_procedural_alter', + 'bbb_hook_order_test_procedural_subtype_alter', + 'ccc_hook_order_test_procedural_alter', + 'ccc_hook_order_test_procedural_subtype_alter', ], ['procedural', 'procedural_subtype']); $move_b_down = function (array &$implementations): void { // Move B to the end, no matter which hook. - $group = $implementations['bbb_hook_test']; - unset($implementations['bbb_hook_test']); - $implementations['bbb_hook_test'] = $group; + $group = $implementations['bbb_hook_order_test']; + unset($implementations['bbb_hook_order_test']); + $implementations['bbb_hook_order_test'] = $group; }; - $modules = ['aaa_hook_test', 'bbb_hook_test', 'ccc_hook_test']; + $modules = ['aaa_hook_order_test', 'bbb_hook_order_test', 'ccc_hook_order_test']; // Test with module B moved to the end for both hooks. ModuleImplementsAlter::set( @@ -73,27 +73,27 @@ function (array &$implementations, string $hook) use ($modules, $move_b_down): v \Drupal::service('kernel')->rebuildContainer(); $this->assertAlterCallOrder($main_altered = [ - 'aaa_hook_test_procedural_alter', - 'ccc_hook_test_procedural_alter', + 'aaa_hook_order_test_procedural_alter', + 'ccc_hook_order_test_procedural_alter', // The implementation of B has been moved. - 'bbb_hook_test_procedural_alter', + 'bbb_hook_order_test_procedural_alter', ], 'procedural'); $this->assertAlterCallOrder($sub_altered = [ - 'aaa_hook_test_procedural_subtype_alter', - 'ccc_hook_test_procedural_subtype_alter', + 'aaa_hook_order_test_procedural_subtype_alter', + 'ccc_hook_order_test_procedural_subtype_alter', // The implementation of B has been moved. - 'bbb_hook_test_procedural_subtype_alter', + 'bbb_hook_order_test_procedural_subtype_alter', ], 'procedural_subtype'); $this->assertAlterCallOrder($combined_altered = [ - 'aaa_hook_test_procedural_alter', - 'aaa_hook_test_procedural_subtype_alter', - 'ccc_hook_test_procedural_alter', - 'ccc_hook_test_procedural_subtype_alter', + 'aaa_hook_order_test_procedural_alter', + 'aaa_hook_order_test_procedural_subtype_alter', + 'ccc_hook_order_test_procedural_alter', + 'ccc_hook_order_test_procedural_subtype_alter', // The implementation of B has been moved. - 'bbb_hook_test_procedural_alter', - 'bbb_hook_test_procedural_subtype_alter', + 'bbb_hook_order_test_procedural_alter', + 'bbb_hook_order_test_procedural_subtype_alter', ], ['procedural', 'procedural_subtype']); // If the altered hook is not the first one, implementations are back in @@ -166,7 +166,7 @@ public function testAlterOrder(): void { DAlterHooks::class . '::testSubtypeAlter', ], ['test', 'test_subtype']); - $this->disableModules(['bbb_hook_test']); + $this->disableModules(['bbb_hook_order_test']); $this->assertAlterCallOrder([ CAlterHooks::class . '::testAlter', diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php index b4c42e712028..971c95bc8e9d 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookOrderTest.php @@ -4,10 +4,10 @@ namespace Drupal\KernelTests\Core\Hook; -use Drupal\aaa_hook_test\Hook\AHooks; -use Drupal\bbb_hook_test\Hook\BHooks; -use Drupal\ccc_hook_test\Hook\CHooks; -use Drupal\ddd_hook_test\Hook\DHooks; +use Drupal\aaa_hook_order_test\Hook\AHooks; +use Drupal\bbb_hook_order_test\Hook\BHooks; +use Drupal\ccc_hook_order_test\Hook\CHooks; +use Drupal\ddd_hook_order_test\Hook\DHooks; use Drupal\KernelTests\KernelTestBase; use PHPUnit\Framework\Attributes\IgnoreDeprecations; @@ -23,10 +23,10 @@ class HookOrderTest extends KernelTestBase { * {@inheritdoc} */ protected static $modules = [ - 'aaa_hook_test', - 'bbb_hook_test', - 'ccc_hook_test', - 'ddd_hook_test', + 'aaa_hook_order_test', + 'bbb_hook_order_test', + 'ccc_hook_order_test', + 'ddd_hook_order_test', ]; public function testHookOrder(): void { @@ -35,14 +35,14 @@ public function testHookOrder(): void { CHooks::class . '::testHookReorderFirst', CHooks::class . '::testHookFirst', AHooks::class . '::testHookFirst', - 'aaa_hook_test_test_hook', + 'aaa_hook_order_test_test_hook', AHooks::class . '::testHook', - 'bbb_hook_test_test_hook', + 'bbb_hook_order_test_test_hook', BHooks::class . '::testHook', AHooks::class . '::testHookAfterB', - 'ccc_hook_test_test_hook', + 'ccc_hook_order_test_test_hook', CHooks::class . '::testHook', - 'ddd_hook_test_test_hook', + 'ddd_hook_order_test_test_hook', DHooks::class . '::testHook', AHooks::class . '::testHookLast', ], @@ -62,9 +62,9 @@ public function testSparseHookOrder(): void { [ // OOP and procedural listeners are correctly intermixed by module // order. - 'aaa_hook_test_sparse_test_hook', + 'aaa_hook_order_test_sparse_test_hook', BHooks::class . '::sparseTestHook', - 'ccc_hook_test_sparse_test_hook', + 'ccc_hook_order_test_sparse_test_hook', DHooks::class . '::sparseTestHook', ], \Drupal::moduleHandler()->invokeAll('sparse_test_hook'), -- GitLab From eaaab7a20083befdd81b8f443ed155135fcd606f Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 15 Apr 2025 21:10:55 -0400 Subject: [PATCH 262/268] Add description for naming --- .../modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php | 4 ++++ .../modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php | 4 ++++ .../modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php | 4 ++++ .../modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php index bf10c4cf4b92..3dc0b707aeae 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php @@ -10,6 +10,10 @@ /** * Hooks for testing ordering. + * + * The hook order test modules are named alphabetically so we have a known + * default order. We then use attributes to reorder hooks in these modules and + * test that the ordering attributes produce the expected hook call order. */ class AHooks { diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php index e35e992f2db1..9a736719e8ce 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php @@ -8,6 +8,10 @@ /** * Hooks for testing ordering. + * + * The hook order test modules are named alphabetically so we have a known + * default order. We then use attributes to reorder hooks in these modules and + * test that the ordering attributes produce the expected hook call order. */ class BHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php index 023e87bf68a4..8a304f1b0e1d 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php @@ -9,6 +9,10 @@ /** * Hooks for testing ordering. + * + * The hook order test modules are named alphabetically so we have a known + * default order. We then use attributes to reorder hooks in these modules and + * test that the ordering attributes produce the expected hook call order. */ class CHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php index 9dc2c42a0337..ee6a4741a365 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php @@ -12,6 +12,10 @@ /** * Hooks for testing ordering. + * + * The hook order test modules are named alphabetically so we have a known + * default order. We then use attributes to reorder hooks in these modules and + * test that the ordering attributes produce the expected hook call order. */ #[ReorderHook('test_hook', CHooks::class, 'testHookReorderFirst', Order::First)] #[RemoveHook('test_hook', CHooks::class, 'testHookRemoved')] -- GitLab From 3ce46c876363546a92f4d36b4b4e725657da6ab9 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 15 Apr 2025 21:17:14 -0400 Subject: [PATCH 263/268] Move and rename hook collector test modules --- .../aaa_hook_collector_test.info.yml} | 0 .../src/Hook/TestHookAfter.php | 4 +- .../src/Hook/TestHookBefore.php | 2 +- .../src/Hook/TestHookFirst.php | 2 +- .../src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderExtraTypes.php | 4 +- .../src/Hook/TestHookReorderHookFirst.php | 4 +- .../bbb_hook_collector_test.info.yml} | 0 .../src/Hook/TestHookAfter.php | 2 +- .../src/Hook/TestHookBefore.php | 4 +- .../src/Hook/TestHookFirst.php | 2 +- .../src/Hook/TestHookLast.php | 2 +- .../src/Hook/TestHookOrderExtraTypes.php | 2 +- .../src/Hook/TestHookReorderHookLast.php | 4 +- .../Core/Hook/HookCollectorPassTest.php | 48 +++++++++---------- 15 files changed, 41 insertions(+), 41 deletions(-) rename core/modules/system/tests/modules/{HookOrder/fff_hook_test/fff_hook_test.info.yml => HookCollector/aaa_hook_collector_test/aaa_hook_collector_test.info.yml} (100%) rename core/modules/system/tests/modules/{HookOrder/fff_hook_test => HookCollector/aaa_hook_collector_test}/src/Hook/TestHookAfter.php (91%) rename core/modules/system/tests/modules/{HookOrder/fff_hook_test => HookCollector/aaa_hook_collector_test}/src/Hook/TestHookBefore.php (94%) rename core/modules/system/tests/modules/{HookOrder/fff_hook_test => HookCollector/aaa_hook_collector_test}/src/Hook/TestHookFirst.php (94%) rename core/modules/system/tests/modules/{HookOrder/fff_hook_test => HookCollector/aaa_hook_collector_test}/src/Hook/TestHookLast.php (94%) rename core/modules/system/tests/modules/{HookOrder/fff_hook_test => HookCollector/aaa_hook_collector_test}/src/Hook/TestHookOrderExtraTypes.php (90%) rename core/modules/system/tests/modules/{HookOrder/fff_hook_test => HookCollector/aaa_hook_collector_test}/src/Hook/TestHookReorderHookFirst.php (91%) rename core/modules/system/tests/modules/{HookOrder/ggg_hook_test/ggg_hook_test.info.yml => HookCollector/bbb_hook_collector_test/bbb_hook_collector_test.info.yml} (100%) rename core/modules/system/tests/modules/{HookOrder/ggg_hook_test => HookCollector/bbb_hook_collector_test}/src/Hook/TestHookAfter.php (94%) rename core/modules/system/tests/modules/{HookOrder/ggg_hook_test => HookCollector/bbb_hook_collector_test}/src/Hook/TestHookBefore.php (91%) rename core/modules/system/tests/modules/{HookOrder/ggg_hook_test => HookCollector/bbb_hook_collector_test}/src/Hook/TestHookFirst.php (94%) rename core/modules/system/tests/modules/{HookOrder/ggg_hook_test => HookCollector/bbb_hook_collector_test}/src/Hook/TestHookLast.php (93%) rename core/modules/system/tests/modules/{HookOrder/ggg_hook_test => HookCollector/bbb_hook_collector_test}/src/Hook/TestHookOrderExtraTypes.php (94%) rename core/modules/system/tests/modules/{HookOrder/ggg_hook_test => HookCollector/bbb_hook_collector_test}/src/Hook/TestHookReorderHookLast.php (89%) diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/fff_hook_test.info.yml b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/aaa_hook_collector_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/fff_hook_test.info.yml rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/aaa_hook_collector_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php similarity index 91% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookAfter.php rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php index c91b1bfb4e31..9eeb2b418fcc 100644 --- a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\fff_hook_test\Hook; +namespace Drupal\aaa_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; @@ -24,7 +24,7 @@ class TestHookAfter { /** * This pair tests OrderAfter. */ - #[Hook('custom_hook_test_hook_after', order: new OrderAfter(['ggg_hook_test']))] + #[Hook('custom_hook_test_hook_after', order: new OrderAfter(['bbb_hook_collector_test']))] public function hookAfter(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php similarity index 94% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookBefore.php rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php index f2205df4c668..91c9207c7164 100644 --- a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\fff_hook_test\Hook; +namespace Drupal\aaa_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php similarity index 94% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookFirst.php rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php index 3b1b53bbb835..75069d46010e 100644 --- a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\fff_hook_test\Hook; +namespace Drupal\aaa_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php similarity index 94% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookLast.php rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php index 4b31f5fb553c..cf465f3ad09e 100644 --- a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\fff_hook_test\Hook; +namespace Drupal\aaa_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php similarity index 90% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookOrderExtraTypes.php rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php index 69431ceb0bf8..229c0f216b4c 100644 --- a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\fff_hook_test\Hook; +namespace Drupal\aaa_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; @@ -26,7 +26,7 @@ class TestHookOrderExtraTypes { */ #[Hook('custom_hook_extra_types1_alter', order: new OrderAfter( - modules: ['ggg_hook_test'], + modules: ['bbb_hook_collector_test'], ) )] public function customHookExtraTypes(array &$calls): void { diff --git a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReorderHookFirst.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php similarity index 91% rename from core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReorderHookFirst.php rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php index 3f65f6dfb38a..6a444f9b5bb7 100644 --- a/core/modules/system/tests/modules/HookOrder/fff_hook_test/src/Hook/TestHookReorderHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Drupal\fff_hook_test\Hook; +namespace Drupal\aaa_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; use Drupal\Core\Hook\Attribute\ReorderHook; -use Drupal\ggg_hook_test\Hook\TestHookReorderHookLast; +use Drupal\bbb_hook_collector_test\Hook\TestHookReorderHookLast; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/ggg_hook_test.info.yml b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/bbb_hook_collector_test.info.yml similarity index 100% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/ggg_hook_test.info.yml rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/bbb_hook_collector_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php similarity index 94% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookAfter.php rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php index d78ed1b88911..65ee1f6b339c 100644 --- a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ggg_hook_test\Hook; +namespace Drupal\bbb_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php similarity index 91% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookBefore.php rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php index 22fec07649ea..fc0225a8b9d9 100644 --- a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ggg_hook_test\Hook; +namespace Drupal\bbb_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderBefore; @@ -24,7 +24,7 @@ class TestHookBefore { /** * This pair tests OrderBefore. */ - #[Hook('custom_hook_test_hook_before', order: new OrderBefore(['fff_hook_test']))] + #[Hook('custom_hook_test_hook_before', order: new OrderBefore(['aaa_hook_collector_test']))] public function hookBefore(): string { return __METHOD__; } diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php similarity index 94% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookFirst.php rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php index 078a643df4ec..f8fae77a04c4 100644 --- a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ggg_hook_test\Hook; +namespace Drupal\bbb_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php similarity index 93% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookLast.php rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php index a40bfa4ab42a..35024643475c 100644 --- a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ggg_hook_test\Hook; +namespace Drupal\bbb_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php similarity index 94% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookOrderExtraTypes.php rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php index 299549153501..7b2af7a0ffd9 100644 --- a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ggg_hook_test\Hook; +namespace Drupal\bbb_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReorderHookLast.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php similarity index 89% rename from core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReorderHookLast.php rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php index 479f88b91311..82c614f42b93 100644 --- a/core/modules/system/tests/modules/HookOrder/ggg_hook_test/src/Hook/TestHookReorderHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ggg_hook_test\Hook; +namespace Drupal\bbb_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\Order; @@ -28,7 +28,7 @@ class TestHookReorderHookLast { public function customHookOverride(): string { // This normally would run second. // We override that order here with Order::First. - // We override, that order in fff_hook_test with + // We override, that order in aaa_hook_collector_test with // ReorderHook. return __METHOD__; } diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index 5e0a0332f4d8..dec0475d6e79 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -160,14 +160,14 @@ public function testHookAttribute(): void { */ public function testHookFirst(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['fff_hook_test']); - $module_installer->install(['ggg_hook_test']); + $module_installer->install(['aaa_hook_collector_test']); + $module_installer->install(['bbb_hook_collector_test']); $module_handler = $this->container->get('module_handler'); // Last alphabetically uses the Order::First enum to place it before // the implementation it would naturally come after. $expected_calls = [ - 'Drupal\ggg_hook_test\Hook\TestHookFirst::hookFirst', - 'Drupal\fff_hook_test\Hook\TestHookFirst::hookFirst', + 'Drupal\bbb_hook_collector_test\Hook\TestHookFirst::hookFirst', + 'Drupal\aaa_hook_collector_test\Hook\TestHookFirst::hookFirst', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_first'); $this->assertEquals($expected_calls, $calls); @@ -178,14 +178,14 @@ public function testHookFirst(): void { */ public function testHookAfter(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['fff_hook_test']); - $module_installer->install(['ggg_hook_test']); + $module_installer->install(['aaa_hook_collector_test']); + $module_installer->install(['bbb_hook_collector_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderAfter to place it after // the implementation it would naturally come before. $expected_calls = [ - 'Drupal\ggg_hook_test\Hook\TestHookAfter::hookAfter', - 'Drupal\fff_hook_test\Hook\TestHookAfter::hookAfter', + 'Drupal\bbb_hook_collector_test\Hook\TestHookAfter::hookAfter', + 'Drupal\aaa_hook_collector_test\Hook\TestHookAfter::hookAfter', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_after'); $this->assertEquals($expected_calls, $calls); @@ -214,14 +214,14 @@ public function testHookAfterClassMethod(): void { */ public function testHookBefore(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['fff_hook_test']); - $module_installer->install(['ggg_hook_test']); + $module_installer->install(['aaa_hook_collector_test']); + $module_installer->install(['bbb_hook_collector_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderBefore to place it before // the implementation it would naturally come after. $expected_calls = [ - 'Drupal\ggg_hook_test\Hook\TestHookBefore::hookBefore', - 'Drupal\fff_hook_test\Hook\TestHookBefore::hookBefore', + 'Drupal\bbb_hook_collector_test\Hook\TestHookBefore::hookBefore', + 'Drupal\aaa_hook_collector_test\Hook\TestHookBefore::hookBefore', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_before'); $this->assertEquals($expected_calls, $calls); @@ -232,14 +232,14 @@ public function testHookBefore(): void { */ public function testHookOrderExtraTypes(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['fff_hook_test']); - $module_installer->install(['ggg_hook_test']); + $module_installer->install(['aaa_hook_collector_test']); + $module_installer->install(['bbb_hook_collector_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderAfter to place it after // the implementation it would naturally come before. $expected_calls = [ - 'Drupal\ggg_hook_test\Hook\TestHookOrderExtraTypes::customHookExtraTypes', - 'Drupal\fff_hook_test\Hook\TestHookOrderExtraTypes::customHookExtraTypes', + 'Drupal\bbb_hook_collector_test\Hook\TestHookOrderExtraTypes::customHookExtraTypes', + 'Drupal\aaa_hook_collector_test\Hook\TestHookOrderExtraTypes::customHookExtraTypes', ]; $hooks = [ 'custom_hook', @@ -256,14 +256,14 @@ public function testHookOrderExtraTypes(): void { */ public function testHookLast(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['fff_hook_test']); - $module_installer->install(['ggg_hook_test']); + $module_installer->install(['aaa_hook_collector_test']); + $module_installer->install(['bbb_hook_collector_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderBefore to place it before // the implementation it would naturally come after. $expected_calls = [ - 'Drupal\ggg_hook_test\Hook\TestHookLast::hookLast', - 'Drupal\fff_hook_test\Hook\TestHookLast::hookLast', + 'Drupal\bbb_hook_collector_test\Hook\TestHookLast::hookLast', + 'Drupal\aaa_hook_collector_test\Hook\TestHookLast::hookLast', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_last'); $this->assertEquals($expected_calls, $calls); @@ -290,12 +290,12 @@ public function testHookRemove(): void { */ public function testHookOverride(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['fff_hook_test']); - $module_installer->install(['ggg_hook_test']); + $module_installer->install(['aaa_hook_collector_test']); + $module_installer->install(['bbb_hook_collector_test']); $module_handler = $this->container->get('module_handler'); $expected_calls = [ - 'Drupal\fff_hook_test\Hook\TestHookReorderHookFirst::customHookOverride', - 'Drupal\ggg_hook_test\Hook\TestHookReorderHookLast::customHookOverride', + 'Drupal\aaa_hook_collector_test\Hook\TestHookReorderHookFirst::customHookOverride', + 'Drupal\bbb_hook_collector_test\Hook\TestHookReorderHookLast::customHookOverride', ]; $calls = $module_handler->invokeAll('custom_hook_override'); $this->assertEquals($expected_calls, $calls); -- GitLab From 21d234208c716489eb2456cd8ed2826d0cd460c8 Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 15 Apr 2025 21:21:26 -0400 Subject: [PATCH 264/268] Consolidate collector tests Fix HCP test --- .../src/Hook/TestHookAfterClassMethod.php | 4 ++-- .../src/Hook/TestHookAfterClassMethod.php | 2 +- .../HookOrder/hhh_hook_test/hhh_hook_test.info.yml | 7 ------- .../HookOrder/iii_hook_test/iii_hook_test.info.yml | 7 ------- .../KernelTests/Core/Hook/HookCollectorPassTest.php | 8 ++++---- 5 files changed, 7 insertions(+), 21 deletions(-) rename core/modules/system/tests/modules/{HookOrder/hhh_hook_test => HookCollector/aaa_hook_collector_test}/src/Hook/TestHookAfterClassMethod.php (86%) rename core/modules/system/tests/modules/{HookOrder/iii_hook_test => HookCollector/bbb_hook_collector_test}/src/Hook/TestHookAfterClassMethod.php (94%) delete mode 100644 core/modules/system/tests/modules/HookOrder/hhh_hook_test/hhh_hook_test.info.yml delete mode 100644 core/modules/system/tests/modules/HookOrder/iii_hook_test/iii_hook_test.info.yml diff --git a/core/modules/system/tests/modules/HookOrder/hhh_hook_test/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php similarity index 86% rename from core/modules/system/tests/modules/HookOrder/hhh_hook_test/src/Hook/TestHookAfterClassMethod.php rename to core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php index b081b9d6a610..56dbadaf8494 100644 --- a/core/modules/system/tests/modules/HookOrder/hhh_hook_test/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Drupal\hhh_hook_test\Hook; +namespace Drupal\aaa_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; use Drupal\Core\Hook\Order\OrderAfter; -use Drupal\iii_hook_test\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; +use Drupal\bbb_hook_collector_test\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; /** * Hook implementations for verifying ordering hooks by attributes. diff --git a/core/modules/system/tests/modules/HookOrder/iii_hook_test/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php similarity index 94% rename from core/modules/system/tests/modules/HookOrder/iii_hook_test/src/Hook/TestHookAfterClassMethod.php rename to core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php index 02b9cb15f61a..9d49ec68de41 100644 --- a/core/modules/system/tests/modules/HookOrder/iii_hook_test/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\iii_hook_test\Hook; +namespace Drupal\bbb_hook_collector_test\Hook; use Drupal\Core\Hook\Attribute\Hook; diff --git a/core/modules/system/tests/modules/HookOrder/hhh_hook_test/hhh_hook_test.info.yml b/core/modules/system/tests/modules/HookOrder/hhh_hook_test/hhh_hook_test.info.yml deleted file mode 100644 index bd8dc0b2f73d..000000000000 --- a/core/modules/system/tests/modules/HookOrder/hhh_hook_test/hhh_hook_test.info.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: first alphabetically -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -core_version_requirement: '*' -hidden: true diff --git a/core/modules/system/tests/modules/HookOrder/iii_hook_test/iii_hook_test.info.yml b/core/modules/system/tests/modules/HookOrder/iii_hook_test/iii_hook_test.info.yml deleted file mode 100644 index aa08f259dc2e..000000000000 --- a/core/modules/system/tests/modules/HookOrder/iii_hook_test/iii_hook_test.info.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: Hook ordering last -type: module -description: 'Test module used to test hook ordering.' -package: Testing -version: VERSION -core_version_requirement: '*' -hidden: true diff --git a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php index dec0475d6e79..774f1289ccdb 100644 --- a/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php +++ b/core/tests/Drupal/KernelTests/Core/Hook/HookCollectorPassTest.php @@ -196,14 +196,14 @@ public function testHookAfter(): void { */ public function testHookAfterClassMethod(): void { $module_installer = $this->container->get('module_installer'); - $module_installer->install(['hhh_hook_test']); - $module_installer->install(['iii_hook_test']); + $module_installer->install(['aaa_hook_collector_test']); + $module_installer->install(['bbb_hook_collector_test']); $module_handler = $this->container->get('module_handler'); // First alphabetically uses the OrderAfter to place it after // the implementation it would naturally come before using call and method. $expected_calls = [ - 'Drupal\iii_hook_test\Hook\TestHookAfterClassMethod::hookAfterClassMethod', - 'Drupal\hhh_hook_test\Hook\TestHookAfterClassMethod::hookAfterClassMethod', + 'Drupal\bbb_hook_collector_test\Hook\TestHookAfterClassMethod::hookAfterClassMethod', + 'Drupal\aaa_hook_collector_test\Hook\TestHookAfterClassMethod::hookAfterClassMethod', ]; $calls = $module_handler->invokeAll('custom_hook_test_hook_after_class_method'); $this->assertEquals($expected_calls, $calls); -- GitLab From 01a060c8938b1a7ff8c7589a8354dff42e45dfaa Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 15 Apr 2025 21:37:26 -0400 Subject: [PATCH 265/268] Standardize comment --- .../src/Hook/TestHookAfter.php | 13 ++++++------- .../src/Hook/TestHookAfterClassMethod.php | 13 ++++++------- .../src/Hook/TestHookBefore.php | 13 ++++++------- .../src/Hook/TestHookFirst.php | 13 ++++++------- .../src/Hook/TestHookLast.php | 13 ++++++------- .../src/Hook/TestHookOrderExtraTypes.php | 13 ++++++------- .../src/Hook/TestHookReorderHookFirst.php | 13 ++++++------- .../src/Hook/TestHookAfter.php | 13 ++++++------- .../src/Hook/TestHookAfterClassMethod.php | 13 ++++++------- .../src/Hook/TestHookBefore.php | 13 ++++++------- .../src/Hook/TestHookFirst.php | 13 ++++++------- .../src/Hook/TestHookLast.php | 13 ++++++------- .../src/Hook/TestHookOrderExtraTypes.php | 13 ++++++------- .../src/Hook/TestHookReorderHookLast.php | 13 ++++++------- .../aaa_hook_order_test/src/Hook/AAlterHooks.php | 10 ++++++++++ .../aaa_hook_order_test/src/Hook/AHooks.php | 12 +++++++++--- .../bbb_hook_order_test/src/Hook/BAlterHooks.php | 10 ++++++++++ .../bbb_hook_order_test/src/Hook/BHooks.php | 12 +++++++++--- .../ccc_hook_order_test/src/Hook/CAlterHooks.php | 12 +++++++++++- .../ccc_hook_order_test/src/Hook/CHooks.php | 12 +++++++++--- .../ddd_hook_order_test/src/Hook/DAlterHooks.php | 10 ++++++++++ .../ddd_hook_order_test/src/Hook/DHooks.php | 12 +++++++++--- 22 files changed, 161 insertions(+), 111 deletions(-) diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php index 9eeb2b418fcc..8acd2b59b0e8 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php @@ -10,14 +10,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookAfter { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php index 56dbadaf8494..3407b3d00eff 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php @@ -11,14 +11,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookAfterClassMethod { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php index 91c9207c7164..35b701cb450b 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php @@ -9,14 +9,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookBefore { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php index 75069d46010e..137bc93c28d4 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php @@ -9,14 +9,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookFirst { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php index cf465f3ad09e..92a22c0a380f 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php @@ -10,14 +10,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookLast { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php index 229c0f216b4c..69394e413612 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php @@ -10,14 +10,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookOrderExtraTypes { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php index 6a444f9b5bb7..2760b979b148 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php @@ -12,14 +12,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookReorderHookFirst { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php index 65ee1f6b339c..c1431f78e156 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php @@ -9,14 +9,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookAfter { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php index 9d49ec68de41..6b4d5ba6ae47 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php @@ -9,14 +9,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookAfterClassMethod { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php index fc0225a8b9d9..2641be3ace08 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php @@ -10,14 +10,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookBefore { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php index f8fae77a04c4..4c956b68c100 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php @@ -10,14 +10,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookFirst { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php index 35024643475c..c74a44b93727 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php @@ -9,14 +9,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookLast { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php index 7b2af7a0ffd9..7b5475d89f16 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php @@ -9,14 +9,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookOrderExtraTypes { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php index 82c614f42b93..4029df0a01cb 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php @@ -10,14 +10,13 @@ /** * Hook implementations for verifying ordering hooks by attributes. * - * We must ensure that the order of the modules is expected and then change - * the order that the hooks are run in order to verify. This module - * comes in a pair first alphabetically and last alphabetically. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. * - * In the normal order a hook implemented by first alphabetically would run - * before the same hook in last alphabetically. - * - * Each method pair tests one hook ordering permutation. + * The default execution order by module would be: + * - aaa_hook_collector_test + * - bbb_hook_collector_test */ class TestHookReorderHookLast { diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php index 3c6384897e76..d4f94a22571a 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php @@ -9,6 +9,16 @@ /** * Hooks for testing ordering. + * + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ class AAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php index 3dc0b707aeae..bd2e955772ac 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php @@ -11,9 +11,15 @@ /** * Hooks for testing ordering. * - * The hook order test modules are named alphabetically so we have a known - * default order. We then use attributes to reorder hooks in these modules and - * test that the ordering attributes produce the expected hook call order. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ class AHooks { diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php index 3e66ed2ca5d3..e71b38cafe0a 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php @@ -8,6 +8,16 @@ /** * Hooks for testing ordering. + * + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ class BAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php index 9a736719e8ce..b69c673f7479 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php @@ -9,9 +9,15 @@ /** * Hooks for testing ordering. * - * The hook order test modules are named alphabetically so we have a known - * default order. We then use attributes to reorder hooks in these modules and - * test that the ordering attributes produce the expected hook call order. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ class BHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php index 451c14eae54e..3e82940fc6d5 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php @@ -2,12 +2,22 @@ declare(strict_types=1); -namespace Drupal\ccc_hook_order_test\Hook; +namespace Drupal\ccc_hook_test\Hook; use Drupal\Core\Hook\Attribute\Hook; /** * Hooks for testing ordering. + * + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ class CAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php index 8a304f1b0e1d..b1471dcf8e5f 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php @@ -10,9 +10,15 @@ /** * Hooks for testing ordering. * - * The hook order test modules are named alphabetically so we have a known - * default order. We then use attributes to reorder hooks in these modules and - * test that the ordering attributes produce the expected hook call order. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ class CHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php index b1745f427c97..38a3209f2c55 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php @@ -8,6 +8,16 @@ /** * Hooks for testing ordering. + * + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ class DAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php index ee6a4741a365..b4d75a3ea23d 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php @@ -13,9 +13,15 @@ /** * Hooks for testing ordering. * - * The hook order test modules are named alphabetically so we have a known - * default order. We then use attributes to reorder hooks in these modules and - * test that the ordering attributes produce the expected hook call order. + * We must ensure the execution order of hook implementations is expected. We + * Then change that order using attributes and verify the new order of + * execution. This module comes in an alphabetical set. + * + * The default execution order by module would be: + * - aaa_hook_order_test + * - bbb_hook_order_test + * - ccc_hook_order_test + * - ddd_hook_order_test */ #[ReorderHook('test_hook', CHooks::class, 'testHookReorderFirst', Order::First)] #[RemoveHook('test_hook', CHooks::class, 'testHookRemoved')] -- GitLab From 80e055d41a860e884849c39f0c730bfd64713cdd Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 15 Apr 2025 21:53:30 -0400 Subject: [PATCH 266/268] Namespace --- .../HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php index 3e82940fc6d5..771d8b0fa579 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\ccc_hook_test\Hook; +namespace Drupal\ccc_hook_order_test\Hook; use Drupal\Core\Hook\Attribute\Hook; -- GitLab From 59c074e5312d7c56d03b0fd138bd9e79e45bd23e Mon Sep 17 00:00:00 2001 From: nicxvan <nic@nlightened.net> Date: Tue, 15 Apr 2025 22:51:56 -0400 Subject: [PATCH 267/268] Match module names in info --- .../aaa_hook_collector_test/aaa_hook_collector_test.info.yml | 2 +- .../bbb_hook_collector_test/bbb_hook_collector_test.info.yml | 2 +- .../HookOrder/aaa_hook_order_test/aaa_hook_order_test.info.yml | 2 +- .../HookOrder/bbb_hook_order_test/bbb_hook_order_test.info.yml | 2 +- .../HookOrder/ccc_hook_order_test/ccc_hook_order_test.info.yml | 2 +- .../HookOrder/ddd_hook_order_test/ddd_hook_order_test.info.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/aaa_hook_collector_test.info.yml b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/aaa_hook_collector_test.info.yml index bd8dc0b2f73d..0ebae91b6735 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/aaa_hook_collector_test.info.yml +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/aaa_hook_collector_test.info.yml @@ -1,4 +1,4 @@ -name: first alphabetically +name: AAA Hook collector test type: module description: 'Test module used to test hook ordering.' package: Testing diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/bbb_hook_collector_test.info.yml b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/bbb_hook_collector_test.info.yml index aa08f259dc2e..a084845fb14f 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/bbb_hook_collector_test.info.yml +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/bbb_hook_collector_test.info.yml @@ -1,4 +1,4 @@ -name: Hook ordering last +name: BBB Hook collector test type: module description: 'Test module used to test hook ordering.' package: Testing diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.info.yml b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.info.yml index a85c8b96fee7..0cc98c51c0b6 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.info.yml +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/aaa_hook_order_test.info.yml @@ -1,4 +1,4 @@ -name: Hook A Test +name: AAA Hook order test type: module description: 'Test module used to test hook ordering.' package: Testing diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.info.yml b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.info.yml index c286f011bc7b..4b0667172e64 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.info.yml +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/bbb_hook_order_test.info.yml @@ -1,4 +1,4 @@ -name: Hook B Test +name: BBB Hook order test type: module description: 'Test module used to test hook ordering.' package: Testing diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.info.yml b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.info.yml index 02f09d78faea..d20a7a36ab1f 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.info.yml +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/ccc_hook_order_test.info.yml @@ -1,4 +1,4 @@ -name: Hook C Test +name: CCC Hook order test type: module description: 'Test module used to test hook ordering.' package: Testing diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.info.yml b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.info.yml index 1d8c2e86d63d..df2c987a6672 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.info.yml +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/ddd_hook_order_test.info.yml @@ -1,4 +1,4 @@ -name: Hook D Test +name: DDD Hook order test type: module description: 'Test module used to test hook ordering.' package: Testing -- GitLab From 71dec77222ed928ffa00dd6038fcea45b6661aab Mon Sep 17 00:00:00 2001 From: nlighteneddesign <nic@nlighteneddevelopment.com> Date: Thu, 17 Apr 2025 09:43:18 -0400 Subject: [PATCH 268/268] Simplify comments --- .../src/Hook/TestHookAfter.php | 12 ++++-------- .../src/Hook/TestHookAfterClassMethod.php | 12 ++++-------- .../src/Hook/TestHookBefore.php | 12 ++++-------- .../src/Hook/TestHookFirst.php | 12 ++++-------- .../src/Hook/TestHookLast.php | 12 ++++-------- .../src/Hook/TestHookOrderExtraTypes.php | 12 ++++-------- .../src/Hook/TestHookReorderHookFirst.php | 12 ++++-------- .../src/Hook/TestHookAfter.php | 12 ++++-------- .../src/Hook/TestHookAfterClassMethod.php | 12 ++++-------- .../src/Hook/TestHookBefore.php | 12 ++++-------- .../src/Hook/TestHookFirst.php | 12 ++++-------- .../src/Hook/TestHookLast.php | 12 ++++-------- .../src/Hook/TestHookOrderExtraTypes.php | 12 ++++-------- .../src/Hook/TestHookReorderHookLast.php | 12 ++++-------- .../aaa_hook_order_test/src/Hook/AAlterHooks.php | 14 ++++---------- .../aaa_hook_order_test/src/Hook/AHooks.php | 14 ++++---------- .../bbb_hook_order_test/src/Hook/BAlterHooks.php | 14 ++++---------- .../bbb_hook_order_test/src/Hook/BHooks.php | 14 ++++---------- .../ccc_hook_order_test/src/Hook/CAlterHooks.php | 14 ++++---------- .../ccc_hook_order_test/src/Hook/CHooks.php | 14 ++++---------- .../ddd_hook_order_test/src/Hook/DAlterHooks.php | 13 +++---------- .../ddd_hook_order_test/src/Hook/DHooks.php | 13 +++---------- 22 files changed, 86 insertions(+), 192 deletions(-) diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php index 8acd2b59b0e8..bf2ae7db46d3 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfter.php @@ -8,15 +8,11 @@ use Drupal\Core\Hook\Order\OrderAfter; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookAfter { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php index 3407b3d00eff..e3861fe3516b 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookAfterClassMethod.php @@ -9,15 +9,11 @@ use Drupal\bbb_hook_collector_test\Hook\TestHookAfterClassMethod as TestHookAfterClassMethodForAfter; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookAfterClassMethod { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php index 35b701cb450b..76661c5297d4 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookBefore.php @@ -7,15 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookBefore { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php index 137bc93c28d4..e3ce6b86963a 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookFirst.php @@ -7,15 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookFirst { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php index 92a22c0a380f..427422b378f5 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookLast.php @@ -8,15 +8,11 @@ use Drupal\Core\Hook\Order\Order; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookLast { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php index 69394e413612..eb2c35472f03 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php @@ -8,15 +8,11 @@ use Drupal\Core\Hook\Order\OrderAfter; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookOrderExtraTypes { diff --git a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php index 2760b979b148..55e12aad240f 100644 --- a/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/aaa_hook_collector_test/src/Hook/TestHookReorderHookFirst.php @@ -10,15 +10,11 @@ use Drupal\bbb_hook_collector_test\Hook\TestHookReorderHookLast; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookReorderHookFirst { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php index c1431f78e156..89a6f3a1b05b 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfter.php @@ -7,15 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookAfter { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php index 6b4d5ba6ae47..1cedcdcaa66c 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookAfterClassMethod.php @@ -7,15 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookAfterClassMethod { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php index 2641be3ace08..12341cd27d21 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookBefore.php @@ -8,15 +8,11 @@ use Drupal\Core\Hook\Order\OrderBefore; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookBefore { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php index 4c956b68c100..ac72de1da804 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookFirst.php @@ -8,15 +8,11 @@ use Drupal\Core\Hook\Order\Order; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookFirst { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php index c74a44b93727..6b30344f583d 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookLast.php @@ -7,15 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookLast { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php index 7b5475d89f16..a32e3529ec6f 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookOrderExtraTypes.php @@ -7,15 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookOrderExtraTypes { diff --git a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php index 4029df0a01cb..3b89394347a8 100644 --- a/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php +++ b/core/modules/system/tests/modules/HookCollector/bbb_hook_collector_test/src/Hook/TestHookReorderHookLast.php @@ -8,15 +8,11 @@ use Drupal\Core\Hook\Order\Order; /** - * Hook implementations for verifying ordering hooks by attributes. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_collector_test - * - bbb_hook_collector_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class TestHookReorderHookLast { diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php index d4f94a22571a..f9e124e217b2 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AAlterHooks.php @@ -8,17 +8,11 @@ use Drupal\Core\Hook\Order\OrderAfter; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class AAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php index bd2e955772ac..234264cbbe55 100644 --- a/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php +++ b/core/modules/system/tests/modules/HookOrder/aaa_hook_order_test/src/Hook/AHooks.php @@ -9,17 +9,11 @@ use Drupal\Core\Hook\Order\OrderAfter; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class AHooks { diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php index e71b38cafe0a..0be826094ebf 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BAlterHooks.php @@ -7,17 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class BAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php index b69c673f7479..a93643e45e4b 100644 --- a/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php +++ b/core/modules/system/tests/modules/HookOrder/bbb_hook_order_test/src/Hook/BHooks.php @@ -7,17 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class BHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php index 771d8b0fa579..f005daa0a38b 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CAlterHooks.php @@ -7,17 +7,11 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class CAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php index b1471dcf8e5f..b3571d490832 100644 --- a/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ccc_hook_order_test/src/Hook/CHooks.php @@ -8,17 +8,11 @@ use Drupal\Core\Hook\Order\Order; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. Some of the implementations are reordered + * using order attributes. */ class CHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php index 38a3209f2c55..7da7d91002cd 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DAlterHooks.php @@ -7,17 +7,10 @@ use Drupal\Core\Hook\Attribute\Hook; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. */ class DAlterHooks { diff --git a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php index b4d75a3ea23d..053228642d64 100644 --- a/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php +++ b/core/modules/system/tests/modules/HookOrder/ddd_hook_order_test/src/Hook/DHooks.php @@ -11,17 +11,10 @@ use Drupal\ccc_hook_order_test\Hook\CHooks; /** - * Hooks for testing ordering. + * This class contains hook implementations. * - * We must ensure the execution order of hook implementations is expected. We - * Then change that order using attributes and verify the new order of - * execution. This module comes in an alphabetical set. - * - * The default execution order by module would be: - * - aaa_hook_order_test - * - bbb_hook_order_test - * - ccc_hook_order_test - * - ddd_hook_order_test + * By default, these will be called in module order, which is predictable due + * to the alphabetical module names. */ #[ReorderHook('test_hook', CHooks::class, 'testHookReorderFirst', Order::First)] #[RemoveHook('test_hook', CHooks::class, 'testHookRemoved')] -- GitLab