Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Registers the Twig exception listener if Twig is registered as a templating engine.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExceptionListenerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('twig')) {
return;
}
// register the exception controller only if Twig is enabled and required dependencies do exist
if (!class_exists('Symfony\Component\Debug\Exception\FlattenException') || !interface_exists('Symfony\Component\EventDispatcher\EventSubscriberInterface')) {
$container->removeDefinition('twig.exception_listener');
} elseif ($container->hasParameter('templating.engines')) {
$engines = $container->getParameter('templating.engines');
if (!in_array('twig', $engines)) {
$container->removeDefinition('twig.exception_listener');
}
}
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Workflow\Workflow;
use Symfony\Component\Yaml\Parser as YamlParser;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class ExtensionPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!class_exists('Symfony\Component\Asset\Packages')) {
$container->removeDefinition('twig.extension.assets');
}
if (!class_exists('Symfony\Component\ExpressionLanguage\Expression')) {
$container->removeDefinition('twig.extension.expression');
}
if (!interface_exists('Symfony\Component\Routing\Generator\UrlGeneratorInterface')) {
$container->removeDefinition('twig.extension.routing');
}
if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) {
$container->removeDefinition('twig.extension.trans');
}
if (!class_exists('Symfony\Component\Yaml\Yaml')) {
$container->removeDefinition('twig.extension.yaml');
}
if ($container->has('form.extension')) {
$container->getDefinition('twig.extension.form')->addTag('twig.extension');
$reflClass = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension');
$container->getDefinition('twig.loader.native_filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form'));
}
if ($container->has('translator')) {
$container->getDefinition('twig.extension.trans')->addTag('twig.extension');
}
if ($container->has('router')) {
$container->getDefinition('twig.extension.routing')->addTag('twig.extension');
}
if ($container->has('fragment.handler')) {
$container->getDefinition('twig.extension.httpkernel')->addTag('twig.extension');
// inject Twig in the hinclude service if Twig is the only registered templating engine
if ((!$container->hasParameter('templating.engines') || array('twig') == $container->getParameter('templating.engines')) && $container->hasDefinition('fragment.renderer.hinclude')) {
$container->getDefinition('fragment.renderer.hinclude')
->addTag('kernel.fragment_renderer', array('alias' => 'hinclude'))
->replaceArgument(0, new Reference('twig'))
;
}
}
if ($container->has('request_stack')) {
$container->getDefinition('twig.extension.httpfoundation')->addTag('twig.extension');
}
if ($container->getParameter('kernel.debug')) {
$container->getDefinition('twig.extension.profiler')->addTag('twig.extension');
$container->getDefinition('twig.extension.debug')->addTag('twig.extension');
}
$composerRootDir = $this->getComposerRootDir($container->getParameter('kernel.root_dir'));
$twigLoader = $container->getDefinition('twig.loader.native_filesystem');
if ($container->has('templating')) {
$loader = $container->getDefinition('twig.loader.filesystem');
$loader->setMethodCalls(array_merge($twigLoader->getMethodCalls(), $loader->getMethodCalls()));
$loader->replaceArgument(2, $composerRootDir);
$twigLoader->clearTag('twig.loader');
} else {
$twigLoader->replaceArgument(1, $composerRootDir);
$container->setAlias('twig.loader.filesystem', new Alias('twig.loader.native_filesystem', false));
$container->removeDefinition('templating.engine.twig');
}
if ($container->has('assets.packages')) {
$container->getDefinition('twig.extension.assets')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(YamlParser::class));
if (class_exists(YamlParser::class)) {
$container->getDefinition('twig.extension.yaml')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(Stopwatch::class));
if (class_exists(Stopwatch::class)) {
$container->getDefinition('twig.extension.debug.stopwatch')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(ExpressionLanguage::class));
if (class_exists(ExpressionLanguage::class)) {
$container->getDefinition('twig.extension.expression')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(Workflow::class));
if (!class_exists(Workflow::class) || !$container->has('workflow.registry')) {
$container->removeDefinition('workflow.twig_extension');
} else {
$container->getDefinition('workflow.twig_extension')->addTag('twig.extension');
}
}
private function getComposerRootDir($rootDir)
{
$dir = $rootDir;
while (!file_exists($dir.'/composer.json')) {
if ($dir === dirname($dir)) {
return $rootDir;
}
$dir = dirname($dir);
}
return $dir;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* Registers Twig runtime services.
*/
class RuntimeLoaderPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('twig.runtime_loader')) {
return;
}
$definition = $container->getDefinition('twig.runtime_loader');
$mapping = array();
foreach ($container->findTaggedServiceIds('twig.runtime') as $id => $attributes) {
$def = $container->getDefinition($id);
if (!$def->isPublic()) {
throw new InvalidArgumentException(sprintf('The service "%s" must be public as it can be lazy-loaded.', $id));
}
if ($def->isAbstract()) {
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as it can be lazy-loaded.', $id));
}
$mapping[$def->getClass()] = $id;
}
$definition->replaceArgument(1, $mapping);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds tagged twig.extension services to twig service.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TwigEnvironmentPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('twig')) {
return;
}
$definition = $container->getDefinition('twig');
// Extensions must always be registered before everything else.
// For instance, global variable definitions must be registered
// afterward. If not, the globals from the extensions will never
// be registered.
$calls = $definition->getMethodCalls();
$definition->setMethodCalls(array());
foreach ($container->findTaggedServiceIds('twig.extension') as $id => $attributes) {
$definition->addMethodCall('addExtension', array(new Reference($id)));
}
$definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls));
}
}

