Twig-1.3.0/lib/Twig/NodeVisitor/Escaper.php
changeset 4 9a001a04b634
equal deleted inserted replaced
3:6d109e3804ac 4:9a001a04b634
       
     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  * @package    twig
       
    16  * @author     Fabien Potencier <fabien@symfony.com>
       
    17  */
       
    18 class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
       
    19 {
       
    20     protected $statusStack = array();
       
    21     protected $blocks = array();
       
    22 
       
    23     protected $safeAnalysis;
       
    24     protected $traverser;
       
    25 
       
    26     function __construct()
       
    27     {
       
    28         $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis();
       
    29     }
       
    30 
       
    31     /**
       
    32      * Called before child nodes are visited.
       
    33      *
       
    34      * @param Twig_NodeInterface $node The node to visit
       
    35      * @param Twig_Environment   $env  The Twig environment instance
       
    36      *
       
    37      * @param Twig_NodeInterface The modified node
       
    38      */
       
    39     public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
       
    40     {
       
    41         if ($node instanceof Twig_Node_AutoEscape) {
       
    42             $this->statusStack[] = $node->getAttribute('value');
       
    43         } elseif ($node instanceof Twig_Node_Block) {
       
    44             $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
       
    45         }
       
    46 
       
    47         return $node;
       
    48     }
       
    49 
       
    50     /**
       
    51      * Called after child nodes are visited.
       
    52      *
       
    53      * @param Twig_NodeInterface $node The node to visit
       
    54      * @param Twig_Environment   $env  The Twig environment instance
       
    55      *
       
    56      * @param Twig_NodeInterface The modified node
       
    57      */
       
    58     public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
       
    59     {
       
    60         if ($node instanceof Twig_Node_Expression_Filter) {
       
    61             return $this->preEscapeFilterNode($node, $env);
       
    62         } elseif ($node instanceof Twig_Node_Print) {
       
    63             return $this->escapePrintNode($node, $env, $this->needEscaping($env));
       
    64         }
       
    65 
       
    66         if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) {
       
    67             array_pop($this->statusStack);
       
    68         } elseif ($node instanceof Twig_Node_BlockReference) {
       
    69             $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
       
    70         }
       
    71 
       
    72         return $node;
       
    73     }
       
    74 
       
    75     protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type)
       
    76     {
       
    77         if (false === $type) {
       
    78             return $node;
       
    79         }
       
    80 
       
    81         $expression = $node->getNode('expr');
       
    82 
       
    83         if ($this->isSafeFor($type, $expression, $env)) {
       
    84             return $node;
       
    85         }
       
    86 
       
    87         $class = get_class($node);
       
    88 
       
    89         return new $class(
       
    90             $this->getEscaperFilter($type, $expression),
       
    91             $node->getLine()
       
    92         );
       
    93     }
       
    94 
       
    95     protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env)
       
    96     {
       
    97         $name = $filter->getNode('filter')->getAttribute('value');
       
    98 
       
    99         if (false !== $f = $env->getFilter($name)) {
       
   100             $type = $f->getPreEscape();
       
   101             if (null === $type) {
       
   102                 return $filter;
       
   103             }
       
   104 
       
   105             $node = $filter->getNode('node');
       
   106             if ($this->isSafeFor($type, $node, $env)) {
       
   107                 return $filter;
       
   108             }
       
   109 
       
   110             $filter->setNode('node', $this->getEscaperFilter($type, $node));
       
   111 
       
   112             return $filter;
       
   113         }
       
   114 
       
   115         return $filter;
       
   116     }
       
   117 
       
   118     protected function isSafeFor($type, Twig_NodeInterface $expression, $env)
       
   119     {
       
   120         $safe = $this->safeAnalysis->getSafe($expression);
       
   121 
       
   122         if (null === $safe) {
       
   123             if (null === $this->traverser) {
       
   124                 $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis));
       
   125             }
       
   126             $this->traverser->traverse($expression);
       
   127             $safe = $this->safeAnalysis->getSafe($expression);
       
   128         }
       
   129 
       
   130         return in_array($type, $safe) || in_array('all', $safe);
       
   131     }
       
   132 
       
   133     protected function needEscaping(Twig_Environment $env)
       
   134     {
       
   135         if (count($this->statusStack)) {
       
   136             return $this->statusStack[count($this->statusStack) - 1];
       
   137         }
       
   138 
       
   139         if ($env->hasExtension('escaper') && $env->getExtension('escaper')->isGlobal()) {
       
   140             return 'html';
       
   141         }
       
   142 
       
   143         return false;
       
   144     }
       
   145 
       
   146     protected function getEscaperFilter($type, Twig_NodeInterface $node)
       
   147     {
       
   148         $line = $node->getLine();
       
   149         $name = new Twig_Node_Expression_Constant('escape', $line);
       
   150         $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)));
       
   151         return new Twig_Node_Expression_Filter($node, $name, $args, $line);
       
   152     }
       
   153 
       
   154     /**
       
   155      * {@inheritdoc}
       
   156      */
       
   157     public function getPriority()
       
   158     {
       
   159         return 0;
       
   160     }
       
   161 }