diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index 15ec673..6fd9ce5 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -653,7 +653,6 @@ protected function mergePreprocessFunctions($hook, $complete_hook, array &$cache } $cache[$hook]['preprocess functions'] = $preprocess_functions; - unset($cache[$hook]['incomplete preprocess functions']); } /** @@ -744,6 +743,7 @@ protected function postProcessExtension(array &$cache, ActiveTheme $theme) { else { $this->mergePreprocessFunctions($hook, $info['base hook'], $cache); } + unset($cache[$hook]['incomplete preprocess functions']); } // Optimize the registry. diff --git a/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php index df39966..79ca932 100644 --- a/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php +++ b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php @@ -5,325 +5,337 @@ * Contains \Drupal\Tests\Core\Theme\RegistryTest. */ -namespace Drupal\Tests\Core\Theme { +namespace Drupal\Tests\Core\Theme; - use Drupal\Core\Theme\ActiveTheme; - use Drupal\Core\Theme\Registry; - use Drupal\Tests\UnitTestCase; +use Drupal\Core\Theme\ActiveTheme; +use Drupal\Core\Theme\Registry; +use Drupal\Tests\UnitTestCase; +/** + * @coversDefaultClass \Drupal\Core\Theme\Registry + * @group Theme + */ +class RegistryTest extends UnitTestCase { /** - * @coversDefaultClass \Drupal\Core\Theme\Registry - * @group Theme + * The tested theme registry. + * + * @var \Drupal\Tests\Core\Theme\TestRegistry */ - class RegistryTest extends UnitTestCase { - - /** - * The tested theme registry. - * - * @var \Drupal\Tests\Core\Theme\TestRegistry - */ - protected $registry; - - /** - * The mocked cache backend. - * - * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $cache; - - /** - * The mocked lock backend. - * - * @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $lock; - - /** - * The mocked module handler. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $moduleHandler; - - /** - * The mocked theme handler. - * - * @var \Drupal\Core\Extension\ThemeHandlerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $themeHandler; - - /** - * The mocked theme initialization. - * - * @var \Drupal\Core\Theme\ThemeInitializationInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $themeInitialization; - - /** - * The theme manager. - * - * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $themeManager; - - /** - * The theme. - * - * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $theme; - - - /** - * The list of functions that get_defined_functions() should provide. - * - * @var array - */ - public static $functions; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); - $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); - $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); - $this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface'); - $this->themeInitialization = $this->getMock('Drupal\Core\Theme\ThemeInitializationInterface'); - $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface'); - self::$functions = []; - $this->setupTheme(); - } + protected $registry; - /** - * {@inheritdoc} - */ - protected function tearDown() { - parent::tearDown(); - RegistryTest::$functions = NULL; - } + /** + * The mocked cache backend. + * + * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cache; + + /** + * The mocked lock backend. + * + * @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $lock; + + /** + * The mocked module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $moduleHandler; + + /** + * The mocked theme handler. + * + * @var \Drupal\Core\Extension\ThemeHandlerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $themeHandler; + + /** + * The mocked theme initialization. + * + * @var \Drupal\Core\Theme\ThemeInitializationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $themeInitialization; + + /** + * The theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $themeManager; + + /** + * The theme. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $theme; + + /** + * The list of functions that get_defined_functions() should provide. + * + * @var array + */ + public static $functions = []; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); - /** - * Tests getting the theme registry defined by a module. - */ - public function testGetRegistryForModule() { - $test_theme = new ActiveTheme([ - 'name' => 'test_theme', - 'path' => 'core/modules/system/tests/themes/test_theme/test_theme.info.yml', - 'engine' => 'twig', - 'owner' => 'twig', - 'stylesheets_remove' => [], - 'libraries_override' => [], - 'libraries_extend' => [], - 'libraries' => [], - 'extension' => '.twig', - 'base_themes' => [], - ]); - - $test_stable = new ActiveTheme([ - 'name' => 'test_stable', - 'path' => 'core/modules/system/tests/themes/test_stable/test_stable.info.yml', - 'engine' => 'twig', - 'owner' => 'twig', - 'stylesheets_remove' => [], - 'libraries_override' => [], - 'libraries_extend' => [], - 'libraries' => [], - 'extension' => '.twig', - 'base_themes' => [], - ]); - - $this->themeManager->expects($this->exactly(2)) - ->method('getActiveTheme') - ->willReturnOnConsecutiveCalls($test_theme, $test_stable); - - // Include the module and theme files so that hook_theme can be called. - include_once $this->root . '/core/modules/system/tests/modules/theme_test/theme_test.module'; - include_once $this->root . '/core/modules/system/tests/themes/test_stable/test_stable.theme'; - $this->moduleHandler->expects($this->exactly(2)) + $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); + $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); + $this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface'); + $this->themeInitialization = $this->getMock('Drupal\Core\Theme\ThemeInitializationInterface'); + $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface'); + $this->setupTheme(); + } + + /** + * {@inheritdoc} + */ + protected function tearDown() { + parent::tearDown(); + static::$functions = []; + } + + /** + * Tests getting the theme registry defined by a module. + */ + public function testGetRegistryForModule() { + $test_theme = new ActiveTheme([ + 'name' => 'test_theme', + 'path' => 'core/modules/system/tests/themes/test_theme/test_theme.info.yml', + 'engine' => 'twig', + 'owner' => 'twig', + 'stylesheets_remove' => [], + 'libraries_override' => [], + 'libraries_extend' => [], + 'libraries' => [], + 'extension' => '.twig', + 'base_themes' => [], + ]); + + $test_stable = new ActiveTheme([ + 'name' => 'test_stable', + 'path' => 'core/modules/system/tests/themes/test_stable/test_stable.info.yml', + 'engine' => 'twig', + 'owner' => 'twig', + 'stylesheets_remove' => [], + 'libraries_override' => [], + 'libraries_extend' => [], + 'libraries' => [], + 'extension' => '.twig', + 'base_themes' => [], + ]); + + $this->themeManager->expects($this->exactly(2)) + ->method('getActiveTheme') + ->willReturnOnConsecutiveCalls($test_theme, $test_stable); + + // Include the module and theme files so that hook_theme can be called. + include_once $this->root . '/core/modules/system/tests/modules/theme_test/theme_test.module'; + include_once $this->root . '/core/modules/system/tests/themes/test_stable/test_stable.theme'; + $this->moduleHandler->expects($this->exactly(2)) ->method('getImplementations') ->with('theme') ->will($this->returnValue(array('theme_test'))); - $this->moduleHandler->expects($this->atLeastOnce()) + $this->moduleHandler->expects($this->atLeastOnce()) ->method('getModuleList') ->willReturn([]); - $registry = $this->registry->get(); - - // Ensure that the registry entries from the module are found. - $this->assertArrayHasKey('theme_test', $registry); - $this->assertArrayHasKey('theme_test_template_test', $registry); - $this->assertArrayHasKey('theme_test_template_test_2', $registry); - $this->assertArrayHasKey('theme_test_suggestion_provided', $registry); - $this->assertArrayHasKey('theme_test_specific_suggestions', $registry); - $this->assertArrayHasKey('theme_test_suggestions', $registry); - $this->assertArrayHasKey('theme_test_function_suggestions', $registry); - $this->assertArrayHasKey('theme_test_foo', $registry); - $this->assertArrayHasKey('theme_test_render_element', $registry); - $this->assertArrayHasKey('theme_test_render_element_children', $registry); - $this->assertArrayHasKey('theme_test_function_template_override', $registry); - - $this->assertArrayNotHasKey('test_theme_not_existing_function', $registry); - $this->assertFalse(in_array('test_stable_preprocess_theme_test_render_element', $registry['theme_test_render_element']['preprocess functions'])); - - $info = $registry['theme_test_function_suggestions']; - $this->assertEquals('module', $info['type']); - $this->assertEquals('core/modules/system/tests/modules/theme_test', $info['theme path']); - $this->assertEquals('theme_theme_test_function_suggestions', $info['function']); - $this->assertEquals(array(), $info['variables']); - - // The second call will initialize with the second theme. Ensure that this - // returns a different object and the discovery for the second theme's - // preprocess function worked. - $other_registry = $this->registry->get(); - $this->assertNotSame($registry, $other_registry); - $this->assertTrue(in_array('test_stable_preprocess_theme_test_render_element', $other_registry['theme_test_render_element']['preprocess functions'])); - } + $registry = $this->registry->get(); + + // Ensure that the registry entries from the module are found. + $this->assertArrayHasKey('theme_test', $registry); + $this->assertArrayHasKey('theme_test_template_test', $registry); + $this->assertArrayHasKey('theme_test_template_test_2', $registry); + $this->assertArrayHasKey('theme_test_suggestion_provided', $registry); + $this->assertArrayHasKey('theme_test_specific_suggestions', $registry); + $this->assertArrayHasKey('theme_test_suggestions', $registry); + $this->assertArrayHasKey('theme_test_function_suggestions', $registry); + $this->assertArrayHasKey('theme_test_foo', $registry); + $this->assertArrayHasKey('theme_test_render_element', $registry); + $this->assertArrayHasKey('theme_test_render_element_children', $registry); + $this->assertArrayHasKey('theme_test_function_template_override', $registry); + + $this->assertArrayNotHasKey('test_theme_not_existing_function', $registry); + $this->assertFalse(in_array('test_stable_preprocess_theme_test_render_element', $registry['theme_test_render_element']['preprocess functions'])); + + $info = $registry['theme_test_function_suggestions']; + $this->assertEquals('module', $info['type']); + $this->assertEquals('core/modules/system/tests/modules/theme_test', $info['theme path']); + $this->assertEquals('theme_theme_test_function_suggestions', $info['function']); + $this->assertEquals(array(), $info['variables']); + + // The second call will initialize with the second theme. Ensure that this + // returns a different object and the discovery for the second theme's + // preprocess function worked. + $other_registry = $this->registry->get(); + $this->assertNotSame($registry, $other_registry); + $this->assertTrue(in_array('test_stable_preprocess_theme_test_render_element', $other_registry['theme_test_render_element']['preprocess functions'])); + } - /** - * Sets up the mocks needed for testing Registry::postProcessExtension. - */ - public function setUpPostProcessExtension() { - $this->theme = $this->getMockBuilder('\Drupal\Core\Theme\ActiveTheme')->disableOriginalConstructor()->getMock(); - $this->theme->expects($this->atLeastOnce()) + /** + * Sets up the mocks needed for testing Registry::postProcessExtension. + */ + public function setUpPostProcessExtension() { + $this->theme = $this->getMockBuilder(ActiveTheme::class) + ->disableOriginalConstructor() + ->getMock(); + $this->theme->expects($this->atLeastOnce()) ->method('getBaseThemes') ->will($this->returnValue([])); - $this->theme->expects($this->atLeastOnce()) + $this->theme->expects($this->atLeastOnce()) ->method('getName') ->will($this->returnValue('test')); - $this->moduleHandler->expects($this->atLeastOnce()) + $this->moduleHandler->expects($this->atLeastOnce()) ->method('getModuleList') ->willReturn([]); + } - $class = new \ReflectionClass('Drupal\Tests\Core\Theme\TestRegistry'); - $this->reflectionMethod = $class->getMethod('postProcessExtension'); - $this->reflectionMethod->setAccessible(TRUE); - } - - /** - * Tests that preprocess functions are added when base hook is specified. - */ - public function testManualBaseHook() { - - $functions = ['cat', 'mouse', 'cheese']; - self::$functions['user'] = $functions; - // Define a test case registry with mock data. - $hooks = [ - 'test_hook' => - ['preprocess functions' => ['cat', 'mouse'], ], - 'test_hook_manual' => - ['preprocess functions' => ['cheese'], - 'base hook' => 'test_hook', - 'incomplete preprocess functions' => TRUE, ] - ]; - - $expected = [ - 'test_hook' => - ['preprocess functions' => ['cat', 'mouse'], ], - 'test_hook_manual' => - ['preprocess functions' => ['cat', 'mouse', 'cheese'], - 'base hook' => 'test_hook', ] - ]; - - $this->setUpPostProcessExtension(); - - $this->reflectionMethod->invokeArgs($this->registry, [ &$hooks, $this->theme]); - - $this->assertArrayEquals($hooks, $expected); - - } - - /** - * Tests that a suggestion defined in hook_theme gets preprocess functions. - */ - public function testManualSuggestion() { - - $this->setUpPostProcessExtension(); - - $functions = ['cat', 'mouse', 'cheese']; - - $hooks = [ - 'test_hook' => - ['preprocess functions' => ['cat', 'mouse'], - 'template' => 'test-hook'], - 'test_hook__suggestion' => - ['preprocess functions' => ['bread'], - 'incomplete preprocess functions' => TRUE, ], - 'test_hook__suggestion__another' => - ['preprocess functions' => ['cheese'], - 'incomplete preprocess functions' => TRUE, ] - ]; - - $expected = [ - 'test_hook' => - ['preprocess functions' => ['cat', 'mouse'], - 'template' => 'test-hook', ], - 'test_hook__suggestion' => - ['preprocess functions' => ['cat', 'mouse', 'bread', - 'test_preprocess_test_hook__suggestion'], - 'base hook' => 'test_hook', - 'template' => 'test-hook', ], - 'test_hook__suggestion__another' => - ['preprocess functions' => - ['cat', 'mouse', 'bread', - 'test_preprocess_test_hook__suggestion', - 'cheese', - 'test_preprocess_test_hook__suggestion__another'], - 'base hook' => 'test_hook', - 'template' => 'test-hook', ] - ]; - - $functions = ['cat', 'mouse', 'cheese', 'bread', - 'test_preprocess_test_hook__suggestion', - 'test_preprocess_test_hook__suggestion__another']; - self::$functions['user'] = $functions; - - $this->reflectionMethod->invokeArgs($this->registry, [ &$hooks, $this->theme]); - - $this->assertArrayEquals($hooks, $expected); - } + /** + * Tests that preprocess functions are added when base hook is specified. + * + * @covers ::postProcessExtension + */ + public function testManualBaseHook() { + static::$functions['user'] = ['cat', 'mouse', 'cheese']; + + // Define a test case registry with mock data. + $hooks = [ + 'test_hook' => [ + 'preprocess functions' => ['cat', 'mouse'], + ], + 'test_hook_manual' => [ + 'preprocess functions' => ['cheese'], + 'base hook' => 'test_hook', + 'incomplete preprocess functions' => TRUE, + ], + ]; + + $expected = [ + 'test_hook' => [ + 'preprocess functions' => ['cat', 'mouse'], + ], + 'test_hook_manual' => [ + 'preprocess functions' => ['cat', 'mouse', 'cheese'], + 'base hook' => 'test_hook', + ], + ]; + + $this->setUpPostProcessExtension(); + + $class = new \ReflectionClass(TestRegistry::class); + $reflection_method = $class->getMethod('postProcessExtension'); + $reflection_method->setAccessible(TRUE); + $reflection_method->invokeArgs($this->registry, [ &$hooks, $this->theme]); + + $this->assertArrayEquals($hooks, $expected); + } - protected function setupTheme() { - $this->registry = new TestRegistry($this->root, $this->cache, $this->lock, $this->moduleHandler, $this->themeHandler, $this->themeInitialization); - $this->registry->setThemeManager($this->themeManager); - } + /** + * Tests that a suggestion defined in hook_theme gets preprocess functions. + * + * @covers ::postProcessExtension + */ + public function testManualSuggestion() { + $this->setUpPostProcessExtension(); + + $hooks = [ + 'test_hook' => [ + 'preprocess functions' => ['cat', 'mouse'], + 'template' => 'test-hook', + ], + 'test_hook__suggestion' => [ + 'preprocess functions' => ['bread'], + 'incomplete preprocess functions' => TRUE, + ], + 'test_hook__suggestion__another' => [ + 'preprocess functions' => ['cheese'], + 'incomplete preprocess functions' => TRUE, + ], + ]; + + $expected = [ + 'test_hook' => [ + 'preprocess functions' => [ + 'cat', + 'mouse', + ], + 'template' => 'test-hook', + ], + 'test_hook__suggestion' => [ + 'preprocess functions' => [ + 'cat', + 'mouse', + 'bread', + 'test_preprocess_test_hook__suggestion', + ], + 'base hook' => 'test_hook', + 'template' => 'test-hook', + ], + 'test_hook__suggestion__another' => [ + 'preprocess functions' => [ + 'cat', + 'mouse', + 'bread', + 'test_preprocess_test_hook__suggestion', + 'cheese', + 'test_preprocess_test_hook__suggestion__another', + ], + 'base hook' => 'test_hook', + 'template' => 'test-hook', + ], + ]; + + static::$functions['user'] = [ + 'cat', + 'mouse', + 'cheese', + 'bread', + 'test_preprocess_test_hook__suggestion', + 'test_preprocess_test_hook__suggestion__another', + ]; + + $class = new \ReflectionClass(TestRegistry::class); + $reflection_method = $class->getMethod('postProcessExtension'); + $reflection_method->setAccessible(TRUE); + $reflection_method->invokeArgs($this->registry, [&$hooks, $this->theme]); + + $this->assertArrayEquals($hooks, $expected); + } + protected function setupTheme() { + $this->registry = new TestRegistry($this->root, $this->cache, $this->lock, $this->moduleHandler, $this->themeHandler, $this->themeInitialization); + $this->registry->setThemeManager($this->themeManager); } - class TestRegistry extends Registry { +} - protected $functions = []; +class TestRegistry extends Registry { - protected function getPath($module) { - if ($module == 'theme_test') { - return 'core/modules/system/tests/modules/theme_test'; - } + protected function getPath($module) { + if ($module == 'theme_test') { + return 'core/modules/system/tests/modules/theme_test'; } - } } -namespace Drupal\Core\Theme { +namespace Drupal\Core\Theme; - /** - * Override get_defined_functions with a configurable mock. - * - * Registry::getPrefixGroupedUserFunctions calls get_defined_functions and - * that is overridden here to allow testing postProcessExtension. - */ - function get_defined_functions() { - if (\Drupal\Tests\Core\Theme\RegistryTest::$functions) { - return \Drupal\Tests\Core\Theme\RegistryTest::$functions; - } - else { - return \get_defined_functions(); - } - } +use Drupal\Tests\Core\Theme\RegistryTest; +/** + * Overrides get_defined_functions() with a configurable mock. + */ +function get_defined_functions() { + return RegistryTest::$functions ?: \get_defined_functions(); }