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