diff --git a/core/lib/Drupal/Core/Cache/CacheFactory.php b/core/lib/Drupal/Core/Cache/CacheFactory.php index 3cbd920be9..d06f330234 100644 --- a/core/lib/Drupal/Core/Cache/CacheFactory.php +++ b/core/lib/Drupal/Core/Cache/CacheFactory.php @@ -76,7 +76,7 @@ public function get($bin) { elseif (isset($cache_settings['default']) && $this->container->has($cache_settings['default'])) { $service_name = $cache_settings['default']; } - else { + if (!isset($service_name) || !$this->container->has($service_name)) { // Fall back to the database backend if nothing else is configured. $service_name = 'cache.backend.database'; } diff --git a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php index 00c519c65f..e721485a6b 100644 --- a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php +++ b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php @@ -66,17 +66,6 @@ public function __construct(Settings $settings = NULL, $consistent_service_name } } - /** - * Getter for the consistent service name. - * - * @return string - * The service name of the consistent backend factory. - */ - public function getConsistentServiceName() { - return ($this->consistentServiceName && $this->container->has($this->consistentServiceName)) ? - $this->consistentServiceName : 'cache.backend.database'; - } - /** * Instantiates a chained, fast cache backend class for a given cache bin. * @@ -87,17 +76,18 @@ public function getConsistentServiceName() { * The cache backend object associated with the specified bin. */ public function get($bin) { + $this->consistentServiceName = ($this->consistentServiceName && $this->container->has($this->consistentServiceName)) ? $this->consistentServiceName : 'cache.backend.database'; // Use the chained backend only if there is a fast backend available; // otherwise, just return the consistent backend directly. if (isset($this->fastServiceName)) { return new ChainedFastBackend( - $this->container->get($this->getConsistentServiceName())->get($bin), + $this->container->get($this->consistentServiceName)->get($bin), $this->container->get($this->fastServiceName)->get($bin), $bin ); } else { - return $this->container->get($this->getConsistentServiceName())->get($bin); + return $this->container->get($this->consistentServiceName)->get($bin); } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index cda055b2a3..88b3116400 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -297,7 +297,7 @@ function system_requirements($phase) { $requirements['php_extensions']['value'] = t('Enabled'); } - if ($phase === 'install' || $phase === 'runtime') { + if ($phase == 'install' || $phase == 'runtime') { // Check to see if OPcache is installed. if (!OpCodeCache::isEnabled()) { $requirements['php_opcache'] = [ @@ -352,31 +352,23 @@ function system_requirements($phase) { } } - if ($phase === 'install' || $phase === 'runtime') { + if ($phase === 'runtime') { // Check that the required cache backends exist. - $cache_settings = Settings::get('cache'); - $missing_cache_backends = []; - if (isset($cache_settings['bins'])) { - foreach ($cache_settings['bins'] as $bin => $backend) { - if (!Drupal::getContainer()->has($cache_settings['bins'][$bin])) { - $missing_cache_backends[] = $cache_settings['bins'][$bin]; + $cache_default_bin_backends = Drupal::getContainer()->getParameter('cache_default_bin_backends'); + if (!empty($cache_default_bin_backends)) { + foreach ($cache_default_bin_backends as $bin => $backend) { + if (!Drupal::getContainer()->has($backend)) { + $requirements['cache_backend_bin_' . $bin] = [ + 'title' => t('Cache Backend'), + 'value' => t('Missing'), + 'severity' => REQUIREMENT_WARNING, + 'description' => new TranslatableMarkup( + 'The configured cache backend %backend is not available for the bin %bin.', + ['%backend' => $backend, '%bin' => $bin]), + ]; } } } - if (isset($cache_settings['default']) && !Drupal::getContainer()->has($cache_settings['default'])) { - $missing_cache_backends[] = $cache_settings['default']; - } - if ($missing_cache_backends) { - $requirements['cache_backend'] = [ - 'title' => t('Cache Backend'), - 'value' => t('Missing'), - 'severity' => REQUIREMENT_WARNING, - 'description' => new PluralTranslatableMarkup(count($missing_cache_backends), - 'The configured Cache Backend %backend is not available.', - 'The configured Cache Backends %backend are not available.', - ['%backend' => implode(', ', $missing_cache_backends)]), - ]; - } } if ($phase != 'update') { diff --git a/core/modules/system/tests/modules/cache_test/cache_test.services.yml b/core/modules/system/tests/modules/cache_test/cache_test.services.yml new file mode 100644 index 0000000000..577c6333b8 --- /dev/null +++ b/core/modules/system/tests/modules/cache_test/cache_test.services.yml @@ -0,0 +1,25 @@ +services: + cache.cache_test_missing: + class: Drupal\Core\Cache\CacheBackendInterface + tags: + - { name: cache.bin, default_backend: cache.backend.missing } + factory: [ '@cache_factory', 'get' ] + arguments: [ cache_test_missing ] + cache.cache_test_lost: + class: Drupal\Core\Cache\CacheBackendInterface + tags: + - { name: cache.bin, default_backend: cache.backend.lost } + factory: [ '@cache_factory', 'get' ] + arguments: [ cache_test_lost ] + cache.cache_test_memory: + class: Drupal\Core\Cache\CacheBackendInterface + tags: + - { name: cache.bin, default_backend: cache.backend.memory } + factory: [ '@cache_factory', 'get' ] + arguments: [ cache_test_memory ] + cache.cache_test_no_default: + class: Drupal\Core\Cache\CacheBackendInterface + tags: + - { name: cache.bin } + factory: [ '@cache_factory', 'get' ] + arguments: [ cache_test_no_default ] diff --git a/core/modules/system/tests/src/Functional/System/MissingCacheBackendTest.php b/core/modules/system/tests/src/Functional/System/MissingCacheBackendTest.php new file mode 100644 index 0000000000..8fcbb7496f --- /dev/null +++ b/core/modules/system/tests/src/Functional/System/MissingCacheBackendTest.php @@ -0,0 +1,49 @@ +drupalLogin($this->createUser(['administer site configuration'])); + } + + /** + * Tests warnings correctly appear on status page. + */ + public function testStatusPage() { + $assert_session = $this->assertSession(); + $this->drupalGet('admin/reports/status'); + + // A couple services are configured with non-existent backends. + $assert_session->pageTextContains('The configured cache backend cache.backend.missing is not available for the bin cache_test_missing'); + $assert_session->pageTextContains('The configured cache backend cache.backend.lost is not available for the bin cache_test_lost'); + // A couple services have valid configuration and should have no warning. + $assert_session->pageTextNotContains('cache_test_memory'); + $assert_session->pageTextNotContains('cache_test_no_default'); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Cache/ChainedFastBackendFactoryTest.php b/core/tests/Drupal/Tests/Core/Cache/ChainedFastBackendFactoryTest.php new file mode 100644 index 0000000000..f7373099fa --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Cache/ChainedFastBackendFactoryTest.php @@ -0,0 +1,190 @@ +setContainer($container); + + $builtin_default_backend_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.database', $builtin_default_backend_factory); + $builtin_fast_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.apcu', $builtin_fast_factory); + + $render_bin = $this->createMock('\Drupal\Core\Cache\CacheBackendInterface'); + $builtin_default_backend_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + $builtin_fast_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + + $actual_bin = $chained_fast_backend_factory->get('render'); + $this->assertInstanceOf('Drupal\Core\Cache\ChainedFastBackend', $actual_bin); + } + + /** + * Tests that chained fast backend is bypassed during installation. + * + * @covers ::__construct + * @covers ::get + */ + public function testChainedFastBackendFactoryDuringInstallation() { + $GLOBALS['install_state']['installation_finished'] = FALSE; + $settings = new Settings([]); + $chained_fast_backend_factory = new ChainedFastBackendFactory($settings); + + $container = new ContainerBuilder(); + $chained_fast_backend_factory->setContainer($container); + + $builtin_default_backend_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.database', $builtin_default_backend_factory); + $builtin_fast_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.apcu', $builtin_fast_factory); + + $render_bin = $this->createMock('\Drupal\Core\Cache\CacheBackendInterface'); + $builtin_default_backend_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + + $actual_bin = $chained_fast_backend_factory->get('render'); + $this->assertSame($render_bin, $actual_bin); + unset($GLOBALS['install_state']); + } + + /** + * Tests that it falls back to customized default service. + * + * @covers ::__construct + * @covers ::get + */ + public function testChainedFastBackendFactoryWithCustomizedDefaultBackend() { + $settings = new Settings([ + 'cache' => [ + 'default' => 'cache.backend.custom', + ], + ]); + $chained_fast_backend_factory = new ChainedFastBackendFactory($settings); + + $container = new ContainerBuilder(); + $chained_fast_backend_factory->setContainer($container); + + $custom_default_backend_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.custom', $custom_default_backend_factory); + $builtin_fast_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.apcu', $builtin_fast_factory); + + $render_bin = $this->createMock('\Drupal\Core\Cache\CacheBackendInterface'); + $custom_default_backend_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + $builtin_fast_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + + $actual_bin = $chained_fast_backend_factory->get('render'); + $this->assertInstanceOf('Drupal\Core\Cache\ChainedFastBackend', $actual_bin); + } + + /** + * Tests that it uses the correct default bin backend. + * + * @covers ::__construct + * @covers ::get + */ + public function testChainedFastBackendFactoryWithDefaultBinBackend() { + // Ensure the default bin backends are used before the configured default. + $settings = new Settings([ + 'cache' => [ + 'default' => 'cache.backend.unused', + ], + ]); + + $consistent_service_name = 'cache.backend.custom'; + + $chained_fast_backend_factory = new ChainedFastBackendFactory($settings, $consistent_service_name); + + $container = new ContainerBuilder(); + $chained_fast_backend_factory->setContainer($container); + + $custom_default_backend_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.custom', $custom_default_backend_factory); + $builtin_fast_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.apcu', $builtin_fast_factory); + + $render_bin = $this->createMock('\Drupal\Core\Cache\CacheBackendInterface'); + $custom_default_backend_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + $builtin_fast_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + + $actual_bin = $chained_fast_backend_factory->get('render'); + $this->assertInstanceOf('Drupal\Core\Cache\ChainedFastBackend', $actual_bin); + } + + /** + * Tests when default bin service does not exist. + * + * @covers ::__construct + * @covers ::get + */ + public function testChainedFastBackendFactoryBinBackendDefaultServiceExist() { + $settings = new Settings([ + 'cache' => [ + 'default' => 'cache.backend.custom', + ], + ]); + $chained_fast_backend_factory = new ChainedFastBackendFactory($settings); + + $container = new ContainerBuilder(); + $chained_fast_backend_factory->setContainer($container); + + $builtin_default_backend_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.database', $builtin_default_backend_factory); + $builtin_fast_factory = $this->createMock('\Drupal\Core\Cache\CacheFactoryInterface'); + $container->set('cache.backend.apcu', $builtin_fast_factory); + + $render_bin = $this->createMock('\Drupal\Core\Cache\CacheBackendInterface'); + $builtin_default_backend_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + $builtin_fast_factory->expects($this->once()) + ->method('get') + ->with('render') + ->will($this->returnValue($render_bin)); + + $actual_bin = $chained_fast_backend_factory->get('render'); + $this->assertInstanceOf('Drupal\Core\Cache\ChainedFastBackend', $actual_bin); + } + +}