From 09541d8224c8569a3c4e66e4c012b1454123c8f3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 22 Oct 2024 12:26:52 +0200 Subject: [PATCH 1/2] Minor fixes around parse_url() checks --- Command/DebugCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index 42a2795d..0510df58 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -562,7 +562,7 @@ private function getRelativePath(string $path): string private function isAbsolutePath(string $file): bool { - return strspn($file, '/\\', 0, 1) || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1)) || null !== parse_url($file, \PHP_URL_SCHEME); + return strspn($file, '/\\', 0, 1) || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1)) || parse_url($file, \PHP_URL_SCHEME); } /** From b3d3738b4be14bf1a4544a6faeed89463fe8b60e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 23 Oct 2024 22:39:31 +0200 Subject: [PATCH 2/2] ensure compatibility with Twig 3.15 --- Node/DumpNode.php | 29 +++++++++++++++---- Node/StopwatchNode.php | 10 ++++++- Node/TransNode.php | 3 +- .../TranslationDefaultDomainNodeVisitor.php | 6 ++-- Tests/Node/DumpNodeTest.php | 7 +++-- Tests/Node/FormThemeTest.php | 5 ++-- Tests/Node/SearchAndRenderBlockNodeTest.php | 21 +++++++------- Tests/Node/TransNodeTest.php | 3 +- .../TranslationNodeVisitorTest.php | 3 +- .../TokenParser/FormThemeTokenParserTest.php | 13 +++++---- TokenParser/DumpTokenParser.php | 3 +- TokenParser/StopwatchTokenParser.php | 3 +- 12 files changed, 71 insertions(+), 35 deletions(-) diff --git a/Node/DumpNode.php b/Node/DumpNode.php index 01a2eef8..b4313b1a 100644 --- a/Node/DumpNode.php +++ b/Node/DumpNode.php @@ -14,6 +14,7 @@ use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Attribute\YieldReady; use Twig\Compiler; +use Twig\Node\Expression\Variable\LocalVariable; use Twig\Node\Node; /** @@ -22,10 +23,20 @@ #[YieldReady] final class DumpNode extends Node { + /** + * @var LocalVariable|string + */ private $varPrefix; - public function __construct(string $varPrefix, ?Node $values, int $lineno, ?string $tag = null) + /** + * @param LocalVariable|string $varPrefix + */ + public function __construct($varPrefix, ?Node $values, int $lineno, ?string $tag = null) { + if (!\is_string($varPrefix) && !$varPrefix instanceof LocalVariable) { + throw new \TypeError(sprintf('Expected a string or an instance of "%s", but got "%s".', LocalVariable::class, get_debug_type($varPrefix))); + } + $nodes = []; if (null !== $values) { $nodes['values'] = $values; @@ -42,6 +53,12 @@ public function __construct(string $varPrefix, ?Node $values, int $lineno, ?stri public function compile(Compiler $compiler): void { + if ($this->varPrefix instanceof LocalVariable) { + $varPrefix = $this->varPrefix->getAttribute('name'); + } else { + $varPrefix = $this->varPrefix; + } + $compiler ->write("if (\$this->env->isDebug()) {\n") ->indent(); @@ -49,18 +66,18 @@ public function compile(Compiler $compiler): void if (!$this->hasNode('values')) { // remove embedded templates (macros) from the context $compiler - ->write(sprintf('$%svars = [];'."\n", $this->varPrefix)) - ->write(sprintf('foreach ($context as $%1$skey => $%1$sval) {'."\n", $this->varPrefix)) + ->write(sprintf('$%svars = [];'."\n", $varPrefix)) + ->write(sprintf('foreach ($context as $%1$skey => $%1$sval) {'."\n", $varPrefix)) ->indent() - ->write(sprintf('if (!$%sval instanceof \Twig\Template) {'."\n", $this->varPrefix)) + ->write(sprintf('if (!$%sval instanceof \Twig\Template) {'."\n", $varPrefix)) ->indent() - ->write(sprintf('$%1$svars[$%1$skey] = $%1$sval;'."\n", $this->varPrefix)) + ->write(sprintf('$%1$svars[$%1$skey] = $%1$sval;'."\n", $varPrefix)) ->outdent() ->write("}\n") ->outdent() ->write("}\n") ->addDebugInfo($this) - ->write(sprintf('\Symfony\Component\VarDumper\VarDumper::dump($%svars);'."\n", $this->varPrefix)); + ->write(sprintf('\Symfony\Component\VarDumper\VarDumper::dump($%svars);'."\n", $varPrefix)); } elseif (($values = $this->getNode('values')) && 1 === $values->count()) { $compiler ->addDebugInfo($this) diff --git a/Node/StopwatchNode.php b/Node/StopwatchNode.php index 239d1ca6..e8ac13d6 100644 --- a/Node/StopwatchNode.php +++ b/Node/StopwatchNode.php @@ -15,6 +15,7 @@ use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AssignNameExpression; +use Twig\Node\Expression\Variable\LocalVariable; use Twig\Node\Node; /** @@ -25,8 +26,15 @@ #[YieldReady] final class StopwatchNode extends Node { - public function __construct(Node $name, Node $body, AssignNameExpression $var, int $lineno = 0, ?string $tag = null) + /** + * @param AssignNameExpression|LocalVariable $var + */ + public function __construct(Node $name, Node $body, $var, int $lineno = 0, ?string $tag = null) { + if (!$var instanceof AssignNameExpression && !$var instanceof LocalVariable) { + throw new \TypeError(sprintf('Expected an instance of "%s" or "%s", but got "%s".', AssignNameExpression::class, LocalVariable::class, get_debug_type($var))); + } + if (class_exists(FirstClassTwigCallableReady::class)) { parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno); } else { diff --git a/Node/TransNode.php b/Node/TransNode.php index a711a7ca..c1080fec 100644 --- a/Node/TransNode.php +++ b/Node/TransNode.php @@ -18,6 +18,7 @@ use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\Node\TextNode; @@ -126,7 +127,7 @@ private function compileString(Node $body, ArrayExpression $vars, bool $ignoreSt if ('count' === $var && $this->hasNode('count')) { $vars->addElement($this->getNode('count'), $key); } else { - $varExpr = new NameExpression($var, $body->getTemplateLine()); + $varExpr = class_exists(ContextVariable::class) ? new ContextVariable($var, $body->getTemplateLine()) : new NameExpression($var, $body->getTemplateLine()); $varExpr->setAttribute('ignore_strict_check', $ignoreStrictCheck); $vars->addElement($varExpr, $key); } diff --git a/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/NodeVisitor/TranslationDefaultDomainNodeVisitor.php index d218f62e..2bbfc4ab 100644 --- a/NodeVisitor/TranslationDefaultDomainNodeVisitor.php +++ b/NodeVisitor/TranslationDefaultDomainNodeVisitor.php @@ -20,6 +20,8 @@ use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\ModuleNode; use Twig\Node\Node; use Twig\Node\Nodes; @@ -51,8 +53,8 @@ public function enterNode(Node $node, Environment $env): Node return $node; } else { $var = $this->getVarName(); - $name = new AssignNameExpression($var, $node->getTemplateLine()); - $this->scope->set('domain', new NameExpression($var, $node->getTemplateLine())); + $name = class_exists(AssignContextVariable::class) ? new AssignContextVariable($var, $node->getTemplateLine()) : new AssignNameExpression($var, $node->getTemplateLine()); + $this->scope->set('domain', class_exists(ContextVariable::class) ? new ContextVariable($var, $node->getTemplateLine()) : new NameExpression($var, $node->getTemplateLine())); if (class_exists(Nodes::class)) { return new SetNode(false, new Nodes([$name]), new Nodes([$node->getNode('expr')]), $node->getTemplateLine()); diff --git a/Tests/Node/DumpNodeTest.php b/Tests/Node/DumpNodeTest.php index a19ba041..6d584c89 100644 --- a/Tests/Node/DumpNodeTest.php +++ b/Tests/Node/DumpNodeTest.php @@ -17,6 +17,7 @@ use Twig\Environment; use Twig\Loader\LoaderInterface; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\Node\Nodes; @@ -74,7 +75,7 @@ public function testOneVar() { if (class_exists(Nodes::class)) { $vars = new Nodes([ - new NameExpression('foo', 7), + new ContextVariable('foo', 7), ]); } else { $vars = new Node([ @@ -104,8 +105,8 @@ public function testMultiVars() { if (class_exists(Nodes::class)) { $vars = new Nodes([ - new NameExpression('foo', 7), - new NameExpression('bar', 7), + new ContextVariable('foo', 7), + new ContextVariable('bar', 7), ]); } else { $vars = new Node([ diff --git a/Tests/Node/FormThemeTest.php b/Tests/Node/FormThemeTest.php index d211bf26..de108056 100644 --- a/Tests/Node/FormThemeTest.php +++ b/Tests/Node/FormThemeTest.php @@ -22,6 +22,7 @@ use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\Node\Nodes; @@ -31,7 +32,7 @@ class FormThemeTest extends TestCase public function testConstructor() { - $form = new NameExpression('form', 0); + $form = class_exists(ContextVariable::class) ? new ContextVariable('form', 0) : new NameExpression('form', 0); if (class_exists(Nodes::class)) { $resources = new Nodes([ new ConstantExpression('tpl1', 0), @@ -53,7 +54,7 @@ public function testConstructor() public function testCompile() { - $form = new NameExpression('form', 0); + $form = class_exists(ContextVariable::class) ? new ContextVariable('form', 0) : new NameExpression('form', 0); $resources = new ArrayExpression([ new ConstantExpression(1, 0), new ConstantExpression('tpl1', 0), diff --git a/Tests/Node/SearchAndRenderBlockNodeTest.php b/Tests/Node/SearchAndRenderBlockNodeTest.php index 582eb6d0..5c2bacf1 100644 --- a/Tests/Node/SearchAndRenderBlockNodeTest.php +++ b/Tests/Node/SearchAndRenderBlockNodeTest.php @@ -22,6 +22,7 @@ use Twig\Node\Expression\ConditionalExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\Node\Nodes; use Twig\TwigFunction; @@ -32,7 +33,7 @@ public function testCompileWidget() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), ]); } else { $arguments = new Node([ @@ -61,7 +62,7 @@ public function testCompileWidgetWithVariables() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ArrayExpression([ new ConstantExpression('foo', 0), new ConstantExpression('bar', 0), @@ -98,7 +99,7 @@ public function testCompileLabelWithLabel() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ConstantExpression('my label', 0), ]); } else { @@ -129,7 +130,7 @@ public function testCompileLabelWithNullLabel() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ConstantExpression(null, 0), ]); } else { @@ -162,7 +163,7 @@ public function testCompileLabelWithEmptyStringLabel() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ConstantExpression('', 0), ]); } else { @@ -195,7 +196,7 @@ public function testCompileLabelWithDefaultLabel() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), ]); } else { $arguments = new Node([ @@ -224,7 +225,7 @@ public function testCompileLabelWithAttributes() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ConstantExpression(null, 0), new ArrayExpression([ new ConstantExpression('foo', 0), @@ -266,7 +267,7 @@ public function testCompileLabelWithLabelAndAttributes() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ConstantExpression('value in argument', 0), new ArrayExpression([ new ConstantExpression('foo', 0), @@ -309,7 +310,7 @@ public function testCompileLabelWithLabelThatEvaluatesToNull() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ConditionalExpression( // if new ConstantExpression(true, 0), @@ -360,7 +361,7 @@ public function testCompileLabelWithLabelThatEvaluatesToNullAndAttributes() { if (class_exists(Nodes::class)) { $arguments = new Nodes([ - new NameExpression('form', 0), + new ContextVariable('form', 0), new ConditionalExpression( // if new ConstantExpression(true, 0), diff --git a/Tests/Node/TransNodeTest.php b/Tests/Node/TransNodeTest.php index 1ac37b9c..a6b54f53 100644 --- a/Tests/Node/TransNodeTest.php +++ b/Tests/Node/TransNodeTest.php @@ -18,6 +18,7 @@ use Twig\Environment; use Twig\Loader\LoaderInterface; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\TextNode; /** @@ -28,7 +29,7 @@ class TransNodeTest extends TestCase public function testCompileStrict() { $body = new TextNode('trans %var%', 0); - $vars = new NameExpression('foo', 0); + $vars = class_exists(ContextVariable::class) ? new ContextVariable('foo', 0) : new NameExpression('foo', 0); $node = new TransNode($body, null, null, $vars); $env = new Environment($this->createMock(LoaderInterface::class), ['strict_variables' => true]); diff --git a/Tests/NodeVisitor/TranslationNodeVisitorTest.php b/Tests/NodeVisitor/TranslationNodeVisitorTest.php index 96134b6e..6dbd0d27 100644 --- a/Tests/NodeVisitor/TranslationNodeVisitorTest.php +++ b/Tests/NodeVisitor/TranslationNodeVisitorTest.php @@ -20,6 +20,7 @@ use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\Node\Nodes; use Twig\TwigFilter; @@ -44,7 +45,7 @@ public function testMessageExtractionWithInvalidDomainNode() if (class_exists(Nodes::class)) { $n = new Nodes([ new ArrayExpression([], 0), - new NameExpression('variable', 0), + new ContextVariable('variable', 0), ]); } else { $n = new Node([ diff --git a/Tests/TokenParser/FormThemeTokenParserTest.php b/Tests/TokenParser/FormThemeTokenParserTest.php index c9c0ce80..02b6597c 100644 --- a/Tests/TokenParser/FormThemeTokenParserTest.php +++ b/Tests/TokenParser/FormThemeTokenParserTest.php @@ -20,6 +20,7 @@ use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Parser; use Twig\Source; @@ -51,7 +52,7 @@ public static function getTestsForFormTheme() [ '{% form_theme form "tpl1" %}', new FormThemeNode( - new NameExpression('form', 1), + class_exists(ContextVariable::class) ? new ContextVariable('form', 1) : new NameExpression('form', 1), new ArrayExpression([ new ConstantExpression(0, 1), new ConstantExpression('tpl1', 1), @@ -63,7 +64,7 @@ public static function getTestsForFormTheme() [ '{% form_theme form "tpl1" "tpl2" %}', new FormThemeNode( - new NameExpression('form', 1), + class_exists(ContextVariable::class) ? new ContextVariable('form', 1) : new NameExpression('form', 1), new ArrayExpression([ new ConstantExpression(0, 1), new ConstantExpression('tpl1', 1), @@ -77,7 +78,7 @@ public static function getTestsForFormTheme() [ '{% form_theme form with "tpl1" %}', new FormThemeNode( - new NameExpression('form', 1), + class_exists(ContextVariable::class) ? new ContextVariable('form', 1) : new NameExpression('form', 1), new ConstantExpression('tpl1', 1), 1, 'form_theme' @@ -86,7 +87,7 @@ public static function getTestsForFormTheme() [ '{% form_theme form with ["tpl1"] %}', new FormThemeNode( - new NameExpression('form', 1), + class_exists(ContextVariable::class) ? new ContextVariable('form', 1) : new NameExpression('form', 1), new ArrayExpression([ new ConstantExpression(0, 1), new ConstantExpression('tpl1', 1), @@ -98,7 +99,7 @@ public static function getTestsForFormTheme() [ '{% form_theme form with ["tpl1", "tpl2"] %}', new FormThemeNode( - new NameExpression('form', 1), + class_exists(ContextVariable::class) ? new ContextVariable('form', 1) : new NameExpression('form', 1), new ArrayExpression([ new ConstantExpression(0, 1), new ConstantExpression('tpl1', 1), @@ -112,7 +113,7 @@ public static function getTestsForFormTheme() [ '{% form_theme form with ["tpl1", "tpl2"] only %}', new FormThemeNode( - new NameExpression('form', 1), + class_exists(ContextVariable::class) ? new ContextVariable('form', 1) : new NameExpression('form', 1), new ArrayExpression([ new ConstantExpression(0, 1), new ConstantExpression('tpl1', 1), diff --git a/TokenParser/DumpTokenParser.php b/TokenParser/DumpTokenParser.php index 341dc418..2d80f05c 100644 --- a/TokenParser/DumpTokenParser.php +++ b/TokenParser/DumpTokenParser.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Twig\TokenParser; use Symfony\Bridge\Twig\Node\DumpNode; +use Twig\Node\Expression\Variable\LocalVariable; use Twig\Node\Node; use Twig\Token; use Twig\TokenParser\AbstractTokenParser; @@ -40,7 +41,7 @@ public function parse(Token $token): Node } $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); - return new DumpNode($this->parser->getVarName(), $values, $token->getLine(), $this->getTag()); + return new DumpNode(class_exists(LocalVariable::class) ? new LocalVariable(null, $token->getLine()) : $this->parser->getVarName(), $values, $token->getLine(), $this->getTag()); } /** diff --git a/TokenParser/StopwatchTokenParser.php b/TokenParser/StopwatchTokenParser.php index a70e94b8..84faee22 100644 --- a/TokenParser/StopwatchTokenParser.php +++ b/TokenParser/StopwatchTokenParser.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\Node\StopwatchNode; use Twig\Node\Expression\AssignNameExpression; +use Twig\Node\Expression\Variable\LocalVariable; use Twig\Node\Node; use Twig\Token; use Twig\TokenParser\AbstractTokenParser; @@ -46,7 +47,7 @@ public function parse(Token $token): Node $stream->expect(Token::BLOCK_END_TYPE); if ($this->stopwatchIsAvailable) { - return new StopwatchNode($name, $body, new AssignNameExpression($this->parser->getVarName(), $token->getLine()), $lineno, $this->getTag()); + return new StopwatchNode($name, $body, class_exists(LocalVariable::class) ? new LocalVariable(null, $token->getLine()) : new AssignNameExpression($this->parser->getVarName(), $token->getLine()), $lineno, $this->getTag()); } return $body;