Issue #1964156 by Cottser, geoffreyr, joelpittet: Contrib cannot define additional Twig extensions.
parent
b48f8fda69
commit
436fe57760
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterTwigExtensionsPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Register additional Twig extensions to the Twig service container.
|
||||
*/
|
||||
class RegisterTwigExtensionsPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* Adds services tagged 'twig.extension' to the Twig service container.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
|
||||
* The container to process.
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
if (!$container->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)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigExtensionTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig extensions.
|
||||
*/
|
||||
class TwigExtensionTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test', 'twig_extension_test');
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => '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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\twig_extension_test\TwigExtension\TestExtension.
|
||||
*/
|
||||
|
||||
namespace Drupal\twig_extension_test\TwigExtension;
|
||||
|
||||
use Drupal\Core\Template\TwigExtension;
|
||||
use Drupal\Core\Template\TwigReferenceFunction;
|
||||
|
||||
/**
|
||||
* A test Twig extension that adds a custom function and a custom filter.
|
||||
*/
|
||||
class TestExtension extends TwigExtension {
|
||||
|
||||
/**
|
||||
* Generates a list of all Twig functions that this extension defines.
|
||||
*
|
||||
* @return array
|
||||
* A key/value array that defines custom Twig functions. The key denotes the
|
||||
* function name used in the tag, e.g.:
|
||||
* @code
|
||||
* {{ testfunc() }}
|
||||
* @endcode
|
||||
*
|
||||
* The value is a standard PHP callback that defines what the function does.
|
||||
*/
|
||||
public function getFunctions() {
|
||||
return array(
|
||||
'testfunc' => 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\twig_extension_test\TwigExtensionTestController.
|
||||
*/
|
||||
|
||||
namespace Drupal\twig_extension_test;
|
||||
|
||||
use Drupal\Core\Controller\ControllerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Controller routines for Twig extension test routes.
|
||||
*/
|
||||
class TwigExtensionTestController implements ControllerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback for testing Twig filters in a Twig template.
|
||||
*/
|
||||
public function testFilterRender() {
|
||||
return array(
|
||||
'#theme' => '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');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<div class="testfilter">
|
||||
{{ message|testfilter }}
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
<div class="testfunction">
|
||||
{{ testfunc(1) }}
|
||||
</div>
|
||||
|
||||
<div class="testfunction">
|
||||
{{ testfunc(0) }}
|
||||
</div>
|
|
@ -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
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper module for Twig extension tests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function twig_extension_test_theme($existing, $type, $theme, $path) {
|
||||
return array(
|
||||
'twig_extension_test_filter' => array(
|
||||
'variables' => array('message' => NULL),
|
||||
'template' => 'twig_extension_test.filter',
|
||||
),
|
||||
'twig_extension_test_function' => array(
|
||||
'render element' => 'element',
|
||||
'template' => 'twig_extension_test.function',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -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'
|
|
@ -0,0 +1,5 @@
|
|||
services:
|
||||
twig_extension_test.twig.test_extension:
|
||||
class: Drupal\twig_extension_test\TwigExtension\TestExtension
|
||||
tags:
|
||||
- { name: twig.extension }
|
Loading…
Reference in New Issue