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