diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php index 0f5eded9db9ac300bba1038f34f34a02426d989c..87924987c2c6ca0c3b0ccdb141ad0746840ab415 100644 --- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php +++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php @@ -505,34 +505,30 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { * The name of the module for which to remove all registered cache bins. */ protected function removeCacheBins($module) { - // Remove any cache bins defined by a module. $service_yaml_file = drupal_get_path('module', $module) . "/$module.services.yml"; - if (file_exists($service_yaml_file)) { - $definitions = Yaml::decode(file_get_contents($service_yaml_file)); - if (isset($definitions['services'])) { - foreach ($definitions['services'] as $id => $definition) { - if (isset($definition['tags'])) { - foreach ($definition['tags'] as $tag) { - // This works for the default cache registration and even in some - // cases when a non-default "super" factory is used. That should - // be extremely rare. - if ($tag['name'] == 'cache.bin' && isset($definition['factory_service']) && isset($definition['factory_method']) && !empty($definition['arguments'])) { - try { - $factory = \Drupal::service($definition['factory_service']); - if (method_exists($factory, $definition['factory_method'])) { - $backend = call_user_func_array([$factory, $definition['factory_method']], $definition['arguments']); - if ($backend instanceof CacheBackendInterface) { - $backend->removeBin(); - } - } - } - catch (\Exception $e) { - watchdog_exception('system', $e, 'Failed to remove cache bin defined by the service %id.', ['%id' => $id]); - } - } - } + if (!file_exists($service_yaml_file)) { + return; + } + + $definitions = Yaml::decode(file_get_contents($service_yaml_file)); + + $cache_bin_services = array_filter( + isset($definitions['services']) ? $definitions['services'] : [], + function ($definition) { + $tags = isset($definition['tags']) ? $definition['tags'] : []; + foreach ($tags as $tag) { + if (isset($tag['name']) && ($tag['name'] == 'cache.bin')) { + return TRUE; } } + return FALSE; + } + ); + + foreach (array_keys($cache_bin_services) as $service_id) { + $backend = $this->kernel->getContainer()->get($service_id); + if ($backend instanceof CacheBackendInterface) { + $backend->removeBin(); } } } diff --git a/core/modules/system/tests/modules/module_cachebin/module_cachebin.info.yml b/core/modules/system/tests/modules/module_cachebin/module_cachebin.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..d255795c52f408c000810de70e7db7f7f1c796e8 --- /dev/null +++ b/core/modules/system/tests/modules/module_cachebin/module_cachebin.info.yml @@ -0,0 +1,7 @@ +name: module cache bin tests +type: module +description: Test cache bins defined by modules. +package: Testing +version: VERSION +core: 8.x +hidden: true diff --git a/core/modules/system/tests/modules/module_cachebin/module_cachebin.services.yml b/core/modules/system/tests/modules/module_cachebin/module_cachebin.services.yml new file mode 100644 index 0000000000000000000000000000000000000000..8e1427463bfe9f348f514c75ca1fa9a21167c36a --- /dev/null +++ b/core/modules/system/tests/modules/module_cachebin/module_cachebin.services.yml @@ -0,0 +1,7 @@ +services: + module_cachebin.cache_bin: + class: Drupal\Core\Cache\CacheBackendInterface + tags: + - { name: cache.bin } + factory: cache.backend.database:get + arguments: [module_cachebin] diff --git a/core/tests/Drupal/KernelTests/Core/Extension/ModuleInstallerTest.php b/core/tests/Drupal/KernelTests/Core/Extension/ModuleInstallerTest.php index fa5a9a17513b6728927a67d27fdea2c23c3e9f78..ff183c6f00dbf3f59e8d2fb1ccc094656cc886b2 100644 --- a/core/tests/Drupal/KernelTests/Core/Extension/ModuleInstallerTest.php +++ b/core/tests/Drupal/KernelTests/Core/Extension/ModuleInstallerTest.php @@ -60,4 +60,29 @@ public function testConfigChangeOnInstall() { $this->assertEquals(1, $modules['module_handler_test_multiple_child'], 'Weight of module_handler_test_multiple_child is set.'); } + /** + * Tests cache bins defined by modules are removed when uninstalled. + * + * @covers ::removeCacheBins + */ + public function testCacheBinCleanup() { + $schema = $this->container->get('database')->schema(); + $table = 'cache_module_cachebin'; + + $module_installer = $this->container->get('module_installer'); + $module_installer->install(['module_cachebin']); + + // Prime the bin. + /** @var \Drupal\Core\Cache\CacheBackendInterface $cache_bin */ + $cache_bin = $this->container->get('module_cachebin.cache_bin'); + $cache_bin->set('foo', 'bar'); + + // A database backend is used so there is a convenient way check whether the + // backend is uninstalled. + $this->assertTrue($schema->tableExists($table)); + + $module_installer->uninstall(['module_cachebin']); + $this->assertFalse($schema->tableExists($table)); + } + }