mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-02 05:26:43 +00:00
Fix correctness visitor regressions
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
* Track the source offset of each token and expose it via `Token::getOffset()`
|
||||
* Fix nested `block()` calls to resolve against the overriding template when a block rendered through `block(name, template)` calls `parent()`
|
||||
* Deprecate the possibility to use a `block` tag within a capture node (like `set`)
|
||||
* Deprecate the possibility to use a `block` tag within a capture node (like `set`) in child templates
|
||||
* Deprecate using a `macro`, `extends`, or `use` tag outside the root of a template
|
||||
* Stop reporting a skipped test in `IntegrationTestCase` when there is no legacy test to run
|
||||
* Fix `markdown_to_html` to strip the indentation shared by all lines instead of mangling content that starts with a blank line
|
||||
|
||||
+4
-3
@@ -302,9 +302,10 @@ Templates
|
||||
instances of ``Twig\TemplateWrapper`` instead.
|
||||
|
||||
* Having a "block" definition nested in another node that captures the output
|
||||
(like "set") is deprecated in Twig 3.14 and will throw in Twig 4.0. Such use
|
||||
cases should be avoided as the "block" tag is used to both define the block
|
||||
AND display it in place. Here is how you can decouple both easily:
|
||||
(like "set") in a child template is deprecated in Twig 3.14 and will throw
|
||||
in Twig 4.0. Such use cases should be avoided as the "block" tag is used to
|
||||
both define the block AND display it in place. Here is how you can decouple
|
||||
both:
|
||||
|
||||
Before::
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ use Twig\Node\MacroNode;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\NodeCaptureInterface;
|
||||
use Twig\Node\NodeOutputInterface;
|
||||
use Twig\Node\Nodes;
|
||||
use Twig\Node\TextNode;
|
||||
|
||||
@@ -145,6 +146,10 @@ final class CorrectnessNodeVisitor implements NodeVisitorInterface
|
||||
return $node->isBlank();
|
||||
}
|
||||
|
||||
if (!$node instanceof BlockReferenceNode && $node instanceof NodeOutputInterface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($node as $n) {
|
||||
if (!$this->isEmptyOutputNode($n)) {
|
||||
return false;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
namespace Twig\TokenParser;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\ConfigNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Token;
|
||||
@@ -27,8 +28,16 @@ final class ExtendsTokenParser extends AbstractTokenParser
|
||||
{
|
||||
public function parse(Token $token): Node
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
if ($this->parser->peekBlockStack()) {
|
||||
throw new SyntaxError('Cannot use "extend" in a block.', $token->getLine(), $stream->getSourceContext());
|
||||
} elseif (!$this->parser->isMainScope()) {
|
||||
throw new SyntaxError('Cannot use "extend" in a macro.', $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
$this->parser->setParent($this->parser->parseExpression());
|
||||
$this->parser->getStream()->expect(Token::BLOCK_END_TYPE);
|
||||
$stream->expect(Token::BLOCK_END_TYPE);
|
||||
|
||||
return new ConfigNode($token->getLine());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
--TEST--
|
||||
Exception for child templates printing content outside blocks defined by parent
|
||||
--TEMPLATE--
|
||||
{% extends 'base.twig' %}
|
||||
|
||||
{{ content }}
|
||||
|
||||
{% block sidebar %}
|
||||
Content inside a block.
|
||||
{% endblock %}
|
||||
--TEMPLATE(base.twig)--
|
||||
{% block sidebar %}
|
||||
{% endblock %}
|
||||
--DATA--
|
||||
return ['content' => 'Content outside a block.'];
|
||||
--EXCEPTION--
|
||||
Twig\Error\SyntaxError: A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag in "index.twig" at line 4?
|
||||
@@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
"extends" tag in a block after a parent has been defined
|
||||
--TEMPLATE--
|
||||
{% extends "base.twig" %}
|
||||
|
||||
{% block foo %}
|
||||
{% extends "foo.twig" %}
|
||||
{% endblock %}
|
||||
--TEMPLATE(base.twig)--
|
||||
--TEMPLATE(foo.twig)--
|
||||
--DATA--
|
||||
return []
|
||||
--EXCEPTION--
|
||||
Twig\Error\SyntaxError: Cannot use "extend" in a block in "index.twig" at line 5.
|
||||
@@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
"extends" tag in a macro after a parent has been defined
|
||||
--TEMPLATE--
|
||||
{% extends "base.twig" %}
|
||||
|
||||
{% macro foo() %}
|
||||
{% extends "foo.twig" %}
|
||||
{% endmacro %}
|
||||
--TEMPLATE(base.twig)--
|
||||
--TEMPLATE(foo.twig)--
|
||||
--DATA--
|
||||
return []
|
||||
--EXCEPTION--
|
||||
Twig\Error\SyntaxError: Cannot use "extend" in a macro in "index.twig" at line 5.
|
||||
@@ -20,6 +20,7 @@ namespace Twig\Tests\Node;
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Twig\Node\TextNode;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
@@ -43,6 +44,7 @@ class TextTest extends NodeTestCase
|
||||
/**
|
||||
* @dataProvider getIsBlankData
|
||||
*/
|
||||
#[DataProvider('getIsBlankData')]
|
||||
public function testIsBlank($blank)
|
||||
{
|
||||
$this->assertTrue((new TextNode($blank, 1))->isBlank());
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Twig\Tests\NodeVisitor;
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\SyntaxError;
|
||||
@@ -18,9 +19,11 @@ use Twig\Loader\ArrayLoader;
|
||||
use Twig\Node\BodyNode;
|
||||
use Twig\Node\EmptyNode;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\IncludeNode;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\Nodes;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\SetNode;
|
||||
use Twig\Node\TextNode;
|
||||
use Twig\NodeTraverser;
|
||||
@@ -32,6 +35,7 @@ class CorrectnessTest extends TestCase
|
||||
/**
|
||||
* @dataProvider getFilterBodyNodesData
|
||||
*/
|
||||
#[DataProvider('getFilterBodyNodesData')]
|
||||
public function testFilterBodyNodes($input, $expected)
|
||||
{
|
||||
$this->assertEquals($expected, $this->traverse($input, $expected));
|
||||
@@ -54,6 +58,7 @@ class CorrectnessTest extends TestCase
|
||||
/**
|
||||
* @dataProvider getFilterBodyNodesDataThrowsException
|
||||
*/
|
||||
#[DataProvider('getFilterBodyNodesDataThrowsException')]
|
||||
public function testFilterBodyNodesThrowsException($input)
|
||||
{
|
||||
$this->expectException(SyntaxError::class);
|
||||
@@ -64,6 +69,8 @@ class CorrectnessTest extends TestCase
|
||||
{
|
||||
return [
|
||||
[new TextNode('foo', 1)],
|
||||
[new PrintNode(new ConstantExpression('foo', 1), 1)],
|
||||
[new IncludeNode(new ConstantExpression('foo', 1), null, false, false, 1)],
|
||||
[new Nodes([new Nodes([new TextNode('foo', 1)])])],
|
||||
];
|
||||
}
|
||||
@@ -71,6 +78,7 @@ class CorrectnessTest extends TestCase
|
||||
/**
|
||||
* @dataProvider getFilterBodyNodesWithBOMData
|
||||
*/
|
||||
#[DataProvider('getFilterBodyNodesWithBOMData')]
|
||||
public function testFilterBodyNodesWithBOM($emptyText)
|
||||
{
|
||||
$input = new TextNode(\chr(0xEF).\chr(0xBB).\chr(0xBF).$emptyText, 1);
|
||||
|
||||
Reference in New Issue
Block a user