Use trigger_deprecation() instead of trigger_error() for deprecations

This commit is contained in:
Fabien Potencier
2024-08-08 11:56:21 +02:00
parent 92525785d8
commit 0ce79df5c4
13 changed files with 131 additions and 30 deletions
+2
View File
@@ -1,5 +1,7 @@
# 3.11.0 (2024-XX-XX)
* Add the possibility to add a package and a version to the `deprecated` tag
* Add the possibility to add a package for filter/function/test deprecations
* Mark `ConstantExpression` as being `@final`
* Add the `find` filter
* Fix optimizer mode validation in `OptimizerNodeVisitor`
+12
View File
@@ -285,6 +285,18 @@ deprecated one when that makes sense::
// ...
}, ['deprecated' => true, 'alternative' => 'new_one']);
.. versionadded:: 3.11
The ``deprecating_package`` option was added in Twig 3.11.
You can also set the ``deprecating_package`` option to specify the package that
is deprecating the filter, and ``deprecated`` can be set to the package version
when the filter was deprecated::
$filter = new \Twig\TwigFilter('obsolete', function () {
// ...
}, ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar']);
When a filter is deprecated, Twig emits a deprecation notice when compiling a
template using it. See :ref:`deprecation-notices` for more information.
+11
View File
@@ -23,6 +23,17 @@ You can also deprecate a macro in the following way:
Note that by default, the deprecation notices are silenced and never displayed nor logged.
See :ref:`deprecation-notices` to learn how to handle them.
.. versionadded:: 3.11
The ``package`` and ``version`` options were added in Twig 3.11.
You can optionally add the package and the version that introduced the deprecation:
.. code-block:: twig
{% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' package='twig/twig' %}
{% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' package='twig/twig' version='3.11' %}
.. note::
Don't use the ``deprecated`` tag to deprecate a ``block`` as the
+3 -12
View File
@@ -774,16 +774,13 @@ class ExpressionParser
$stream = $this->parser->getStream();
$message = \sprintf('Twig Test "%s" is deprecated', $test->getName());
if ($test->getDeprecatedVersion()) {
$message .= \sprintf(' since version %s', $test->getDeprecatedVersion());
}
if ($test->getAlternative()) {
$message .= \sprintf('. Use "%s" instead', $test->getAlternative());
}
$src = $stream->getSourceContext();
$message .= \sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $stream->getCurrent()->getLine());
@trigger_error($message, \E_USER_DEPRECATED);
trigger_deprecation($test->getDeprecatingPackage(), $test->getDeprecatedVersion(), $message);
}
return $test->getNodeClass();
@@ -800,16 +797,13 @@ class ExpressionParser
if ($function->isDeprecated()) {
$message = \sprintf('Twig Function "%s" is deprecated', $function->getName());
if ($function->getDeprecatedVersion()) {
$message .= \sprintf(' since version %s', $function->getDeprecatedVersion());
}
if ($function->getAlternative()) {
$message .= \sprintf('. Use "%s" instead', $function->getAlternative());
}
$src = $this->parser->getStream()->getSourceContext();
$message .= \sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);
@trigger_error($message, \E_USER_DEPRECATED);
trigger_deprecation($function->getDeprecatingPackage(), $function->getDeprecatedVersion(), $message);
}
return $function->getNodeClass();
@@ -826,16 +820,13 @@ class ExpressionParser
if ($filter->isDeprecated()) {
$message = \sprintf('Twig Filter "%s" is deprecated', $filter->getName());
if ($filter->getDeprecatedVersion()) {
$message .= \sprintf(' since version %s', $filter->getDeprecatedVersion());
}
if ($filter->getAlternative()) {
$message .= \sprintf('. Use "%s" instead', $filter->getAlternative());
}
$src = $this->parser->getStream()->getSourceContext();
$message .= \sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);
@trigger_error($message, \E_USER_DEPRECATED);
trigger_deprecation($filter->getDeprecatingPackage(), $filter->getDeprecatedVersion(), $message);
}
return $filter->getNodeClass();
+27 -9
View File
@@ -35,21 +35,39 @@ class DeprecatedNode extends Node
$expr = $this->getNode('expr');
if ($expr instanceof ConstantExpression) {
$compiler->write('@trigger_error(')
->subcompile($expr);
} else {
if (!$expr instanceof ConstantExpression) {
$varName = $compiler->getVarName();
$compiler->write(\sprintf('$%s = ', $varName))
$compiler
->write(\sprintf('$%s = ', $varName))
->subcompile($expr)
->raw(";\n")
->write(\sprintf('@trigger_error($%s', $varName));
;
}
$compiler->write('trigger_deprecation(');
if ($this->hasNode('package')) {
$compiler->subcompile($this->getNode('package'));
} else {
$compiler->raw("''");
}
$compiler->raw(', ');
if ($this->hasNode('version')) {
$compiler->subcompile($this->getNode('version'));
} else {
$compiler->raw("''");
}
$compiler->raw(', ');
if ($expr instanceof ConstantExpression) {
$compiler->subcompile($expr);
} else {
$compiler->write(\sprintf('$%s', $varName));
}
$compiler
->raw('.')
->string(\sprintf(' ("%s" at line %d).', $this->getTemplateName(), $this->getTemplateLine()))
->raw(", E_USER_DEPRECATED);\n")
->raw(".")
->string(\sprintf(' in "%s" at line %d.', $this->getTemplateName(), $this->getTemplateLine()))
->raw(");\n")
;
}
}
+26 -3
View File
@@ -11,6 +11,7 @@
namespace Twig\TokenParser;
use Twig\Error\SyntaxError;
use Twig\Node\DeprecatedNode;
use Twig\Node\Node;
use Twig\Token;
@@ -21,6 +22,8 @@ use Twig\Token;
* {% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' %}
* {% extends 'layout.html.twig' %}
*
* {% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' package="foo/bar" version="1.1" %}
*
* @author Yonel Ceruto <yonelceruto@gmail.com>
*
* @internal
@@ -29,11 +32,31 @@ final class DeprecatedTokenParser extends AbstractTokenParser
{
public function parse(Token $token): Node
{
$expr = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
$expressionParser = $this->parser->getExpressionParser();
$expr = $expressionParser->parseExpression();
$node = new DeprecatedNode($expr, $token->getLine(), $this->getTag());
$this->parser->getStream()->expect(Token::BLOCK_END_TYPE);
while ($stream->test(Token::NAME_TYPE)) {
$k = $stream->getCurrent()->getValue();
$stream->next();
$stream->expect(Token::OPERATOR_TYPE, '=');
return new DeprecatedNode($expr, $token->getLine(), $this->getTag());
switch ($k) {
case 'package':
$node->setNode('package', $expressionParser->parseExpression());
break;
case 'version':
$node->setNode('version', $expressionParser->parseExpression());
break;
default:
throw new SyntaxError(\sprintf('Unknown "%s" option.', $k), $stream->getCurrent()->getLine(), $stream->getSourceContext());
}
}
$stream->expect(Token::BLOCK_END_TYPE);
return $node;
}
public function getTag(): string
+6
View File
@@ -46,6 +46,7 @@ final class TwigFilter
'preserves_safety' => null,
'node_class' => FilterExpression::class,
'deprecated' => false,
'deprecating_package' => '',
'alternative' => null,
], $options);
}
@@ -128,6 +129,11 @@ final class TwigFilter
return (bool) $this->options['deprecated'];
}
public function getDeprecatingPackage(): string
{
return $this->options['deprecating_package'];
}
public function getDeprecatedVersion(): string
{
return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated'];
+6
View File
@@ -44,6 +44,7 @@ final class TwigFunction
'is_safe_callback' => null,
'node_class' => FunctionExpression::class,
'deprecated' => false,
'deprecating_package' => '',
'alternative' => null,
], $options);
}
@@ -116,6 +117,11 @@ final class TwigFunction
return (bool) $this->options['deprecated'];
}
public function getDeprecatingPackage(): string
{
return $this->options['deprecating_package'];
}
public function getDeprecatedVersion(): string
{
return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated'];
+6
View File
@@ -38,6 +38,7 @@ final class TwigTest
'is_variadic' => false,
'node_class' => TestExpression::class,
'deprecated' => false,
'deprecating_package' => '',
'alternative' => null,
'one_mandatory_argument' => false,
], $options);
@@ -83,6 +84,11 @@ final class TwigTest
return (bool) $this->options['deprecated'];
}
public function getDeprecatingPackage(): string
{
return $this->options['deprecating_package'];
}
public function getDeprecatedVersion(): string
{
return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated'];
@@ -0,0 +1,10 @@
--TEST--
Deprecating a template with "deprecated" tag
--TEMPLATE--
{% deprecated 'The "index.twig" template is deprecated, use "greeting.twig" instead.' package="foo/bar" %}
Hello Fabien
--DATA--
return []
--EXPECT--
Hello Fabien
@@ -0,0 +1,10 @@
--TEST--
Deprecating a template with "deprecated" tag
--TEMPLATE--
{% deprecated 'The "index.twig" template is deprecated, use "greeting.twig" instead.' package="foo/bar" version=1.1 %}
Hello Fabien
--DATA--
return []
--EXPECT--
Hello Fabien
+10 -4
View File
@@ -39,25 +39,29 @@ class DeprecatedTest extends NodeTestCase
$expr = new ConstantExpression('This section is deprecated', 1);
$node = new DeprecatedNode($expr, 1, 'deprecated');
$node->setSourceContext(new Source('', 'foo.twig'));
$node->setNode('package', new ConstantExpression('twig/twig', 1));
$node->setNode('version', new ConstantExpression('1.1', 1));
$tests[] = [$node, <<<EOF
// line 1
@trigger_error("This section is deprecated"." (\"foo.twig\" at line 1).", E_USER_DEPRECATED);
trigger_deprecation("twig/twig", "1.1", "This section is deprecated"." in \"foo.twig\" at line 1.");
EOF
];
$t = new Node([
new ConstantExpression(true, 1),
new DeprecatedNode($expr, 2, 'deprecated'),
$dep = new DeprecatedNode($expr, 2, 'deprecated'),
], [], 1);
$node = new IfNode($t, null, 1);
$node->setSourceContext(new Source('', 'foo.twig'));
$dep->setNode('package', new ConstantExpression('twig/twig', 1));
$dep->setNode('version', new ConstantExpression('1.1', 1));
$tests[] = [$node, <<<EOF
// line 1
if (true) {
// line 2
@trigger_error("This section is deprecated"." (\"foo.twig\" at line 2).", E_USER_DEPRECATED);
trigger_deprecation("twig/twig", "1.1", "This section is deprecated"." in \"foo.twig\" at line 2.");
}
EOF
];
@@ -68,6 +72,8 @@ EOF
$expr = new FunctionExpression('foo', new Node(), 1);
$node = new DeprecatedNode($expr, 1, 'deprecated');
$node->setSourceContext(new Source('', 'foo.twig'));
$node->setNode('package', new ConstantExpression('twig/twig', 1));
$node->setNode('version', new ConstantExpression('1.1', 1));
$compiler = $this->getCompiler($environment);
$varName = $compiler->getVarName();
@@ -75,7 +81,7 @@ EOF
$tests[] = [$node, <<<EOF
// line 1
\$$varName = foo();
@trigger_error(\$$varName." (\"foo.twig\" at line 1).", E_USER_DEPRECATED);
trigger_deprecation("twig/twig", "1.1", \$$varName." in \"foo.twig\" at line 1.");
EOF
, $environment];
+2 -2
View File
@@ -25,12 +25,12 @@ class DeprecationCollectorTest extends TestCase
public function testCollect()
{
$twig = new Environment(new ArrayLoader());
$twig->addFunction(new TwigFunction('deprec', [$this, 'deprec'], ['deprecated' => '1.1']));
$twig->addFunction(new TwigFunction('deprec', [$this, 'deprec'], ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar']));
$collector = new DeprecationCollector($twig);
$deprecations = $collector->collect(new Iterator());
$this->assertEquals(['Twig Function "deprec" is deprecated since version 1.1 in deprec.twig at line 1.'], $deprecations);
$this->assertEquals(['Since foo/bar 1.1: Twig Function "deprec" is deprecated in deprec.twig at line 1.'], $deprecations);
}
public function deprec()