From 862b581d3ad1bf9e21520800fc24c04b91eb4ffd Mon Sep 17 00:00:00 2001 From: solideogloria <39064-solideogloria@users.noreply.drupalcode.org> Date: Fri, 8 Mar 2024 09:11:09 -0600 Subject: [PATCH] Add changes from patch 27. --- .../Drupal/Core/Extension/ModuleHandler.php | 17 +++++++++++++ .../Core/Extension/ModuleHandlerTest.php | 24 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 7b72f3260497..88b62147cdac 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -636,6 +636,12 @@ protected function getImplementationInfo($hook) { protected function buildImplementationInfo($hook) { $implementations = []; $hook_info = $this->getHookInfo(); + + // In case it's an early stage and the hook info was loaded from cache, the + // module files are not yet loaded at this point and discovery below + // produces an empty list. + $this->loadAll(); + foreach ($this->moduleList as $module => $extension) { $include_file = isset($hook_info[$hook]['group']) && $this->loadInclude($module, 'inc', $module . '.' . $hook_info[$hook]['group']); // Since $this->implementsHook() may needlessly try to load the include @@ -685,6 +691,17 @@ protected function buildImplementationInfo($hook) { * from the cache. */ protected function verifyImplementations(&$implementations, $hook) { + // Under certain circumstances the module files are not yet loaded. This + // happens for example when invoking a hook inside the constructor of a + // http_middleware service; these services are constructed very early as + // a dependency of http_kernel service. A more concrete example is a + // middleware service using the entity_type.manager. Most of the times + // the entity type information is retrieved from cache (stored in the + // discovery cache bin). When this cache however is missing, hooks + // like hook_entity_type_build() and hook_entity_type_alter() need to be + // invoked at this early stage. + $this->loadAll(); + $all_valid = TRUE; foreach ($implementations as $module => $group) { // If this hook implementation is stored in a lazy-loaded file, include diff --git a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php index 6485ecb1f97b..a13296c5320c 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ModuleHandlerTest.php @@ -394,6 +394,7 @@ function (callable $hook, string $module) use (&$implementors) { * Tests getImplementations. * * @covers ::invokeAllWith + * @covers ::verifyImplementations */ public function testCachedGetImplementationsMissingMethod(): void { $this->cacheBackend->expects($this->exactly(1)) @@ -420,7 +421,6 @@ public function testCachedGetImplementationsMissingMethod(): void { ]) ->onlyMethods(['buildImplementationInfo']) ->getMock(); - $module_handler->load('module_handler_test'); $module_handler->expects($this->never())->method('buildImplementationInfo'); $implementors = []; @@ -536,4 +536,26 @@ public function testGetModuleDirectories(): void { $this->assertEquals(['node' => $this->root . '/core/modules/node'], $module_handler->getModuleDirectories()); } + /** + * Tests that modules are included in case of a partial cache miss. + * + * @covers ::hasImplementations + * @covers ::getImplementationInfo + * @covers ::buildImplementationInfo + */ + public function testMissingHookImplementationCache() { + // Simulate missing cache entry for hook implementations, but existing one + // for hook info. + $this->cacheBackend + ->expects($this->exactly(2)) + ->method('get') + ->willReturnMap([ + ['hook_info', FALSE, (object) ['data' => []]], + ['module_implements', FALSE, FALSE], + ]); + + $module_handler = $this->getModuleHandler(); + $this->assertTrue($module_handler->hasImplementations('hook', 'module_handler_test')); + } + } -- GitLab