mirror of
https://github.com/twigphp/Twig.git
synced 2026-08-18 12:12:31 +00:00
330 lines
11 KiB
PHP
330 lines
11 KiB
PHP
<?php
|
|
|
|
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\TestCase;
|
|
use Twig\Environment;
|
|
use Twig\Error\Error;
|
|
use Twig\Error\RuntimeError;
|
|
use Twig\Loader\ArrayLoader;
|
|
use Twig\Loader\FilesystemLoader;
|
|
use Twig\Source;
|
|
|
|
class ErrorTest extends TestCase
|
|
{
|
|
public function testErrorWithObjectFilename()
|
|
{
|
|
$error = new Error('foo');
|
|
$error->setSourceContext(new Source('', new \SplFileInfo(__FILE__)));
|
|
|
|
$this->assertStringContainsString('tests'.\DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage());
|
|
}
|
|
|
|
public function testTwigExceptionGuessWithMissingVarAndArrayLoader()
|
|
{
|
|
$loader = new ArrayLoader([
|
|
'base.html' => '{% block content %}{% endblock %}',
|
|
'index.html' => <<<EOHTML
|
|
{% extends 'base.html' %}
|
|
{% block content %}
|
|
{{ foo.bar }}
|
|
{% endblock %}
|
|
{% block foo %}
|
|
{{ foo.bar }}
|
|
{% endblock %}
|
|
EOHTML
|
|
]);
|
|
|
|
$twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('index.html');
|
|
try {
|
|
$template->render([]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals('Variable "foo" does not exist in "index.html" at line 3.', $e->getMessage());
|
|
$this->assertEquals(3, $e->getTemplateLine());
|
|
$this->assertEquals('index.html', $e->getSourceContext()->getName());
|
|
}
|
|
}
|
|
|
|
public function testTwigExceptionGuessWithExceptionAndArrayLoader()
|
|
{
|
|
$loader = new ArrayLoader([
|
|
'base.html' => '{% block content %}{% endblock %}',
|
|
'index.html' => <<<EOHTML
|
|
{% extends 'base.html' %}
|
|
{% block content %}
|
|
{{ foo.bar }}
|
|
{% endblock %}
|
|
{% block foo %}
|
|
{{ foo.bar }}
|
|
{% endblock %}
|
|
EOHTML
|
|
]);
|
|
$twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('index.html');
|
|
try {
|
|
$template->render(['foo' => new ErrorTest_Foo()]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage());
|
|
$this->assertEquals(3, $e->getTemplateLine());
|
|
$this->assertEquals('index.html', $e->getSourceContext()->getName());
|
|
}
|
|
}
|
|
|
|
public function testTwigExceptionGuessWithMissingVarAndFilesystemLoader()
|
|
{
|
|
$loader = new FilesystemLoader(__DIR__.'/Fixtures/errors');
|
|
$twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('index.html');
|
|
try {
|
|
$template->render([]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals('Variable "foo" does not exist.', $e->getMessage());
|
|
$this->assertEquals(3, $e->getTemplateLine());
|
|
$this->assertEquals('index.html', $e->getSourceContext()->getName());
|
|
$this->assertEquals(3, $e->getLine());
|
|
$this->assertEquals(strtr(__DIR__.'/Fixtures/errors/index.html', '/', \DIRECTORY_SEPARATOR), $e->getFile());
|
|
}
|
|
}
|
|
|
|
public function testTwigExceptionGuessWithExceptionAndFilesystemLoader()
|
|
{
|
|
$loader = new FilesystemLoader(__DIR__.'/Fixtures/errors');
|
|
$twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('index.html');
|
|
try {
|
|
$template->render(['foo' => new ErrorTest_Foo()]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...").', $e->getMessage());
|
|
$this->assertEquals(3, $e->getTemplateLine());
|
|
$this->assertEquals('index.html', $e->getSourceContext()->getName());
|
|
$this->assertEquals(3, $e->getLine());
|
|
$this->assertEquals(strtr(__DIR__.'/Fixtures/errors/index.html', '/', \DIRECTORY_SEPARATOR), $e->getFile());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider getErroredTemplates
|
|
*/
|
|
public function testTwigExceptionAddsFileAndLine($templates, $name, $line)
|
|
{
|
|
$loader = new ArrayLoader($templates);
|
|
$twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('index');
|
|
|
|
try {
|
|
$template->render([]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals(sprintf('Variable "foo" does not exist in "%s" at line %d.', $name, $line), $e->getMessage());
|
|
$this->assertEquals($line, $e->getTemplateLine());
|
|
$this->assertEquals($name, $e->getSourceContext()->getName());
|
|
}
|
|
|
|
try {
|
|
$template->render(['foo' => new ErrorTest_Foo()]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals(sprintf('An exception has been thrown during the rendering of a template ("Runtime error...") in "%s" at line %d.', $name, $line), $e->getMessage());
|
|
$this->assertEquals($line, $e->getTemplateLine());
|
|
$this->assertEquals($name, $e->getSourceContext()->getName());
|
|
}
|
|
}
|
|
|
|
public function testTwigArrayFilterThrowsRuntimeExceptions()
|
|
{
|
|
$loader = new ArrayLoader([
|
|
'filter-null.html' => <<<EOHTML
|
|
{# Argument 1 passed to IteratorIterator::__construct() must implement interface Traversable, null given: #}
|
|
{% for n in variable|filter(x => x > 3) %}
|
|
This list contains {{n}}.
|
|
{% endfor %}
|
|
EOHTML
|
|
]);
|
|
|
|
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('filter-null.html');
|
|
$out = $template->render(['variable' => [1, 2, 3, 4]]);
|
|
$this->assertEquals('This list contains 4.', trim($out));
|
|
|
|
try {
|
|
$template->render(['variable' => null]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals(2, $e->getTemplateLine());
|
|
$this->assertEquals('filter-null.html', $e->getSourceContext()->getName());
|
|
}
|
|
}
|
|
|
|
public function testTwigArrayMapThrowsRuntimeExceptions()
|
|
{
|
|
$loader = new ArrayLoader([
|
|
'map-null.html' => <<<EOHTML
|
|
{# We expect a runtime error if `variable` is not traversable #}
|
|
{% for n in variable|map(x => x * 3) %}
|
|
{{- n -}}
|
|
{% endfor %}
|
|
EOHTML
|
|
]);
|
|
|
|
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('map-null.html');
|
|
$out = $template->render(['variable' => [1, 2, 3, 4]]);
|
|
$this->assertEquals('36912', trim($out));
|
|
|
|
try {
|
|
$template->render(['variable' => null]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals(2, $e->getTemplateLine());
|
|
$this->assertEquals('map-null.html', $e->getSourceContext()->getName());
|
|
}
|
|
}
|
|
|
|
public function testTwigArrayReduceThrowsRuntimeExceptions()
|
|
{
|
|
$loader = new ArrayLoader([
|
|
'reduce-null.html' => <<<EOHTML
|
|
{# We expect a runtime error if `variable` is not traversable #}
|
|
{{ variable|reduce((carry, x) => carry + x) }}
|
|
EOHTML
|
|
]);
|
|
|
|
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('reduce-null.html');
|
|
$out = $template->render(['variable' => [1, 2, 3, 4]]);
|
|
$this->assertEquals('10', trim($out));
|
|
|
|
try {
|
|
$template->render(['variable' => null]);
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals(2, $e->getTemplateLine());
|
|
$this->assertEquals('reduce-null.html', $e->getSourceContext()->getName());
|
|
}
|
|
}
|
|
|
|
public function testTwigArgumentCountErrorThrowsRuntimeExceptions()
|
|
{
|
|
$loader = new ArrayLoader([
|
|
'argument-error.html' => <<<EOHTML
|
|
{# max requires at least one argument #}
|
|
{{ max() }}
|
|
EOHTML
|
|
]);
|
|
|
|
$twig = new Environment($loader, ['debug' => true, 'cache' => false]);
|
|
|
|
$template = $twig->load('argument-error.html');
|
|
try {
|
|
$template->render();
|
|
|
|
$this->fail();
|
|
} catch (RuntimeError $e) {
|
|
$this->assertEquals(2, $e->getTemplateLine());
|
|
$this->assertEquals('argument-error.html', $e->getSourceContext()->getName());
|
|
}
|
|
}
|
|
|
|
public function getErroredTemplates()
|
|
{
|
|
return [
|
|
// error occurs in a template
|
|
[
|
|
[
|
|
'index' => "\n\n{{ foo.bar }}\n\n\n{{ 'foo' }}",
|
|
],
|
|
'index', 3,
|
|
],
|
|
|
|
// error occurs in an included template
|
|
[
|
|
[
|
|
'index' => "{% include 'partial' %}",
|
|
'partial' => '{{ foo.bar }}',
|
|
],
|
|
'partial', 1,
|
|
],
|
|
|
|
// error occurs in a parent block when called via parent()
|
|
[
|
|
[
|
|
'index' => "{% extends 'base' %}
|
|
{% block content %}
|
|
{{ parent() }}
|
|
{% endblock %}",
|
|
'base' => '{% block content %}{{ foo.bar }}{% endblock %}',
|
|
],
|
|
'base', 1,
|
|
],
|
|
|
|
// error occurs in a block from the child
|
|
[
|
|
[
|
|
'index' => "{% extends 'base' %}
|
|
{% block content %}
|
|
{{ foo.bar }}
|
|
{% endblock %}
|
|
{% block foo %}
|
|
{{ foo.bar }}
|
|
{% endblock %}",
|
|
'base' => '{% block content %}{% endblock %}',
|
|
],
|
|
'index', 3,
|
|
],
|
|
];
|
|
}
|
|
|
|
public function testTwigLeakOutputInDebugMode()
|
|
{
|
|
$output = exec(sprintf('%s %s debug', \PHP_BINARY, escapeshellarg(__DIR__.'/Fixtures/errors/leak-output.php')));
|
|
|
|
$this->assertSame('Hello OOPS', $output);
|
|
}
|
|
|
|
public function testDoesNotTwigLeakOutput()
|
|
{
|
|
$output = exec(sprintf('%s %s', \PHP_BINARY, escapeshellarg(__DIR__.'/Fixtures/errors/leak-output.php')));
|
|
|
|
$this->assertSame('', $output);
|
|
}
|
|
}
|
|
|
|
class ErrorTest_Foo
|
|
{
|
|
public function bar()
|
|
{
|
|
throw new \Exception('Runtime error...');
|
|
}
|
|
}
|