Completed
Push — master ( c5db3b...824ea2 )
by Nikola
12:14
created

NodeVisitor::hasBufferizingNode()   C

Complexity

Conditions 9
Paths 6

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 23
rs 5.8543
cc 9
eloc 12
nc 6
nop 1
1
<?php
2
/*
3
 * This file is part of the Twig Bufferized Template package, an RunOpenCode project.
4
 *
5
 * (c) 2015 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace RunOpenCode\Twig\BufferizedTemplate;
11
12
use RunOpenCode\Twig\BufferizedTemplate\Tag\Bufferize\Node as BufferizeNode;
13
use RunOpenCode\Twig\BufferizedTemplate\Tag\TemplateBuffer\BaseBufferNode;
14
use RunOpenCode\Twig\BufferizedTemplate\Tag\TemplateBuffer\BufferBreakPoint;
15
use RunOpenCode\Twig\BufferizedTemplate\Tag\TemplateBuffer\Initialize;
16
use RunOpenCode\Twig\BufferizedTemplate\Tag\TemplateBuffer\Terminate;
17
18
/**
19
 * Class NodeVisitor
20
 *
21
 * Parses AST adding buffering tags on required templates.
22
 *
23
 * @package RunOpenCode\Twig\BufferizedTemplate
24
 */
25
class NodeVisitor extends \Twig_BaseNodeVisitor
26
{
27
    /**
28
     * @var array
29
     */
30
    private $settings;
31
32
    /**
33
     * @var string Current template name.
34
     */
35
    protected $filename;
36
37
    /**
38
     * @var bool Denotes if current template body should be bufferized.
39
     */
40
    private $shouldBufferize = false;
41
42
    /**
43
     * @var string Denotes current scope of AST (block or body).
44
     */
45
    private $currentScope;
46
47
    /**
48
     * @var \Twig_Node[] List of blocks for current template.
49
     */
50
    private $blocks;
51
52
    public function __construct(array $settings)
53
    {
54
        $this->settings = $settings;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env)
61
    {
62
        if ($node instanceof \Twig_Node_Module) {
63
            $this->filename = $node->getAttribute('filename');
64
        }
65
66
        if ($this->shouldProcess()) {
67
68
            if ($this->isBufferizingNode($node)) {
69
                $this->shouldBufferize = true;
70
            }
71
72
            if ($node instanceof \Twig_Node_Module) {
73
                $this->blocks = $node->getNode('blocks')->getIterator()->getArrayCopy();
74
            }
75
76
            if ($node instanceof \Twig_Node_Body) {
77
                $this->currentScope = null;
78
            } elseif ($node instanceof \Twig_Node_Block) {
79
                $this->currentScope = $node->getAttribute('name');
80
            }
81
        }
82
83
        return $node;
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env)
90
    {
91
        if ($node instanceof \Twig_Node_Module) {
92
            $this->filename = null;
93
        }
94
95
        if ($this->shouldProcess()) {
96
97
            if ($node instanceof \Twig_Node_Module) {
98
99
                if ($this->shouldBufferize) {
100
101
                    $node->setNode('body', new \Twig_Node(array(
102
                        new Initialize($this->settings['defaultExecutionPriority']),
103
                        $node->getNode('body'),
104
                        new Terminate($this->settings['defaultExecutionPriority'])
105
                    )));
106
                }
107
108
                $this->shouldBufferize = false;
109
                $this->blocks = array();
110
            }
111
112
            if ($this->isBufferizingNode($node) || ($node instanceof \Twig_Node_BlockReference && $this->hasBufferizingNode($this->blocks[$node->getAttribute('name')]))) {
113
114
                return new \Twig_Node(array(
115
                    new BufferBreakPoint($this->settings['defaultExecutionPriority']),
116
                    $node,
117
                    new BufferBreakPoint($this->settings['defaultExecutionPriority'], array(), array(BaseBufferNode::BUFFERIZED_EXECUTION_PRIORITY_ATTRIBUTE_NAME => $this->getNodeExecutionPriority($node)))
118
                ));
119
120
            } elseif ($this->currentScope && $node instanceof \Twig_Node_Block && $this->hasBufferizingNode($node)) {
121
122
                $node->setNode('body', new \Twig_Node(array(
123
                    new Initialize($this->settings['defaultExecutionPriority']),
124
                    $node->getNode('body'),
125
                    new Terminate($this->settings['defaultExecutionPriority'])
126
                )));
127
128
                return $node;
129
            }
130
131
        }
132
133
        return $node;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139
    public function getPriority()
140
    {
141
        return $this->settings['nodeVisitorPriority'];
142
    }
143
144
145
    /**
146
     * Check if current template should be processed with node visitor based on whitelist or blacklist.
147
     *
148
     * @return bool
149
     */
150
    protected function shouldProcess()
151
    {
152
        if (count($this->settings['whitelist']) == 0 && count($this->settings['blacklist']) == 0) {
153
            return true;
154
        } elseif (count($this->settings['whitelist']) > 0) {
155
            return in_array($this->filename, $this->settings['whitelist']);
156
        } else {
157
            return !in_array($this->filename, $this->settings['blacklist']);
158
        }
159
    }
160
161
    /**
162
     * Check if provided node is node for bufferizing.
163
     *
164
     * @param \Twig_Node $node
165
     * @return bool
166
     */
167
    protected function isBufferizingNode(\Twig_Node $node = null)
168
    {
169
        if (is_null($node)) {
170
171
            return false;
172
173
        } else {
174
175
            foreach ($this->settings['nodes'] as $nodeClass => $priority) {
176
177
                if (is_a($node, $nodeClass)) {
178
                    return true;
179
                }
180
            }
181
182
        }
183
184
        return false;
185
    }
186
187
    /**
188
     * Checks if current node is asset injection node, or if such node exists in its sub-tree.
189
     *
190
     * @param \Twig_Node $node Node to check.
191
     * @return bool TRUE if this subtree has bufferizing node.
192
     */
193
    protected function hasBufferizingNode(\Twig_Node $node = null)
194
    {
195
        if (is_null($node)) {
196
            return false;
197
        }
198
199
        if ($this->isBufferizingNode($node)) {
200
            return true;
201
        }
202
203
        $has = false;
204
205
        foreach ($node as $k => $n) {
206
207
            if ($this->isBufferizingNode($n) || ($n instanceof \Twig_Node_BlockReference && $this->hasBufferizingNode($this->blocks[$n->getAttribute('name')]))) {
208
                return true;
209
            } elseif (($has = $has || $this->hasBufferizingNode($n))) {
0 ignored issues
show
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $has = ($has || $this->hasBufferizingNode($n)), Probably Intended Meaning: ($has = $has) || $this->hasBufferizingNode($n)
Loading history...
210
                return true;
211
            }
212
        }
213
214
        return $has;
215
    }
216
217
    /**
218
     * Get execution priority of bufferized node.
219
     *
220
     * Get execution priority of bufferized node based on the node settings or configuration of the extension.
221
     *
222
     * @param \Twig_Node $node
223
     * @return mixed
224
     */
225
    protected function getNodeExecutionPriority(\Twig_Node $node)
226
    {
227
        if ($node instanceof BufferizeNode && !is_null($node->getPriority())) {
228
            return $node->getPriority();
229
        }
230
231
        foreach ($this->settings['nodes'] as $nodeClass => $priority) {
232
233
            if (is_a($node, $nodeClass) && !is_null($priority)) {
234
                return $priority;
235
            }
236
        }
237
238
        return $this->settings['defaultExecutionPriority'];
239
    }
240
}