mirror of
https://github.com/twigphp/Twig.git
synced 2026-10-09 13:25:41 +00:00
44eb4242ad
* 3.x: Resolve block chains against the render context instead of freezing lineages Resolve constant parent templates once instead of on every lookup Template runtime and block composition Fix wrapping the Twig cache pool in a second tag aware adapter Check that the use tag is allowed before resolving trait templates # Conflicts: # CHANGELOG # extra/twig-extra-bundle/TwigExtraBundle.php # src/Template.php
179 lines
6.4 KiB
PHP
179 lines
6.4 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of Twig.
|
|
*
|
|
* (c) Fabien Potencier
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Twig\Tests;
|
|
|
|
/*
|
|
* This file is part of Twig.
|
|
*
|
|
* (c) Fabien Potencier
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Twig\Environment;
|
|
use Twig\Error\LoaderError;
|
|
use Twig\Error\RuntimeError;
|
|
use Twig\Loader\ArrayLoader;
|
|
use Twig\TwigFunction;
|
|
|
|
class TemplateWrapperTest extends TestCase
|
|
{
|
|
public function testHasGetBlocks(): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader([
|
|
'index' => '{% block foo %}{% endblock %}',
|
|
'index_with_use' => '{% use "imported" %}{% block foo %}{% endblock %}',
|
|
'index_with_extends' => '{% extends "extended" %}{% block foo %}{% endblock %}',
|
|
'imported' => '{% block imported %}{% endblock %}',
|
|
'extended' => '{% block extended %}{% endblock %}',
|
|
]));
|
|
|
|
$wrapper = $twig->load('index');
|
|
$this->assertTrue($wrapper->hasBlock('foo'));
|
|
$this->assertFalse($wrapper->hasBlock('bar'));
|
|
$this->assertEquals(['foo'], $wrapper->getBlockNames());
|
|
|
|
$wrapper = $twig->load('index_with_use');
|
|
$this->assertTrue($wrapper->hasBlock('foo'));
|
|
$this->assertTrue($wrapper->hasBlock('imported'));
|
|
$this->assertEquals(['imported', 'foo'], $wrapper->getBlockNames());
|
|
|
|
$wrapper = $twig->load('index_with_extends');
|
|
$this->assertTrue($wrapper->hasBlock('foo'));
|
|
$this->assertTrue($wrapper->hasBlock('extended'));
|
|
$this->assertEquals(['foo', 'extended'], $wrapper->getBlockNames());
|
|
}
|
|
|
|
public function testBlockIntrospectionIncludesGlobals(): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader([
|
|
'index' => '{% extends layout %}',
|
|
'global_parent' => '{% block global %}{% endblock %}',
|
|
'local_parent' => '{% block local %}{% endblock %}',
|
|
]));
|
|
$twig->addGlobal('layout', 'global_parent');
|
|
|
|
$wrapper = $twig->load('index');
|
|
$this->assertTrue($wrapper->hasBlock('global'));
|
|
$this->assertFalse($wrapper->hasBlock('local'));
|
|
$this->assertSame(['global'], $wrapper->getBlockNames());
|
|
|
|
$context = ['layout' => 'local_parent'];
|
|
$this->assertTrue($wrapper->hasBlock('local', $context));
|
|
$this->assertFalse($wrapper->hasBlock('global', $context));
|
|
$this->assertSame(['local'], $wrapper->getBlockNames($context));
|
|
}
|
|
|
|
public function testStreamBlockIncludesGlobals(): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader([
|
|
'index' => '{% extends layout %}',
|
|
'layout' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}',
|
|
]));
|
|
$twig->addGlobal('layout', 'layout');
|
|
$twig->addGlobal('bar', 'BAR');
|
|
|
|
$streamed = '';
|
|
foreach ($twig->load('index')->streamBlock('foo', ['foo' => 'FOO']) as $data) {
|
|
$streamed .= $data;
|
|
}
|
|
|
|
$this->assertSame('FOOBAR', $streamed);
|
|
}
|
|
|
|
#[DataProvider('provideDynamicParentFailures')]
|
|
public function testDynamicParentFailuresAreWrapped(string $template, ?TwigFunction $function, string $previousException): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader(['index' => $template]));
|
|
if (null !== $function) {
|
|
$twig->addFunction($function);
|
|
}
|
|
|
|
foreach ([
|
|
'block introspection' => static fn () => $twig->load('index')->getBlockNames(),
|
|
'rendering' => static fn () => $twig->render('index'),
|
|
] as $entryPoint => $call) {
|
|
try {
|
|
$call();
|
|
$this->fail(\sprintf('Resolving the dynamic parent during %s must fail.', $entryPoint));
|
|
} catch (RuntimeError $e) {
|
|
$this->assertSame('index', $e->getSourceContext()->getName());
|
|
$this->assertSame(1, $e->getTemplateLine());
|
|
$this->assertInstanceOf($previousException, $e->getPrevious());
|
|
}
|
|
}
|
|
}
|
|
|
|
public function testDynamicParentTwigErrorsAreNotWrapped(): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader(['index' => '{% extends parent %}']));
|
|
|
|
try {
|
|
$twig->load('index')->getBlockNames(['parent' => 'missing']);
|
|
$this->fail('Loading the dynamic parent must fail.');
|
|
} catch (LoaderError $e) {
|
|
$this->assertSame('index', $e->getSourceContext()->getName());
|
|
$this->assertSame(1, $e->getTemplateLine());
|
|
$this->assertNull($e->getPrevious());
|
|
}
|
|
}
|
|
|
|
public static function provideDynamicParentFailures(): iterable
|
|
{
|
|
yield 'missing parent variable' => ['{% extends parent %}', null, \TypeError::class];
|
|
yield 'exception thrown by parent expression' => ['{% extends boom() %}', new TwigFunction('boom', static function (): never { throw new \DomainException('kaboom'); }), \DomainException::class];
|
|
}
|
|
|
|
public function testBlockIntrospectionReportsTheExtendsLineForAMissingParent(): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader(['index' => "\n\n{% extends 'missing' %}"]));
|
|
|
|
try {
|
|
$twig->load('index')->hasBlock('foo');
|
|
$this->fail('Introspecting a template with a missing parent must fail.');
|
|
} catch (LoaderError $e) {
|
|
$this->assertSame('Template "missing" is not defined.', $e->getRawMessage());
|
|
$this->assertSame(3, $e->getTemplateLine());
|
|
}
|
|
}
|
|
|
|
public function testRenderBlock(): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader([
|
|
'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}',
|
|
]));
|
|
$twig->addGlobal('bar', 'BAR');
|
|
|
|
$wrapper = $twig->load('index');
|
|
$this->assertEquals('FOOBAR', $wrapper->renderBlock('foo', ['foo' => 'FOO']));
|
|
}
|
|
|
|
public function testDisplayBlock(): void
|
|
{
|
|
$twig = new Environment(new ArrayLoader([
|
|
'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}',
|
|
]));
|
|
|
|
$twig->addGlobal('bar', 'BAR');
|
|
|
|
$wrapper = $twig->load('index');
|
|
|
|
ob_start();
|
|
$wrapper->displayBlock('foo', ['foo' => 'FOO']);
|
|
|
|
$this->assertEquals('FOOBAR', ob_get_clean());
|
|
}
|
|
}
|