diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index 861a010..3893338 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -146,7 +146,7 @@ protected function init($theme_name = NULL) { if (!isset($theme_name)) { // #1: The theme registry might get instantiated before the theme was // initialized. Cope with that. - if (!isset($GLOBALS['theme_info'])) { + if (!isset($GLOBALS['theme_info']) || !isset($GLOBALS['theme'])) { unset($this->runtimeRegistry); unset($this->registry); drupal_theme_initialize(); @@ -307,9 +307,9 @@ public function getBaseHook($hook) { protected function build($theme, array $base_theme, $theme_engine) { $cache = array(); // First, preprocess the theme hooks advertised by modules. This will - // serve as the basic registry. Since the list of enabled modules is the same - // regardless of the theme used, this is cached in its own entry to save - // building it for every theme. + // serve as the basic registry. Since the list of enabled modules is the + // same regardless of the theme used, this is cached in its own entry to + // save building it for every theme. if ($cached = $this->cache->get('theme_registry:build:modules')) { $cache = $cached->data; } @@ -361,19 +361,19 @@ protected function build($theme, array $base_theme, $theme_engine) { * * @param $cache * The theme registry that will eventually be cached; It is an associative - * array keyed by theme hooks, whose values are associative arrays describing - * the hook: + * array keyed by theme hooks, whose values are associative arrays + * describing the hook: * - 'type': The passed-in $type. * - 'theme path': The passed-in $path. * - 'function': The name of the function generating output for this theme * hook. Either defined explicitly in hook_theme() or, if neither * 'function' nor 'template' is defined, then the default theme function - * name is used. The default theme function name is the theme hook prefixed - * by either 'theme_' for modules or '$name_' for everything else. If - * 'function' is defined, 'template' is not used. + * name is used. The default theme function name is the theme hook + * prefixed by either 'theme_' for modules or '$name_' for everything + * else. If 'function' is defined, 'template' is not used. * - 'template': The filename of the template generating output for this - * theme hook. The template is in the directory defined by the 'path' key of - * hook_theme() or defaults to "$path/templates". + * theme hook. The template is in the directory defined by the 'path' key + * of hook_theme() or defaults to "$path/templates". * - 'variables': The variables for this theme hook as defined in * hook_theme(). If there is more than one implementation and 'variables' * is not specified in a later one, then the previous definition is kept. @@ -382,19 +382,19 @@ protected function build($theme, array $base_theme, $theme_engine) { * 'render element' is not specified in a later one, then the previous * definition is kept. * - 'preprocess functions': See theme() for detailed documentation. - * @param $name + * @param string $name * The name of the module, theme engine, base theme engine, theme or base * theme implementing hook_theme(). - * @param $type + * @param string $type * One of 'module', 'theme_engine', 'base_theme_engine', 'theme', or - * 'base_theme'. Unlike regular hooks that can only be implemented by modules, - * each of these can implement hook_theme(). _theme_process_registry() is - * called in aforementioned order and new entries override older ones. For - * example, if a theme hook is both defined by a module and a theme, then the - * definition in the theme will be used. - * @param $theme + * 'base_theme'. Unlike regular hooks that can only be implemented by + * modules, each of these can implement hook_theme(). + * _theme_process_registry() is called in aforementioned order and new + * entries override older ones. For example, if a theme hook is both defined + * by a module and a theme, then the definition in the theme will be used. + * @param \stdClass $theme * The loaded $theme object as returned from list_themes(). - * @param $path + * @param string $path * The directory where $name is. For example, modules/system or * themes/bartik. * @@ -572,7 +572,8 @@ public function destruct() { } /** - * @param $module + * Wraps drupal_get_path(). + * * @return string */ protected function getPath($module) { @@ -580,14 +581,19 @@ protected function getPath($module) { } /** + * Wraps list_themes(). + * * @return array */ protected function listThemes() { return list_themes(); } + /** + * Wraps drupal_theme_initialize(). + */ protected function initializeTheme() { return drupal_theme_initialize(); } -} +} diff --git a/core/lib/Drupal/Core/Utility/ThemeRegistry.php b/core/lib/Drupal/Core/Utility/ThemeRegistry.php index 7ef77cc..e09ed65 100644 --- a/core/lib/Drupal/Core/Utility/ThemeRegistry.php +++ b/core/lib/Drupal/Core/Utility/ThemeRegistry.php @@ -50,7 +50,7 @@ class ThemeRegistry extends CacheCollector implements DestructableInterface { * @param bool $modules_loaded * Whether all modules have already been loaded. */ - function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags, $modules_loaded = FALSE) { + function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags = array(), $modules_loaded = FALSE) { $this->cid = $cid; $this->cache = $cache; $this->lock = $lock; @@ -74,6 +74,9 @@ function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $l foreach (array_keys($this->storage) as $key) { $this->persist($key); } + // RegistryTest::testRaceCondition() ensures that the cache entry is + // written on the initial construction of the theme registry. + $this->updateCache(); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php index 5ab43ca..3e4d6af 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php @@ -35,17 +35,19 @@ public static function getInfo() { * Tests the behavior of the theme registry class. */ function testRaceCondition() { - $_SERVER['REQUEST_METHOD'] = 'GET'; + \Drupal::request()->setMethod('GET'); $cid = 'test_theme_registry'; // Directly instantiate the theme registry, this will cause a base cache // entry to be written in __construct(). - $registry = new ThemeRegistry($cid, 'cache', array('theme_registry' => TRUE), $this->container->get('module_handler')->isLoaded()); + $cache = \Drupal::cache('cache'); + $lock_backend = \Drupal::lock(); + $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry' => TRUE), $this->container->get('module_handler')->isLoaded()); $this->assertTrue(cache()->get($cid), 'Cache entry was created.'); // Trigger a cache miss for an offset. - $this->assertTrue($registry['theme_test_template_test'], 'Offset was returned correctly from the theme registry.'); + $this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry.'); // This will cause the ThemeRegistry class to write an updated version of // the cache entry when it is destroyed, usually at the end of the request. // Before that happens, manually delete the cache entry we created earlier @@ -53,15 +55,15 @@ function testRaceCondition() { cache()->delete($cid); // Destroy the class so that it triggers a cache write for the offset. - unset($registry); + $registry->destruct(); $this->assertTrue(cache()->get($cid), 'Cache entry was created.'); // Create a new instance of the class. Confirm that both the offset // requested previously, and one that has not yet been requested are both // available. - $registry = new ThemeRegistry($cid, 'cache', array('theme_registry' => TRUE)); - $this->assertTrue($registry['theme_test_template_test'], 'Offset was returned correctly from the theme registry'); - $this->assertTrue($registry['theme_test_template_test_2'], 'Offset was returned correctly from the theme registry'); + $registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry' => TRUE), $this->container->get('module_handler')->isLoaded()); + $this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry'); + $this->assertTrue($registry->get('theme_test_template_test_2'), 'Offset was returned correctly from the theme registry'); } } diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php index 56cbdaf..41129cc 100644 --- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php +++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/EventSubscriber/ThemeTestSubscriber.php @@ -7,6 +7,9 @@ namespace Drupal\theme_test\EventSubscriber; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\DependencyInjection\IntrospectableContainerInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -14,7 +17,14 @@ /** * Theme test subscriber for controller requests. */ -class ThemeTestSubscriber implements EventSubscriberInterface { +class ThemeTestSubscriber extends ContainerAware implements EventSubscriberInterface { + + /** + * The used container. + * + * @var \Symfony\Component\DependencyInjection\IntrospectableContainerInterface + */ + protected $container; /** * Generates themed output early in a page request. @@ -36,10 +46,18 @@ public function onRequest(GetResponseEvent $event) { // returning output and theming the page as a whole. $GLOBALS['theme_test_output'] = theme('more_link', array('url' => 'user', 'title' => 'Themed output generated in a KernelEvents::REQUEST listener')); } + } + + /** + * Ensures that the theme registry was not initialized. + */ + public function onView(GetResponseEvent $event) { + $request = $event->getRequest(); + $current_path = $request->attributes->get('_system_path'); if (strpos($current_path, 'user/autocomplete') === 0) { - // Register a fake registry loading callback. If it gets called by - // theme_get_registry(), the registry has not been initialized yet. - _theme_registry_callback('_theme_test_load_registry', array()); + if ($this->container->initialized('theme.registry')) { + throw new \Exception('registry initialized'); + } } } @@ -48,6 +66,7 @@ public function onRequest(GetResponseEvent $event) { */ static function getSubscribedEvents() { $events[KernelEvents::REQUEST][] = array('onRequest'); + $events[KernelEvents::VIEW][] = array('onView', -1000); return $events; } diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module index b48e712..87e26b8 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -79,13 +79,6 @@ function theme_test_menu() { ); return $items; } -/** - * Fake registry loading callback. - */ -function _theme_test_load_registry() { - print 'registry initialized'; - return array(); -} /** * Custom theme callback. diff --git a/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php index 099f614..8445150 100644 --- a/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php +++ b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php @@ -72,6 +72,7 @@ public function testGetRegistryForModule() { )); $this->registry->setBaseThemes(array()); + // Include the module so that hook_theme can be called. include_once DRUPAL_ROOT . '/core/modules/system/tests/modules/theme_test/theme_test.module'; $this->moduleHandler->expects($this->once()) ->method('getImplementations')