View File

@@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Exception\LogicException;
/**
* Adds services tagged twig.loader as Twig loaders.
*
* @author Daniel Leech <daniel@dantleech.com>
*/
class TwigLoaderPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('twig')) {
return;
}
$prioritizedLoaders = array();
$found = 0;
foreach ($container->findTaggedServiceIds('twig.loader') as $id => $attributes) {
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$prioritizedLoaders[$priority][] = $id;
++$found;
}
if (!$found) {
throw new LogicException('No twig loaders found. You need to tag at least one loader with "twig.loader"');
}
if (1 === $found) {
$container->setAlias('twig.loader', $id);
} else {
$chainLoader = $container->getDefinition('twig.loader.chain');
krsort($prioritizedLoaders);
foreach ($prioritizedLoaders as $loaders) {
foreach ($loaders as $loader) {
$chainLoader->addMethodCall('addLoader', array(new Reference($loader)));
}
}
$container->setAlias('twig.loader', 'twig.loader.chain');
}
}
}

View File

@@ -0,0 +1,193 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* TwigExtension configuration structure.
*
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class Configuration implements ConfigurationInterface
{
/**
* Generates the configuration tree builder.
*
* @return TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('twig');
$rootNode
->children()
->scalarNode('exception_controller')->defaultValue('twig.controller.exception:showAction')->end()
->end()
;
$this->addFormThemesSection($rootNode);
$this->addGlobalsSection($rootNode);
$this->addTwigOptions($rootNode);
$this->addTwigFormatOptions($rootNode);
return $treeBuilder;
}
private function addFormThemesSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('form_theme')
->children()
->arrayNode('form_themes')
->addDefaultChildrenIfNoneSet()
->prototype('scalar')->defaultValue('form_div_layout.html.twig')->end()
->example(array('MyBundle::form.html.twig'))
->validate()
->ifTrue(function ($v) { return !in_array('form_div_layout.html.twig', $v); })
->then(function ($v) {
return array_merge(array('form_div_layout.html.twig'), $v);
})
->end()
->end()
->end()
;
}
private function addGlobalsSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('global')
->children()
->arrayNode('globals')
->normalizeKeys(false)
->useAttributeAsKey('key')
->example(array('foo' => '"@bar"', 'pi' => 3.14))
->prototype('array')
->beforeNormalization()
->ifTrue(function ($v) { return is_string($v) && 0 === strpos($v, '@'); })
->then(function ($v) {
if (0 === strpos($v, '@@')) {
return substr($v, 1);
}
return array('id' => substr($v, 1), 'type' => 'service');
})
->end()
->beforeNormalization()
->ifTrue(function ($v) {
if (is_array($v)) {
$keys = array_keys($v);
sort($keys);
return $keys !== array('id', 'type') && $keys !== array('value');
}
return true;
})
->then(function ($v) { return array('value' => $v); })
->end()
->children()
->scalarNode('id')->end()
->scalarNode('type')
->validate()
->ifNotInArray(array('service'))
->thenInvalid('The %s type is not supported')
->end()
->end()
->variableNode('value')->end()
->end()
->end()
->end()
->end()
;
}
private function addTwigOptions(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('path')
->children()
->variableNode('autoescape')->defaultValue('name')->end()
->scalarNode('autoescape_service')->defaultNull()->end()
->scalarNode('autoescape_service_method')->defaultNull()->end()
->scalarNode('base_template_class')->example('Twig\Template')->cannotBeEmpty()->end()
->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end()
->scalarNode('charset')->defaultValue('%kernel.charset%')->end()
->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
->booleanNode('strict_variables')->end()
->scalarNode('auto_reload')->end()
->integerNode('optimizations')->min(-1)->end()
->arrayNode('paths')
->normalizeKeys(false)
->useAttributeAsKey('paths')
->beforeNormalization()
->always()
->then(function ($paths) {
$normalized = array();
foreach ($paths as $path => $namespace) {
if (is_array($namespace)) {
// xml
$path = $namespace['value'];
$namespace = $namespace['namespace'];
}
// path within the default namespace
if (ctype_digit((string) $path)) {
$path = $namespace;
$namespace = null;
}
$normalized[$path] = $namespace;
}
return $normalized;
})
->end()
->prototype('variable')->end()
->end()
->end()
;
}
private function addTwigFormatOptions(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('date')
->info('The default format options used by the date filter')
->addDefaultsIfNotSet()
->children()
->scalarNode('format')->defaultValue('F j, Y H:i')->end()
->scalarNode('interval_format')->defaultValue('%d days')->end()
->scalarNode('timezone')
->info('The timezone used when formatting dates, when set to null, the timezone returned by date_default_timezone_get() is used')
->defaultNull()
->end()
->end()
->end()
->arrayNode('number_format')
->info('The default format options for the number_format filter')
->addDefaultsIfNotSet()
->children()
->integerNode('decimals')->defaultValue(0)->end()
->scalarNode('decimal_point')->defaultValue('.')->end()
->scalarNode('thousands_separator')->defaultValue(',')->end()
->end()
->end()
->end()
;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Configurator;
use Twig\Environment;
// BC/FC with namespaced Twig
class_exists('Twig\Environment');
/**
* Twig environment configurator.
*
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
*/
class EnvironmentConfigurator
{
private $dateFormat;
private $intervalFormat;
private $timezone;
private $decimals;
private $decimalPoint;
private $thousandsSeparator;
public function __construct($dateFormat, $intervalFormat, $timezone, $decimals, $decimalPoint, $thousandsSeparator)
{
$this->dateFormat = $dateFormat;
$this->intervalFormat = $intervalFormat;
$this->timezone = $timezone;
$this->decimals = $decimals;
$this->decimalPoint = $decimalPoint;
$this->thousandsSeparator = $thousandsSeparator;
}
public function configure(Environment $environment)
{
$environment->getExtension('Twig\Extension\CoreExtension')->setDateFormat($this->dateFormat, $this->intervalFormat);
if (null !== $this->timezone) {
$environment->getExtension('Twig\Extension\CoreExtension')->setTimezone($this->timezone);
}
$environment->getExtension('Twig\Extension\CoreExtension')->setNumberFormat($this->decimals, $this->decimalPoint, $this->thousandsSeparator);
}
}

View File

@@ -0,0 +1,227 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Resource\FileExistenceResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* TwigExtension.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class TwigExtension extends Extension
{
/**
* Responds to the twig configuration parameter.
*
* @param array $configs
* @param ContainerBuilder $container
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('twig.xml');
if (class_exists('Symfony\Component\Form\Form')) {
$loader->load('form.xml');
}
if (interface_exists('Symfony\Component\Templating\EngineInterface')) {
$loader->load('templating.xml');
}
if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) {
$container->removeDefinition('twig.translation.extractor');
}
foreach ($configs as $key => $config) {
if (isset($config['globals'])) {
foreach ($config['globals'] as $name => $value) {
if (is_array($value) && isset($value['key'])) {
$configs[$key]['globals'][$name] = array(
'key' => $name,
'value' => $value,
);
}
}
}
}
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('twig.exception_listener.controller', $config['exception_controller']);
$container->setParameter('twig.form.resources', $config['form_themes']);
$envConfiguratorDefinition = $container->getDefinition('twig.configurator.environment');
$envConfiguratorDefinition->replaceArgument(0, $config['date']['format']);
$envConfiguratorDefinition->replaceArgument(1, $config['date']['interval_format']);
$envConfiguratorDefinition->replaceArgument(2, $config['date']['timezone']);
$envConfiguratorDefinition->replaceArgument(3, $config['number_format']['decimals']);
$envConfiguratorDefinition->replaceArgument(4, $config['number_format']['decimal_point']);
$envConfiguratorDefinition->replaceArgument(5, $config['number_format']['thousands_separator']);
$twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.native_filesystem');
// register user-configured paths
foreach ($config['paths'] as $path => $namespace) {
if (!$namespace) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path));
} else {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
}
}
$container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']);
$container->getDefinition('twig.template_iterator')->replaceArgument(2, $config['paths']);
$bundleHierarchy = $this->getBundleHierarchy($container);
foreach ($bundleHierarchy as $name => $bundle) {
$namespace = $this->normalizeBundleName($name);
foreach ($bundle['children'] as $child) {
foreach ($bundleHierarchy[$child]['paths'] as $path) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
}
}
foreach ($bundle['paths'] as $path) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
}
}
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir));
}
$container->addResource(new FileExistenceResource($dir));
if (!empty($config['globals'])) {
$def = $container->getDefinition('twig');
foreach ($config['globals'] as $key => $global) {
if (isset($global['type']) && 'service' === $global['type']) {
$def->addMethodCall('addGlobal', array($key, new Reference($global['id'])));
} else {
$def->addMethodCall('addGlobal', array($key, $global['value']));
}
}
}
unset(
$config['form'],
$config['globals'],
$config['extensions']
);
if (isset($config['autoescape_service']) && isset($config['autoescape_service_method'])) {
$config['autoescape'] = array(new Reference($config['autoescape_service']), $config['autoescape_service_method']);
}
unset($config['autoescape_service'], $config['autoescape_service_method']);
$container->getDefinition('twig')->replaceArgument(1, $config);
$this->addClassesToCompile(array(
'Twig_Environment',
'Twig_Extension',
'Twig_Extension_Core',
'Twig_Extension_Escaper',
'Twig_Extension_Optimizer',
'Twig_LoaderInterface',
'Twig_Markup',
'Twig_Template',
));
}
private function getBundleHierarchy(ContainerBuilder $container)
{
$bundleHierarchy = array();
foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) {
if (!array_key_exists($name, $bundleHierarchy)) {
$bundleHierarchy[$name] = array(
'paths' => array(),
'parents' => array(),
'children' => array(),
);
}
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views')) {
$bundleHierarchy[$name]['paths'][] = $dir;
}
$container->addResource(new FileExistenceResource($dir));
if (is_dir($dir = $bundle['path'].'/Resources/views')) {
$bundleHierarchy[$name]['paths'][] = $dir;
}
$container->addResource(new FileExistenceResource($dir));
if (null === $bundle['parent']) {
continue;
}
$bundleHierarchy[$name]['parents'][] = $bundle['parent'];
if (!array_key_exists($bundle['parent'], $bundleHierarchy)) {
$bundleHierarchy[$bundle['parent']] = array(
'paths' => array(),
'parents' => array(),
'children' => array(),
);
}
$bundleHierarchy[$bundle['parent']]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$bundle['parent']]['children']);
foreach ($bundleHierarchy[$bundle['parent']]['parents'] as $parent) {
$bundleHierarchy[$name]['parents'][] = $parent;
$bundleHierarchy[$parent]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$parent]['children']);
}
foreach ($bundleHierarchy[$name]['children'] as $child) {
$bundleHierarchy[$child]['parents'] = array_merge($bundleHierarchy[$child]['parents'], $bundleHierarchy[$name]['parents']);
}
}
return $bundleHierarchy;
}
private function normalizeBundleName($name)
{
if ('Bundle' === substr($name, -6)) {
$name = substr($name, 0, -6);
}
return $name;
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/twig';
}
}