vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php line 79

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\NodeVisitor;
  11. use Twig\Environment;
  12. use Twig\Extension\EscaperExtension;
  13. use Twig\Node\AutoEscapeNode;
  14. use Twig\Node\BlockNode;
  15. use Twig\Node\BlockReferenceNode;
  16. use Twig\Node\DoNode;
  17. use Twig\Node\Expression\ConditionalExpression;
  18. use Twig\Node\Expression\ConstantExpression;
  19. use Twig\Node\Expression\FilterExpression;
  20. use Twig\Node\Expression\InlinePrint;
  21. use Twig\Node\ImportNode;
  22. use Twig\Node\ModuleNode;
  23. use Twig\Node\Node;
  24. use Twig\Node\PrintNode;
  25. use Twig\NodeTraverser;
  26. /**
  27.  * @author Fabien Potencier <fabien@symfony.com>
  28.  *
  29.  * @internal
  30.  */
  31. final class EscaperNodeVisitor implements NodeVisitorInterface
  32. {
  33.     private $statusStack = [];
  34.     private $blocks = [];
  35.     private $safeAnalysis;
  36.     private $traverser;
  37.     private $defaultStrategy false;
  38.     private $safeVars = [];
  39.     public function __construct()
  40.     {
  41.         $this->safeAnalysis = new SafeAnalysisNodeVisitor();
  42.     }
  43.     public function enterNode(Node $nodeEnvironment $env): Node
  44.     {
  45.         if ($node instanceof ModuleNode) {
  46.             if ($env->hasExtension(EscaperExtension::class) && $defaultStrategy $env->getExtension(EscaperExtension::class)->getDefaultStrategy($node->getTemplateName())) {
  47.                 $this->defaultStrategy $defaultStrategy;
  48.             }
  49.             $this->safeVars = [];
  50.             $this->blocks = [];
  51.         } elseif ($node instanceof AutoEscapeNode) {
  52.             $this->statusStack[] = $node->getAttribute('value');
  53.         } elseif ($node instanceof BlockNode) {
  54.             $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
  55.         } elseif ($node instanceof ImportNode) {
  56.             $this->safeVars[] = $node->getNode('var')->getAttribute('name');
  57.         }
  58.         return $node;
  59.     }
  60.     public function leaveNode(Node $nodeEnvironment $env): ?Node
  61.     {
  62.         if ($node instanceof ModuleNode) {
  63.             $this->defaultStrategy false;
  64.             $this->safeVars = [];
  65.             $this->blocks = [];
  66.         } elseif ($node instanceof FilterExpression) {
  67.             return $this->preEscapeFilterNode($node$env);
  68.         } elseif ($node instanceof PrintNode && false !== $type $this->needEscaping($env)) {
  69.             $expression $node->getNode('expr');
  70.             if ($expression instanceof ConditionalExpression && $this->shouldUnwrapConditional($expression$env$type)) {
  71.                 return new DoNode($this->unwrapConditional($expression$env$type), $expression->getTemplateLine());
  72.             }
  73.             return $this->escapePrintNode($node$env$type);
  74.         }
  75.         if ($node instanceof AutoEscapeNode || $node instanceof BlockNode) {
  76.             array_pop($this->statusStack);
  77.         } elseif ($node instanceof BlockReferenceNode) {
  78.             $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
  79.         }
  80.         return $node;
  81.     }
  82.     private function shouldUnwrapConditional(ConditionalExpression $expressionEnvironment $envstring $type): bool
  83.     {
  84.         $expr2Safe $this->isSafeFor($type$expression->getNode('expr2'), $env);
  85.         $expr3Safe $this->isSafeFor($type$expression->getNode('expr3'), $env);
  86.         return $expr2Safe !== $expr3Safe;
  87.     }
  88.     private function unwrapConditional(ConditionalExpression $expressionEnvironment $envstring $type): ConditionalExpression
  89.     {
  90.         // convert "echo a ? b : c" to "a ? echo b : echo c" recursively
  91.         $expr2 $expression->getNode('expr2');
  92.         if ($expr2 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr2$env$type)) {
  93.             $expr2 $this->unwrapConditional($expr2$env$type);
  94.         } else {
  95.             $expr2 $this->escapeInlinePrintNode(new InlinePrint($expr2$expr2->getTemplateLine()), $env$type);
  96.         }
  97.         $expr3 $expression->getNode('expr3');
  98.         if ($expr3 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr3$env$type)) {
  99.             $expr3 $this->unwrapConditional($expr3$env$type);
  100.         } else {
  101.             $expr3 $this->escapeInlinePrintNode(new InlinePrint($expr3$expr3->getTemplateLine()), $env$type);
  102.         }
  103.         return new ConditionalExpression($expression->getNode('expr1'), $expr2$expr3$expression->getTemplateLine());
  104.     }
  105.     private function escapeInlinePrintNode(InlinePrint $nodeEnvironment $envstring $type): Node
  106.     {
  107.         $expression $node->getNode('node');
  108.         if ($this->isSafeFor($type$expression$env)) {
  109.             return $node;
  110.         }
  111.         return new InlinePrint($this->getEscaperFilter($type$expression), $node->getTemplateLine());
  112.     }
  113.     private function escapePrintNode(PrintNode $nodeEnvironment $envstring $type): Node
  114.     {
  115.         if (false === $type) {
  116.             return $node;
  117.         }
  118.         $expression $node->getNode('expr');
  119.         if ($this->isSafeFor($type$expression$env)) {
  120.             return $node;
  121.         }
  122.         $class = \get_class($node);
  123.         return new $class($this->getEscaperFilter($type$expression), $node->getTemplateLine());
  124.     }
  125.     private function preEscapeFilterNode(FilterExpression $filterEnvironment $env): FilterExpression
  126.     {
  127.         $name $filter->getNode('filter')->getAttribute('value');
  128.         $type $env->getFilter($name)->getPreEscape();
  129.         if (null === $type) {
  130.             return $filter;
  131.         }
  132.         $node $filter->getNode('node');
  133.         if ($this->isSafeFor($type$node$env)) {
  134.             return $filter;
  135.         }
  136.         $filter->setNode('node'$this->getEscaperFilter($type$node));
  137.         return $filter;
  138.     }
  139.     private function isSafeFor(string $typeNode $expressionEnvironment $env): bool
  140.     {
  141.         $safe $this->safeAnalysis->getSafe($expression);
  142.         if (null === $safe) {
  143.             if (null === $this->traverser) {
  144.                 $this->traverser = new NodeTraverser($env, [$this->safeAnalysis]);
  145.             }
  146.             $this->safeAnalysis->setSafeVars($this->safeVars);
  147.             $this->traverser->traverse($expression);
  148.             $safe $this->safeAnalysis->getSafe($expression);
  149.         }
  150.         return \in_array($type$safe) || \in_array('all'$safe);
  151.     }
  152.     private function needEscaping(Environment $env)
  153.     {
  154.         if (\count($this->statusStack)) {
  155.             return $this->statusStack[\count($this->statusStack) - 1];
  156.         }
  157.         return $this->defaultStrategy $this->defaultStrategy false;
  158.     }
  159.     private function getEscaperFilter(string $typeNode $node): FilterExpression
  160.     {
  161.         $line $node->getTemplateLine();
  162.         $name = new ConstantExpression('escape'$line);
  163.         $args = new Node([new ConstantExpression($type$line), new ConstantExpression(null$line), new ConstantExpression(true$line)]);
  164.         return new FilterExpression($node$name$args$line);
  165.     }
  166.     public function getPriority(): int
  167.     {
  168.         return 0;
  169.     }
  170. }