SafeAnalysis.php (5001B)
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 12 class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor 13 { 14 protected $data = array(); 15 protected $safeVars = array(); 16 17 public function setSafeVars($safeVars) 18 { 19 $this->safeVars = $safeVars; 20 } 21 22 public function getSafe(Twig_NodeInterface $node) 23 { 24 $hash = spl_object_hash($node); 25 if (!isset($this->data[$hash])) { 26 return; 27 } 28 29 foreach ($this->data[$hash] as $bucket) { 30 if ($bucket['key'] !== $node) { 31 continue; 32 } 33 34 if (in_array('html_attr', $bucket['value'])) { 35 $bucket['value'][] = 'html'; 36 } 37 38 return $bucket['value']; 39 } 40 } 41 42 protected function setSafe(Twig_NodeInterface $node, array $safe) 43 { 44 $hash = spl_object_hash($node); 45 if (isset($this->data[$hash])) { 46 foreach ($this->data[$hash] as &$bucket) { 47 if ($bucket['key'] === $node) { 48 $bucket['value'] = $safe; 49 50 return; 51 } 52 } 53 } 54 $this->data[$hash][] = array( 55 'key' => $node, 56 'value' => $safe, 57 ); 58 } 59 60 /** 61 * {@inheritdoc} 62 */ 63 protected function doEnterNode(Twig_Node $node, Twig_Environment $env) 64 { 65 return $node; 66 } 67 68 /** 69 * {@inheritdoc} 70 */ 71 protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) 72 { 73 if ($node instanceof Twig_Node_Expression_Constant) { 74 // constants are marked safe for all 75 $this->setSafe($node, array('all')); 76 } elseif ($node instanceof Twig_Node_Expression_BlockReference) { 77 // blocks are safe by definition 78 $this->setSafe($node, array('all')); 79 } elseif ($node instanceof Twig_Node_Expression_Parent) { 80 // parent block is safe by definition 81 $this->setSafe($node, array('all')); 82 } elseif ($node instanceof Twig_Node_Expression_Conditional) { 83 // intersect safeness of both operands 84 $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); 85 $this->setSafe($node, $safe); 86 } elseif ($node instanceof Twig_Node_Expression_Filter) { 87 // filter expression is safe when the filter is safe 88 $name = $node->getNode('filter')->getAttribute('value'); 89 $args = $node->getNode('arguments'); 90 if (false !== $filter = $env->getFilter($name)) { 91 $safe = $filter->getSafe($args); 92 if (null === $safe) { 93 $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); 94 } 95 $this->setSafe($node, $safe); 96 } else { 97 $this->setSafe($node, array()); 98 } 99 } elseif ($node instanceof Twig_Node_Expression_Function) { 100 // function expression is safe when the function is safe 101 $name = $node->getAttribute('name'); 102 $args = $node->getNode('arguments'); 103 $function = $env->getFunction($name); 104 if (false !== $function) { 105 $this->setSafe($node, $function->getSafe($args)); 106 } else { 107 $this->setSafe($node, array()); 108 } 109 } elseif ($node instanceof Twig_Node_Expression_MethodCall) { 110 if ($node->getAttribute('safe')) { 111 $this->setSafe($node, array('all')); 112 } else { 113 $this->setSafe($node, array()); 114 } 115 } elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) { 116 $name = $node->getNode('node')->getAttribute('name'); 117 // attributes on template instances are safe 118 if ('_self' == $name || in_array($name, $this->safeVars)) { 119 $this->setSafe($node, array('all')); 120 } else { 121 $this->setSafe($node, array()); 122 } 123 } else { 124 $this->setSafe($node, array()); 125 } 126 127 return $node; 128 } 129 130 protected function intersectSafe(array $a = null, array $b = null) 131 { 132 if (null === $a || null === $b) { 133 return array(); 134 } 135 136 if (in_array('all', $a)) { 137 return $b; 138 } 139 140 if (in_array('all', $b)) { 141 return $a; 142 } 143 144 return array_intersect($a, $b); 145 } 146 147 /** 148 * {@inheritdoc} 149 */ 150 public function getPriority() 151 { 152 return 0; 153 } 154 }