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
12use Twig\Environment;
13use Twig\Node\ForNode;
14use Twig\Source;
15
16class Twig_Tests_NodeVisitor_OptimizerTest extends \PHPUnit\Framework\TestCase
17{
18    public function testRenderBlockOptimizer()
19    {
20        $env = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock(), ['cache' => false, 'autoescape' => false]);
21
22        $stream = $env->parse($env->tokenize(new Source('{{ block("foo") }}', 'index')));
23
24        $node = $stream->getNode('body')->getNode(0);
25
26        $this->assertInstanceOf('\Twig\Node\Expression\BlockReferenceExpression', $node);
27        $this->assertTrue($node->getAttribute('output'));
28    }
29
30    public function testRenderParentBlockOptimizer()
31    {
32        $env = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock(), ['cache' => false, 'autoescape' => false]);
33
34        $stream = $env->parse($env->tokenize(new Source('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index')));
35
36        $node = $stream->getNode('blocks')->getNode('content')->getNode(0)->getNode('body');
37
38        $this->assertInstanceOf('\Twig\Node\Expression\ParentExpression', $node);
39        $this->assertTrue($node->getAttribute('output'));
40    }
41
42    public function testRenderVariableBlockOptimizer()
43    {
44        if (PHP_VERSION_ID >= 50400) {
45            $this->markTestSkipped('not needed on PHP >= 5.4');
46        }
47
48        $env = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock(), ['cache' => false, 'autoescape' => false]);
49        $stream = $env->parse($env->tokenize(new Source('{{ block(name|lower) }}', 'index')));
50
51        $node = $stream->getNode('body')->getNode(0)->getNode(1);
52
53        $this->assertInstanceOf('\Twig\Node\Expression\BlockReferenceExpression', $node);
54        $this->assertTrue($node->getAttribute('output'));
55    }
56
57    /**
58     * @dataProvider getTestsForForOptimizer
59     */
60    public function testForOptimizer($template, $expected)
61    {
62        $env = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock(), ['cache' => false]);
63
64        $stream = $env->parse($env->tokenize(new Source($template, 'index')));
65
66        foreach ($expected as $target => $withLoop) {
67            $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop), sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : ''));
68        }
69    }
70
71    public function getTestsForForOptimizer()
72    {
73        return [
74            ['{% for i in foo %}{% endfor %}', ['i' => false]],
75
76            ['{% for i in foo %}{{ loop.index }}{% endfor %}', ['i' => true]],
77
78            ['{% for i in foo %}{% for j in foo %}{% endfor %}{% endfor %}', ['i' => false, 'j' => false]],
79
80            ['{% for i in foo %}{% include "foo" %}{% endfor %}', ['i' => true]],
81
82            ['{% for i in foo %}{% include "foo" only %}{% endfor %}', ['i' => false]],
83
84            ['{% for i in foo %}{% include "foo" with { "foo": "bar" } only %}{% endfor %}', ['i' => false]],
85
86            ['{% for i in foo %}{% include "foo" with { "foo": loop.index } only %}{% endfor %}', ['i' => true]],
87
88            ['{% for i in foo %}{% for j in foo %}{{ loop.index }}{% endfor %}{% endfor %}', ['i' => false, 'j' => true]],
89
90            ['{% for i in foo %}{% for j in foo %}{{ loop.parent.loop.index }}{% endfor %}{% endfor %}', ['i' => true, 'j' => true]],
91
92            ['{% for i in foo %}{% set l = loop %}{% for j in foo %}{{ l.index }}{% endfor %}{% endfor %}', ['i' => true, 'j' => false]],
93
94            ['{% for i in foo %}{% for j in foo %}{{ foo.parent.loop.index }}{% endfor %}{% endfor %}', ['i' => false, 'j' => false]],
95
96            ['{% for i in foo %}{% for j in foo %}{{ loop["parent"].loop.index }}{% endfor %}{% endfor %}', ['i' => true, 'j' => true]],
97
98            ['{% for i in foo %}{{ include("foo") }}{% endfor %}', ['i' => true]],
99
100            ['{% for i in foo %}{{ include("foo", with_context = false) }}{% endfor %}', ['i' => false]],
101
102            ['{% for i in foo %}{{ include("foo", with_context = true) }}{% endfor %}', ['i' => true]],
103
104            ['{% for i in foo %}{{ include("foo", { "foo": "bar" }, with_context = false) }}{% endfor %}', ['i' => false]],
105
106            ['{% for i in foo %}{{ include("foo", { "foo": loop.index }, with_context = false) }}{% endfor %}', ['i' => true]],
107        ];
108    }
109
110    public function checkForConfiguration(Twig_NodeInterface $node = null, $target, $withLoop)
111    {
112        if (null === $node) {
113            return;
114        }
115
116        foreach ($node as $n) {
117            if ($n instanceof ForNode) {
118                if ($target === $n->getNode('value_target')->getAttribute('name')) {
119                    return $withLoop == $n->getAttribute('with_loop');
120                }
121            }
122
123            $ret = $this->checkForConfiguration($n, $target, $withLoop);
124            if (null !== $ret) {
125                return $ret;
126            }
127        }
128    }
129}
130