Escaper.php (5150B)
1 <?php 2 3 /* 4 * This file is part of Twig. 5 * 6 * (c) 2009 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 /** 13 * Twig_NodeVisitor_Escaper implements output escaping. 14 * 15 * @author Fabien Potencier <fabien@symfony.com> 16 */ 17 class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor 18 { 19 protected $statusStack = array(); 20 protected $blocks = array(); 21 protected $safeAnalysis; 22 protected $traverser; 23 protected $defaultStrategy = false; 24 protected $safeVars = array(); 25 26 public function __construct() 27 { 28 $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis(); 29 } 30 31 /** 32 * {@inheritdoc} 33 */ 34 protected function doEnterNode(Twig_Node $node, Twig_Environment $env) 35 { 36 if ($node instanceof Twig_Node_Module) { 37 if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) { 38 $this->defaultStrategy = $defaultStrategy; 39 } 40 $this->safeVars = array(); 41 $this->blocks = array(); 42 } elseif ($node instanceof Twig_Node_AutoEscape) { 43 $this->statusStack[] = $node->getAttribute('value'); 44 } elseif ($node instanceof Twig_Node_Block) { 45 $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env); 46 } elseif ($node instanceof Twig_Node_Import) { 47 $this->safeVars[] = $node->getNode('var')->getAttribute('name'); 48 } 49 50 return $node; 51 } 52 53 /** 54 * {@inheritdoc} 55 */ 56 protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) 57 { 58 if ($node instanceof Twig_Node_Module) { 59 $this->defaultStrategy = false; 60 $this->safeVars = array(); 61 $this->blocks = array(); 62 } elseif ($node instanceof Twig_Node_Expression_Filter) { 63 return $this->preEscapeFilterNode($node, $env); 64 } elseif ($node instanceof Twig_Node_Print) { 65 return $this->escapePrintNode($node, $env, $this->needEscaping($env)); 66 } 67 68 if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) { 69 array_pop($this->statusStack); 70 } elseif ($node instanceof Twig_Node_BlockReference) { 71 $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env); 72 } 73 74 return $node; 75 } 76 77 protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type) 78 { 79 if (false === $type) { 80 return $node; 81 } 82 83 $expression = $node->getNode('expr'); 84 85 if ($this->isSafeFor($type, $expression, $env)) { 86 return $node; 87 } 88 89 $class = get_class($node); 90 91 return new $class( 92 $this->getEscaperFilter($type, $expression), 93 $node->getLine() 94 ); 95 } 96 97 protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env) 98 { 99 $name = $filter->getNode('filter')->getAttribute('value'); 100 101 $type = $env->getFilter($name)->getPreEscape(); 102 if (null === $type) { 103 return $filter; 104 } 105 106 $node = $filter->getNode('node'); 107 if ($this->isSafeFor($type, $node, $env)) { 108 return $filter; 109 } 110 111 $filter->setNode('node', $this->getEscaperFilter($type, $node)); 112 113 return $filter; 114 } 115 116 protected function isSafeFor($type, Twig_NodeInterface $expression, $env) 117 { 118 $safe = $this->safeAnalysis->getSafe($expression); 119 120 if (null === $safe) { 121 if (null === $this->traverser) { 122 $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis)); 123 } 124 125 $this->safeAnalysis->setSafeVars($this->safeVars); 126 127 $this->traverser->traverse($expression); 128 $safe = $this->safeAnalysis->getSafe($expression); 129 } 130 131 return in_array($type, $safe) || in_array('all', $safe); 132 } 133 134 protected function needEscaping(Twig_Environment $env) 135 { 136 if (count($this->statusStack)) { 137 return $this->statusStack[count($this->statusStack) - 1]; 138 } 139 140 return $this->defaultStrategy ? $this->defaultStrategy : false; 141 } 142 143 protected function getEscaperFilter($type, Twig_NodeInterface $node) 144 { 145 $line = $node->getLine(); 146 $name = new Twig_Node_Expression_Constant('escape', $line); 147 $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line))); 148 149 return new Twig_Node_Expression_Filter($node, $name, $args, $line); 150 } 151 152 /** 153 * {@inheritdoc} 154 */ 155 public function getPriority() 156 { 157 return 0; 158 } 159 }