1<?php 2 3/* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Twig\NodeVisitor; 13 14use Twig\Environment; 15use Twig\Node\CheckSecurityNode; 16use Twig\Node\CheckToStringNode; 17use Twig\Node\Expression\Binary\ConcatBinary; 18use Twig\Node\Expression\Binary\RangeBinary; 19use Twig\Node\Expression\FilterExpression; 20use Twig\Node\Expression\FunctionExpression; 21use Twig\Node\Expression\GetAttrExpression; 22use Twig\Node\Expression\NameExpression; 23use Twig\Node\ModuleNode; 24use Twig\Node\Node; 25use Twig\Node\PrintNode; 26use Twig\Node\SetNode; 27 28/** 29 * @final 30 * 31 * @author Fabien Potencier <fabien@symfony.com> 32 */ 33class SandboxNodeVisitor extends AbstractNodeVisitor 34{ 35 protected $inAModule = false; 36 protected $tags; 37 protected $filters; 38 protected $functions; 39 40 private $needsToStringWrap = false; 41 42 protected function doEnterNode(Node $node, Environment $env) 43 { 44 if ($node instanceof ModuleNode) { 45 $this->inAModule = true; 46 $this->tags = []; 47 $this->filters = []; 48 $this->functions = []; 49 50 return $node; 51 } elseif ($this->inAModule) { 52 // look for tags 53 if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) { 54 $this->tags[$node->getNodeTag()] = $node; 55 } 56 57 // look for filters 58 if ($node instanceof FilterExpression && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) { 59 $this->filters[$node->getNode('filter')->getAttribute('value')] = $node; 60 } 61 62 // look for functions 63 if ($node instanceof FunctionExpression && !isset($this->functions[$node->getAttribute('name')])) { 64 $this->functions[$node->getAttribute('name')] = $node; 65 } 66 67 // the .. operator is equivalent to the range() function 68 if ($node instanceof RangeBinary && !isset($this->functions['range'])) { 69 $this->functions['range'] = $node; 70 } 71 72 if ($node instanceof PrintNode) { 73 $this->needsToStringWrap = true; 74 $this->wrapNode($node, 'expr'); 75 } 76 77 if ($node instanceof SetNode && !$node->getAttribute('capture')) { 78 $this->needsToStringWrap = true; 79 } 80 81 // wrap outer nodes that can implicitly call __toString() 82 if ($this->needsToStringWrap) { 83 if ($node instanceof ConcatBinary) { 84 $this->wrapNode($node, 'left'); 85 $this->wrapNode($node, 'right'); 86 } 87 if ($node instanceof FilterExpression) { 88 $this->wrapNode($node, 'node'); 89 $this->wrapArrayNode($node, 'arguments'); 90 } 91 if ($node instanceof FunctionExpression) { 92 $this->wrapArrayNode($node, 'arguments'); 93 } 94 } 95 } 96 97 return $node; 98 } 99 100 protected function doLeaveNode(Node $node, Environment $env) 101 { 102 if ($node instanceof ModuleNode) { 103 $this->inAModule = false; 104 105 $node->setNode('constructor_end', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('display_start')])); 106 } elseif ($this->inAModule) { 107 if ($node instanceof PrintNode || $node instanceof SetNode) { 108 $this->needsToStringWrap = false; 109 } 110 } 111 112 return $node; 113 } 114 115 private function wrapNode(Node $node, $name) 116 { 117 $expr = $node->getNode($name); 118 if ($expr instanceof NameExpression || $expr instanceof GetAttrExpression) { 119 $node->setNode($name, new CheckToStringNode($expr)); 120 } 121 } 122 123 private function wrapArrayNode(Node $node, $name) 124 { 125 $args = $node->getNode($name); 126 foreach ($args as $name => $_) { 127 $this->wrapNode($args, $name); 128 } 129 } 130 131 public function getPriority() 132 { 133 return 0; 134 } 135} 136 137class_alias('Twig\NodeVisitor\SandboxNodeVisitor', 'Twig_NodeVisitor_Sandbox'); 138