From 436fe57760e23322341685dfc0b610daf4b6f38c Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Tue, 2 Jul 2013 13:45:03 +0100 Subject: [PATCH] Issue #1964156 by Cottser, geoffreyr, joelpittet: Contrib cannot define additional Twig extensions. --- core/lib/Drupal/Core/CoreServiceProvider.php | 3 + .../Compiler/RegisterTwigExtensionsPass.php | 46 ++++++++ .../system/Tests/Theme/TwigExtensionTest.php | 72 ++++++++++++ .../TwigExtension/TestExtension.php | 106 ++++++++++++++++++ .../TwigExtensionTestController.php | 42 +++++++ .../twig_extension_test.filter.html.twig | 3 + .../twig_extension_test.function.html.twig | 7 ++ .../twig_extension_test.info.yml | 7 ++ .../twig_extension_test.module | 23 ++++ .../twig_extension_test.routing.yml | 12 ++ .../twig_extension_test.services.yml | 5 + 11 files changed, 326 insertions(+) create mode 100644 core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterTwigExtensionsPass.php create mode 100644 core/modules/system/lib/Drupal/system/Tests/Theme/TwigExtensionTest.php create mode 100644 core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtension/TestExtension.php create mode 100644 core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtensionTestController.php create mode 100644 core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.filter.html.twig create mode 100644 core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.function.html.twig create mode 100644 core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml create mode 100644 core/modules/system/tests/modules/twig_extension_test/twig_extension_test.module create mode 100644 core/modules/system/tests/modules/twig_extension_test/twig_extension_test.routing.yml create mode 100644 core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 15924c3b298..c3b69943e79 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -22,6 +22,7 @@ use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass; use Drupal\Core\DependencyInjection\Compiler\RegisterStringTranslatorsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass; use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterTwigExtensionsPass; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; @@ -74,6 +75,8 @@ class CoreServiceProvider implements ServiceProviderInterface { $container->addCompilerPass(new ModifyServiceDefinitionsPass()); // Add the compiler pass that will process tagged authentication services. $container->addCompilerPass(new RegisterAuthenticationPass()); + // Register Twig extensions. + $container->addCompilerPass(new RegisterTwigExtensionsPass()); } /** diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterTwigExtensionsPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterTwigExtensionsPass.php new file mode 100644 index 00000000000..3df9b87db91 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterTwigExtensionsPass.php @@ -0,0 +1,46 @@ +hasDefinition('twig')) { + return; + } + + $definition = $container->getDefinition('twig'); + + foreach ($container->findTaggedServiceIds('twig.extension') as $id => $attributes) { + // We must assume that the class value has been correcly filled, + // even if the service is created by a factory. + $class = $container->getDefinition($id)->getClass(); + + $refClass = new \ReflectionClass($class); + $interface = 'Twig_ExtensionInterface'; + if (!$refClass->implementsInterface($interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + $definition->addMethodCall('addExtension', array(new Reference($id))); + } + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigExtensionTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigExtensionTest.php new file mode 100644 index 00000000000..8526cb2e103 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigExtensionTest.php @@ -0,0 +1,72 @@ + 'Twig Extension', + 'description' => 'Tests Twig extensions.', + 'group' => 'Theme', + ); + } + + function setUp() { + parent::setUp(); + theme_enable(array('test_theme')); + } + + /** + * Tests that the provided Twig extension loads the service appropriately. + */ + function testTwigExtensionLoaded() { + $twigService = \Drupal::service('twig'); + $ext = $twigService->getExtension('twig_extension_test.test_extension'); + $this->assertEqual(get_class($ext), 'Drupal\twig_extension_test\TwigExtension\TestExtension', 'TestExtension loaded successfully.'); + } + + /** + * Tests that the Twig extension's filter produces expected output. + */ + function testTwigExtensionFilter() { + config('system.theme') + ->set('default', 'test_theme') + ->save(); + + $this->drupalGet('twig-extension-test/filter'); + $this->assertText('Every plant is not a mineral.', 'Success: String filtered.'); + } + + /** + * Tests that the Twig extension's function produces expected output. + */ + function testTwigExtensionFunction() { + config('system.theme') + ->set('default', 'test_theme') + ->save(); + + $this->drupalGet('twig-extension-test/function'); + $this->assertText('THE QUICK BROWN BOX JUMPS OVER THE LAZY DOG 123.', 'Success: Text converted to uppercase.'); + $this->assertText('the quick brown box jumps over the lazy dog 123.', 'Success: Text converted to lowercase.'); + $this->assertNoText('The Quick Brown Fox Jumps Over The Lazy Dog 123.', 'Success: No text left behind.'); + } + +} diff --git a/core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtension/TestExtension.php b/core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtension/TestExtension.php new file mode 100644 index 00000000000..93da6335770 --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtension/TestExtension.php @@ -0,0 +1,106 @@ + new \Twig_Function_Function(array('Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFunction')), + ); + } + + /** + * Generates a list of all Twig filters that this extension defines. + * + * @return array + * A key/value array that defines custom Twig filters. The key denotes the + * filter name used in the tag, e.g.: + * @code + * {{ foo|testfilter }} + * @endcode + * + * The value is a standard PHP callback that defines what the filter does. + */ + public function getFilters() { + return array( + 'testfilter' => new \Twig_Filter_Function(array('Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFilter')), + ); + } + + /** + * Gets a unique identifier for this Twig extension. + * + * @return string + * A unique identifier for this Twig extension. + */ + public function getName() { + return 'twig_extension_test.test_extension'; + } + + /** + * Outputs either an uppercase or lowercase test phrase. + * + * The function generates either an uppercase or lowercase version of the + * phrase "The quick brown fox jumps over the lazy dog 123.", depending on + * whether or not the $upperCase parameter evaluates to TRUE. If $upperCase + * evaluates to TRUE, the result will be uppercase, and if it evaluates to + * FALSE, the result will be lowercase. + * + * @param bool $upperCase + * (optional) Whether the result is uppercase (true) or lowercase (false). + * + * @return string + * The generated string. + * + * @see \Drupal\system\Tests\Theme\TwigExtensionTest::testTwigExtensionFunction() + */ + public static function testFunction($upperCase = FALSE) { + $string = "The quick brown box jumps over the lazy dog 123."; + if ($upperCase == TRUE) { + return strtoupper($string); + } + else { + return strtolower($string); + } + } + + /** + * Replaces all instances of "animal" in a string with "plant". + * + * @param string $string + * The string to be filtered. + * + * @return string + * The filtered string. + * + * @see \Drupal\system\Tests\Theme\TwigExtensionTest::testTwigExtensionFilter() + */ + public static function testFilter($string) { + return str_replace(array('animal'), array('plant'), $string); + } + +} diff --git a/core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtensionTestController.php b/core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtensionTestController.php new file mode 100644 index 00000000000..79e32111454 --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/lib/Drupal/twig_extension_test/TwigExtensionTestController.php @@ -0,0 +1,42 @@ + 'twig_extension_test_filter', + '#message' => 'Every animal is not a mineral.', + ); + } + + /** + * Menu callback for testing Twig functions in a Twig template. + */ + public function testFunctionRender() { + return array('#theme' => 'twig_extension_test_function'); + } + +} diff --git a/core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.filter.html.twig b/core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.filter.html.twig new file mode 100644 index 00000000000..1e224d07a4d --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.filter.html.twig @@ -0,0 +1,3 @@ +
+ {{ message|testfilter }} +
diff --git a/core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.function.html.twig b/core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.function.html.twig new file mode 100644 index 00000000000..76e870f34d7 --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/templates/twig_extension_test.function.html.twig @@ -0,0 +1,7 @@ +
+ {{ testfunc(1) }} +
+ +
+ {{ testfunc(0) }} +
diff --git a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml new file mode 100644 index 00000000000..270352fa7a5 --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml @@ -0,0 +1,7 @@ +name: 'Twig Extension Test' +type: module +description: 'Support module for testing Twig extensions.' +package: Testing +version: VERSION +core: 8.x +hidden: true diff --git a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.module b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.module new file mode 100644 index 00000000000..7e894701042 --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.module @@ -0,0 +1,23 @@ + array( + 'variables' => array('message' => NULL), + 'template' => 'twig_extension_test.filter', + ), + 'twig_extension_test_function' => array( + 'render element' => 'element', + 'template' => 'twig_extension_test.function', + ), + ); +} + diff --git a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.routing.yml b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.routing.yml new file mode 100644 index 00000000000..8d4baced254 --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.routing.yml @@ -0,0 +1,12 @@ +twig_extension_test.filter: + pattern: '/twig-extension-test/filter' + defaults: + _content: '\Drupal\twig_extension_test\TwigExtensionTestController::testFilterRender' + requirements: + _permission: 'access content' +twig_extension_test.function: + pattern: '/twig-extension-test/function' + defaults: + _content: '\Drupal\twig_extension_test\TwigExtensionTestController::testFunctionRender' + requirements: + _permission: 'access content' diff --git a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml new file mode 100644 index 00000000000..8784c0f3415 --- /dev/null +++ b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml @@ -0,0 +1,5 @@ +services: + twig_extension_test.twig.test_extension: + class: Drupal\twig_extension_test\TwigExtension\TestExtension + tags: + - { name: twig.extension }