vendor/twig/twig/src/Extension/EscaperExtension.php line 179

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Twig.
  4. *
  5. * (c) Fabien Potencier
  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 Twig\Extension;
  11. use Twig\Environment;
  12. use Twig\FileExtensionEscapingStrategy;
  13. use Twig\Node\Expression\ConstantExpression;
  14. use Twig\Node\Expression\Filter\EscapeFilter;
  15. use Twig\Node\Expression\Filter\RawFilter;
  16. use Twig\Node\Node;
  17. use Twig\NodeVisitor\EscaperNodeVisitor;
  18. use Twig\Runtime\EscaperRuntime;
  19. use Twig\TokenParser\AutoEscapeTokenParser;
  20. use Twig\TwigFilter;
  21. final class EscaperExtension extends AbstractExtension
  22. {
  23. private $environment;
  24. private $escapers = [];
  25. private $escaper;
  26. private $defaultStrategy;
  27. /**
  28. * @param string|false|callable $defaultStrategy An escaping strategy
  29. *
  30. * @see setDefaultStrategy()
  31. */
  32. public function __construct($defaultStrategy = 'html')
  33. {
  34. $this->setDefaultStrategy($defaultStrategy);
  35. }
  36. public function getTokenParsers(): array
  37. {
  38. return [new AutoEscapeTokenParser()];
  39. }
  40. public function getNodeVisitors(): array
  41. {
  42. return [new EscaperNodeVisitor()];
  43. }
  44. public function getFilters(): array
  45. {
  46. return [
  47. new TwigFilter('escape', [EscaperRuntime::class, 'escape'], ['is_safe_callback' => [self::class, 'escapeFilterIsSafe'], 'node_class' => EscapeFilter::class]),
  48. new TwigFilter('e', [EscaperRuntime::class, 'escape'], ['is_safe_callback' => [self::class, 'escapeFilterIsSafe'], 'node_class' => EscapeFilter::class]),
  49. new TwigFilter('raw', null, ['is_safe' => ['all'], 'node_class' => RawFilter::class]),
  50. ];
  51. }
  52. public function getLastModified(): int
  53. {
  54. return max(
  55. parent::getLastModified(),
  56. filemtime((new \ReflectionClass(EscaperRuntime::class))->getFileName()),
  57. );
  58. }
  59. /**
  60. * @deprecated since Twig 3.10
  61. */
  62. public function setEnvironment(Environment $environment): void
  63. {
  64. $triggerDeprecation = \func_num_args() > 1 ? func_get_arg(1) : true;
  65. if ($triggerDeprecation) {
  66. trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated and not needed if you are using methods from "Twig\Runtime\EscaperRuntime".', __METHOD__);
  67. }
  68. $this->environment = $environment;
  69. $this->escaper = null;
  70. }
  71. /**
  72. * @deprecated since Twig 3.10
  73. */
  74. public function setEscaperRuntime(EscaperRuntime $escaper): void
  75. {
  76. trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated and not needed if you are using methods from "Twig\Runtime\EscaperRuntime".', __METHOD__);
  77. $this->escaper = $escaper;
  78. }
  79. /**
  80. * Sets the default strategy to use when not defined by the user.
  81. *
  82. * The strategy can be a valid PHP callback that takes the template
  83. * name as an argument and returns the strategy to use.
  84. *
  85. * @param string|false|callable(string $templateName): string $defaultStrategy An escaping strategy
  86. */
  87. public function setDefaultStrategy($defaultStrategy): void
  88. {
  89. if ('name' === $defaultStrategy) {
  90. $defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess'];
  91. }
  92. $this->defaultStrategy = $defaultStrategy;
  93. }
  94. /**
  95. * Gets the default strategy to use when not defined by the user.
  96. *
  97. * @param string $name The template name
  98. *
  99. * @return string|false The default strategy to use for the template
  100. */
  101. public function getDefaultStrategy(string $name)
  102. {
  103. // disable string callables to avoid calling a function named html or js,
  104. // or any other upcoming escaping strategy
  105. if (!\is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
  106. return \call_user_func($this->defaultStrategy, $name);
  107. }
  108. return $this->defaultStrategy;
  109. }
  110. /**
  111. * Defines a new escaper to be used via the escape filter.
  112. *
  113. * @param string $strategy The strategy name that should be used as a strategy in the escape call
  114. * @param callable(Environment, string, string): string $callable A valid PHP callable
  115. *
  116. * @deprecated since Twig 3.10
  117. */
  118. public function setEscaper($strategy, callable $callable): void
  119. {
  120. trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::setEscaper()" method instead (be warned that Environment is not passed anymore to the callable).', __METHOD__);
  121. $escaper = $this->getEscaper(__METHOD__);
  122. $this->escapers[$strategy] = $callable;
  123. $callable = function ($string, $charset) use ($callable) {
  124. return $callable($this->environment, $string, $charset);
  125. };
  126. $escaper->setEscaper($strategy, $callable);
  127. }
  128. /**
  129. * Gets all defined escapers.
  130. *
  131. * @return array<string, callable(Environment, string, string): string> An array of escapers
  132. *
  133. * @deprecated since Twig 3.10
  134. */
  135. public function getEscapers()
  136. {
  137. trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::getEscaper()" method instead.', __METHOD__);
  138. return $this->escapers;
  139. }
  140. /**
  141. * @deprecated since Twig 3.10
  142. */
  143. public function setSafeClasses(array $safeClasses = []): void
  144. {
  145. trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::setSafeClasses()" method instead.', __METHOD__);
  146. $this->getEscaper(__METHOD__)->setSafeClasses($safeClasses);
  147. }
  148. /**
  149. * @deprecated since Twig 3.10
  150. */
  151. public function addSafeClass(string $class, array $strategies): void
  152. {
  153. trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::addSafeClass()" method instead.', __METHOD__);
  154. $this->getEscaper(__METHOD__)->addSafeClass($class, $strategies);
  155. }
  156. private function getEscaper(string $fromMethod): EscaperRuntime
  157. {
  158. if (isset($this->escaper)) {
  159. return $this->escaper;
  160. }
  161. if (isset($this->environment)) {
  162. return $this->escaper = $this->environment->getRuntime(EscaperRuntime::class);
  163. }
  164. throw new \LogicException(\sprintf('You must call "setEnvironment()" before calling "%s()".', $fromMethod));
  165. }
  166. /**
  167. * @internal
  168. *
  169. * @return array<string>
  170. */
  171. public static function escapeFilterIsSafe(Node $filterArgs)
  172. {
  173. foreach ($filterArgs as $arg) {
  174. if ($arg instanceof ConstantExpression) {
  175. return [$arg->getAttribute('value')];
  176. }
  177. return [];
  178. }
  179. return ['html'];
  180. }
  181. }