Project

General

Profile

1
<?php
2

    
3
class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
4
{
5
    protected $data = array();
6

    
7
    public function getSafe(Twig_NodeInterface $node)
8
    {
9
        $hash = spl_object_hash($node);
10
        if (isset($this->data[$hash])) {
11
            foreach($this->data[$hash] as $bucket) {
12
                if ($bucket['key'] === $node) {
13
                    return $bucket['value'];
14
                }
15
            }
16
        }
17

    
18
        return null;
19
    }
20

    
21
    protected function setSafe(Twig_NodeInterface $node, array $safe)
22
    {
23
        $hash = spl_object_hash($node);
24
        if (isset($this->data[$hash])) {
25
            foreach($this->data[$hash] as &$bucket) {
26
                if ($bucket['key'] === $node) {
27
                    $bucket['value'] = $safe;
28

    
29
                    return;
30
                }
31
            }
32
        }
33
        $this->data[$hash][] = array(
34
            'key' => $node,
35
            'value' => $safe,
36
        );
37
    }
38

    
39
    public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
40
    {
41
        return $node;
42
    }
43

    
44
    public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
45
    {
46
        if ($node instanceof Twig_Node_Expression_Constant) {
47
            // constants are marked safe for all
48
            $this->setSafe($node, array('all'));
49
        } elseif ($node instanceof Twig_Node_Expression_BlockReference) {
50
            // blocks are safe by definition
51
            $this->setSafe($node, array('all'));
52
        } elseif ($node instanceof Twig_Node_Expression_Parent) {
53
            // parent block is safe by definition
54
            $this->setSafe($node, array('all'));
55
        } elseif ($node instanceof Twig_Node_Expression_Conditional) {
56
            // intersect safeness of both operands
57
            $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3')));
58
            $this->setSafe($node, $safe);
59
        } elseif ($node instanceof Twig_Node_Expression_Filter) {
60
            // filter expression is safe when the filter is safe
61
            $name = $node->getNode('filter')->getAttribute('value');
62
            $args = $node->getNode('arguments');
63
            if (false !== $filter = $env->getFilter($name)) {
64
                $safe = $filter->getSafe($args);
65
                if (null === $safe) {
66
                    $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety());
67
                }
68
                $this->setSafe($node, $safe);
69
            } else {
70
                $this->setSafe($node, array());
71
            }
72
        } elseif ($node instanceof Twig_Node_Expression_Function) {
73
            // function expression is safe when the function is safe
74
            $name = $node->getAttribute('name');
75
            $args = $node->getNode('arguments');
76
            $function = $env->getFunction($name);
77
            if (false !== $function) {
78
                $this->setSafe($node, $function->getSafe($args));
79
            } else {
80
                $this->setSafe($node, array());
81
            }
82
        } elseif ($node instanceof Twig_Node_Expression_MethodCall) {
83
            if ($node->getAttribute('safe')) {
84
                $this->setSafe($node, array('all'));
85
            } else {
86
                $this->setSafe($node, array());
87
            }
88
        } else {
89
            $this->setSafe($node, array());
90
        }
91

    
92
        return $node;
93
    }
94

    
95
    protected function intersectSafe(array $a = null, array $b = null)
96
    {
97
        if (null === $a || null === $b) {
98
            return array();
99
        }
100

    
101
        if (in_array('all', $a)) {
102
            return $b;
103
        }
104

    
105
        if (in_array('all', $b)) {
106
            return $a;
107
        }
108

    
109
        return array_intersect($a, $b);
110
    }
111

    
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function getPriority()
116
    {
117
        return 0;
118
    }
119
}
(3-3/5)