TwigExtension.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Bundle\TwigBundle\DependencyInjection;
  11. use Symfony\Component\Config\FileLocator;
  12. use Symfony\Component\Config\Resource\FileExistenceResource;
  13. use Symfony\Component\Console\Application;
  14. use Symfony\Component\DependencyInjection\ContainerBuilder;
  15. use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
  16. use Symfony\Component\DependencyInjection\Reference;
  17. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  18. use Symfony\Component\Mailer\Mailer;
  19. use Symfony\Component\Translation\Translator;
  20. use Twig\Extension\ExtensionInterface;
  21. use Twig\Extension\RuntimeExtensionInterface;
  22. use Twig\Loader\LoaderInterface;
  23. /**
  24. * TwigExtension.
  25. *
  26. * @author Fabien Potencier <fabien@symfony.com>
  27. * @author Jeremy Mikola <jmikola@gmail.com>
  28. */
  29. class TwigExtension extends Extension
  30. {
  31. public function load(array $configs, ContainerBuilder $container)
  32. {
  33. $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
  34. $loader->load('twig.php');
  35. if (class_exists(\Symfony\Component\Form\Form::class)) {
  36. $loader->load('form.php');
  37. }
  38. if (class_exists(Application::class)) {
  39. $loader->load('console.php');
  40. }
  41. if (class_exists(Mailer::class)) {
  42. $loader->load('mailer.php');
  43. }
  44. if (!class_exists(Translator::class)) {
  45. $container->removeDefinition('twig.translation.extractor');
  46. }
  47. foreach ($configs as $key => $config) {
  48. if (isset($config['globals'])) {
  49. foreach ($config['globals'] as $name => $value) {
  50. if (\is_array($value) && isset($value['key'])) {
  51. $configs[$key]['globals'][$name] = [
  52. 'key' => $name,
  53. 'value' => $value,
  54. ];
  55. }
  56. }
  57. }
  58. }
  59. $configuration = $this->getConfiguration($configs, $container);
  60. $config = $this->processConfiguration($configuration, $configs);
  61. $container->setParameter('twig.form.resources', $config['form_themes']);
  62. $container->setParameter('twig.default_path', $config['default_path']);
  63. $defaultTwigPath = $container->getParameterBag()->resolveValue($config['default_path']);
  64. $envConfiguratorDefinition = $container->getDefinition('twig.configurator.environment');
  65. $envConfiguratorDefinition->replaceArgument(0, $config['date']['format']);
  66. $envConfiguratorDefinition->replaceArgument(1, $config['date']['interval_format']);
  67. $envConfiguratorDefinition->replaceArgument(2, $config['date']['timezone']);
  68. $envConfiguratorDefinition->replaceArgument(3, $config['number_format']['decimals']);
  69. $envConfiguratorDefinition->replaceArgument(4, $config['number_format']['decimal_point']);
  70. $envConfiguratorDefinition->replaceArgument(5, $config['number_format']['thousands_separator']);
  71. $twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.native_filesystem');
  72. // register user-configured paths
  73. foreach ($config['paths'] as $path => $namespace) {
  74. if (!$namespace) {
  75. $twigFilesystemLoaderDefinition->addMethodCall('addPath', [$path]);
  76. } else {
  77. $twigFilesystemLoaderDefinition->addMethodCall('addPath', [$path, $namespace]);
  78. }
  79. }
  80. // paths are modified in ExtensionPass if forms are enabled
  81. $container->getDefinition('twig.template_iterator')->replaceArgument(1, $config['paths']);
  82. foreach ($this->getBundleTemplatePaths($container, $config) as $name => $paths) {
  83. $namespace = $this->normalizeBundleName($name);
  84. foreach ($paths as $path) {
  85. $twigFilesystemLoaderDefinition->addMethodCall('addPath', [$path, $namespace]);
  86. }
  87. if ($paths) {
  88. // the last path must be the bundle views directory
  89. $twigFilesystemLoaderDefinition->addMethodCall('addPath', [$path, '!'.$namespace]);
  90. }
  91. }
  92. if (file_exists($defaultTwigPath)) {
  93. $twigFilesystemLoaderDefinition->addMethodCall('addPath', [$defaultTwigPath]);
  94. }
  95. $container->addResource(new FileExistenceResource($defaultTwigPath));
  96. if (!empty($config['globals'])) {
  97. $def = $container->getDefinition('twig');
  98. foreach ($config['globals'] as $key => $global) {
  99. if (isset($global['type']) && 'service' === $global['type']) {
  100. $def->addMethodCall('addGlobal', [$key, new Reference($global['id'])]);
  101. } else {
  102. $def->addMethodCall('addGlobal', [$key, $global['value']]);
  103. }
  104. }
  105. }
  106. if (isset($config['autoescape_service']) && isset($config['autoescape_service_method'])) {
  107. $config['autoescape'] = [new Reference($config['autoescape_service']), $config['autoescape_service_method']];
  108. }
  109. $container->getDefinition('twig')->replaceArgument(1, array_intersect_key($config, [
  110. 'debug' => true,
  111. 'charset' => true,
  112. 'base_template_class' => true,
  113. 'strict_variables' => true,
  114. 'autoescape' => true,
  115. 'cache' => true,
  116. 'auto_reload' => true,
  117. 'optimizations' => true,
  118. ]));
  119. $container->registerForAutoconfiguration(\Twig_ExtensionInterface::class)->addTag('twig.extension');
  120. $container->registerForAutoconfiguration(\Twig_LoaderInterface::class)->addTag('twig.loader');
  121. $container->registerForAutoconfiguration(ExtensionInterface::class)->addTag('twig.extension');
  122. $container->registerForAutoconfiguration(LoaderInterface::class)->addTag('twig.loader');
  123. $container->registerForAutoconfiguration(RuntimeExtensionInterface::class)->addTag('twig.runtime');
  124. if (false === $config['cache']) {
  125. $container->removeDefinition('twig.template_cache_warmer');
  126. }
  127. }
  128. private function getBundleTemplatePaths(ContainerBuilder $container, array $config): array
  129. {
  130. $bundleHierarchy = [];
  131. foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) {
  132. $defaultOverrideBundlePath = $container->getParameterBag()->resolveValue($config['default_path']).'/bundles/'.$name;
  133. if (file_exists($defaultOverrideBundlePath)) {
  134. $bundleHierarchy[$name][] = $defaultOverrideBundlePath;
  135. }
  136. $container->addResource(new FileExistenceResource($defaultOverrideBundlePath));
  137. if (file_exists($dir = $bundle['path'].'/Resources/views') || file_exists($dir = $bundle['path'].'/templates')) {
  138. $bundleHierarchy[$name][] = $dir;
  139. }
  140. $container->addResource(new FileExistenceResource($dir));
  141. }
  142. return $bundleHierarchy;
  143. }
  144. private function normalizeBundleName(string $name): string
  145. {
  146. if ('Bundle' === substr($name, -6)) {
  147. $name = substr($name, 0, -6);
  148. }
  149. return $name;
  150. }
  151. /**
  152. * {@inheritdoc}
  153. */
  154. public function getXsdValidationBasePath()
  155. {
  156. return __DIR__.'/../Resources/config/schema';
  157. }
  158. public function getNamespace()
  159. {
  160. return 'http://symfony.com/schema/dic/twig';
  161. }
  162. }