Fix the deprecated tag generating invalid PHP for an integer message

This commit is contained in:
Fabien Potencier
2026-09-27 20:06:42 +02:00
parent d79ac7e60d
commit c6f1ef9521
3 changed files with 52 additions and 32 deletions
+1
View File
@@ -7,6 +7,7 @@
* Speed up reading object attributes backed by getters or class constants
* Fix the sandbox not reporting the line of a rejected `guard` tag
* Fix the spread operator compiling to invalid PHP outside sequences, mappings, and call arguments
* Fix the `deprecated` tag generating invalid PHP for an integer message
# 3.30.0 (2026-09-25)
+8 -24
View File
@@ -14,7 +14,6 @@ namespace Twig\Node;
use Twig\Attribute\YieldReady;
use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;
/**
* Represents a deprecated node.
@@ -33,17 +32,6 @@ class DeprecatedNode extends Node implements CoercesChildrenToStringInterface
{
$compiler->addDebugInfo($this);
$expr = $this->getNode('expr');
if (!$expr instanceof ConstantExpression) {
$varName = $compiler->getVarName();
$compiler
->write(\sprintf('$%s = ', $varName))
->subcompile($expr)
->raw(";\n")
;
}
$compiler->write('trigger_deprecation(');
if ($this->hasNode('package')) {
$compiler->subcompile($this->getNode('package'));
@@ -56,24 +44,20 @@ class DeprecatedNode extends Node implements CoercesChildrenToStringInterface
} else {
$compiler->raw("''");
}
$compiler->raw(', ');
if ($expr instanceof ConstantExpression) {
$compiler->subcompile($expr);
} else {
$compiler->write(\sprintf('$%s', $varName));
}
$compiler
->raw('.')
->string(\sprintf(' in "%s" at line %d.', $this->getTemplateName(), $this->getTemplateLine()))
->raw(");\n")
->raw(', sprintf(')
->string(\sprintf('%%s in "%%s" at line %d.', $this->getTemplateLine()))
->raw(', ')
->subcompile($this->getNode('expr'))
->raw(', ')
->string($this->getTemplateName())
->raw("));\n")
;
}
public function getStringCoercedChildNames(): array
{
// the message is concatenated with `.`, and `package` / `version` are typed `string` on trigger_deprecation()
// the message is formatted by `sprintf()`, and `package` / `version` are typed `string` on trigger_deprecation()
$names = ['expr'];
if ($this->hasNode('package')) {
$names[] = 'package';
+43 -8
View File
@@ -20,7 +20,7 @@ namespace Twig\Tests\Node;
* file that was distributed with this source code.
*/
use Twig\Compiler;
use PHPUnit\Framework\Attributes\Group;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Node\DeprecatedNode;
@@ -43,6 +43,36 @@ class DeprecatedTest extends NodeTestCase
$this->assertEquals($expr, $node->getNode('expr'));
}
#[Group('legacy')]
public function testTriggeredMessage(): void
{
$environment = new Environment(new ArrayLoader([
'100%.twig' => "{% deprecated 0 %}\n{% deprecated 4 %}\n{% deprecated 'The %s template' ~ name package='foo/bar' version='1.1' %}",
]));
$deprecations = [];
set_error_handler(static function (int $type, string $message) use (&$deprecations): bool {
if (\E_USER_DEPRECATED === $type) {
$deprecations[] = $message;
return true;
}
return false;
});
try {
$environment->render('100%.twig', ['name' => '%d']);
} finally {
restore_error_handler();
}
$this->assertSame([
'0 in "100%.twig" at line 1.',
'4 in "100%.twig" at line 2.',
'Since foo/bar 1.1: The %s template%d in "100%.twig" at line 3.',
], $deprecations);
}
public static function provideTests(): iterable
{
$tests = [];
@@ -55,7 +85,7 @@ class DeprecatedTest extends NodeTestCase
$tests[] = [$node, <<<EOF
// line 1
trigger_deprecation("twig/twig", "1.1", "This section is deprecated"." in \"foo.twig\" at line 1.");
trigger_deprecation("twig/twig", "1.1", sprintf("%s in \"%s\" at line 1.", "This section is deprecated", "foo.twig"));
EOF
];
@@ -72,7 +102,7 @@ EOF
// line 1
if (true) {
// line 2
trigger_deprecation("twig/twig", "1.1", "This section is deprecated"." in \"foo.twig\" at line 2.");
trigger_deprecation("twig/twig", "1.1", sprintf("%s in \"%s\" at line 2.", "This section is deprecated", "foo.twig"));
}
EOF
];
@@ -86,14 +116,19 @@ EOF
$node->setNode('package', new ConstantExpression('twig/twig', 1));
$node->setNode('version', new ConstantExpression('1.1', 1));
$compiler = new Compiler($environment);
$varName = $compiler->getVarName();
$tests[] = [$node, <<<EOF
// line 1
trigger_deprecation("twig/twig", "1.1", sprintf("%s in \"%s\" at line 1.", Twig\Tests\Node\\foo(), "foo.twig"));
EOF, $environment];
$node = new DeprecatedNode(new ConstantExpression(0, 1), 1);
$node->setSourceContext(new Source('', '100%.twig'));
$tests[] = [$node, <<<EOF
// line 1
\$$varName = Twig\Tests\Node\\foo();
trigger_deprecation("twig/twig", "1.1", \$$varName." in \"foo.twig\" at line 1.");
EOF, $environment];
trigger_deprecation('', '', sprintf("%s in \"%s\" at line 1.", 0, "100%.twig"));
EOF
];
return $tests;
}