feature #2696 [RFC] Added new "deprecated" tag (yceruto)

This PR was merged into the 1.x branch.

Discussion
----------

[RFC] Added new "deprecated" tag

```twig
{% deprecated 'The message...' %}
```

This new tag would allow us to define a deprecation warning anywhere within a template, useful for Frameworks, bundles, etc. where breaking the BC is frequently when templates are involved.

It will be easy to guarantee a smooth migration path when we want e.g. rename/remove a template or block, as well as displaying an accurate depreciation message.

**Deprecating a whole template `{# base.twig #}`**
```twig
{% deprecated 'The "' ~ _self ~ '" template is deprecated, use "layout.twig" instead' %}

{% extends 'layout.twig' %}
```
If we're extending from this template `{% extends 'base.twig' %}` then:
```
The "base.twig" template is deprecated, use "layout.twig" instead ("base.twig" at line 1).
```
**Deprecating a block {# greeting/blocks.twig #}**
```twig
{% block hey %}
    {% deprecated 'The "hey" block is deprecated, use "greet" block instead' %}

    {{ block('greet') }}
{% endblock %}

{% block greet %}
    Hey you!
{% endblock %}
```
If we're using this block `{{ block('hey') }}` then:
```
The "hey" block is deprecated, use "greet" block instead ("greeting/blocks.twig" at line 2).
```
also other examples come from my mind like deprecating macros, and any other extension point.

Commits
-------

2ab43383 Added "deprecated" tag
This commit is contained in:
Fabien Potencier
2018-07-31 14:56:40 +02:00
12 changed files with 270 additions and 0 deletions
+1
View File
@@ -1,5 +1,6 @@
* 1.36.0 (2018-XX-XX)
* added the "deprecated" tag
* added support for dynamically named tests
* fixed filesystem loader throwing an exception instead of returning false
+30
View File
@@ -0,0 +1,30 @@
``deprecated``
==============
.. versionadded:: 1.36 and 2.6
The ``deprecated`` tag was added in Twig 1.36 and 2.6.
Twig generates a deprecation notice (via a call to the ``trigger_error()``
PHP function) where the ``deprecated`` tag is used in a template:
.. code-block:: jinja
{# base.twig #}
{% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' %}
{% extends 'layout.twig' %}
Also you can deprecate a block in the following way:
.. code-block:: jinja
{% block hey %}
{% deprecated 'The "hey" block is deprecated, use "greet" instead.' %}
{{ block('greet') }}
{% endblock %}
{% block greet %}
Hey you!
{% endblock %}
Note that by default, the deprecation notices are silenced and never displayed nor logged.
See :ref:`deprecation-notices` to learn how to handle them.
+1
View File
@@ -23,3 +23,4 @@ Tags
use
verbatim
with
deprecated
+1
View File
@@ -136,6 +136,7 @@ class Twig_Extension_Core extends Twig_Extension
new Twig_TokenParser_Do(),
new Twig_TokenParser_Embed(),
new Twig_TokenParser_With(),
new Twig_TokenParser_Deprecated(),
);
}
+49
View File
@@ -0,0 +1,49 @@
<?php
/*
* 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.
*/
/**
* Represents a deprecated node.
*
* @author Yonel Ceruto <yonelceruto@gmail.com>
*/
class Twig_Node_Deprecated extends Twig_Node
{
public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
{
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$expr = $this->getNode('expr');
if ($expr instanceof Twig_Node_Expression_Constant) {
$compiler->write('@trigger_error(')
->subcompile($expr);
} else {
$varName = $compiler->getVarName();
$compiler->write(sprintf('$%s = ', $varName))
->subcompile($expr)
->raw(";\n")
->write(sprintf('@trigger_error($%s', $varName));
}
$compiler
->raw('.')
->string(sprintf(' ("%s" at line %d).', $this->getTemplateName(), $this->getTemplateLine()))
->raw(", E_USER_DEPRECATED);\n")
;
}
}
class_alias('Twig_Node_Deprecated', 'Twig\Node\DeprecatedNode', false);
+42
View File
@@ -0,0 +1,42 @@
<?php
/*
* 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.
*/
/**
* Deprecates a section of a template.
*
* <pre>
* {% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' %}
*
* {% extends 'layout.html.twig' %}
* </pre>
*
* @author Yonel Ceruto <yonelceruto@gmail.com>
*
* @final
*/
class Twig_TokenParser_Deprecated extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_Deprecated($expr, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'deprecated';
}
}
class_alias('Twig_TokenParser_Deprecated', 'Twig\TokenParser\DeprecatedTokenParser', false);
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Twig\Node;
class_exists('Twig_Node_Deprecated');
if (\false) {
class DeprecatedNode extends \Twig_Node_Deprecated
{
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Twig\TokenParser;
class_exists('Twig_TokenParser_Deprecated');
if (\false) {
class DeprecatedTokenParser extends \Twig_TokenParser_Deprecated
{
}
}
@@ -0,0 +1,20 @@
--TEST--
Deprecating a block with "deprecated" tag
--TEMPLATE--
{% use 'greeting.twig' %}
{{ block('welcome') }}
--TEMPLATE(greeting.twig)--
{% block welcome %}
{% deprecated 'The "welcome" block is deprecated, use "hello" instead.' %}
{{ block('hello') }}
{% endblock %}
{% block hello %}
Hello Fabien
{% endblock %}
--DATA--
return array()
--EXPECT--
Hello Fabien
@@ -0,0 +1,21 @@
--TEST--
Deprecating a macro with "deprecated" tag
--TEMPLATE--
{% import 'greeting.twig' as greeting %}
{{ greeting.welcome('Fabien') }}
--TEMPLATE(greeting.twig)--
{% macro welcome(name) %}
{% deprecated 'The "welcome" macro is deprecated, use "hello" instead.' %}
{% import _self as self %}
{{ self.hello(name) }}
{% endmacro %}
{% macro hello(name) %}
Hello {{ name }}
{% endmacro %}
--DATA--
return array()
--EXPECT--
Hello Fabien
@@ -0,0 +1,12 @@
--TEST--
Deprecating a template with "deprecated" tag
--TEMPLATE--
{% extends 'greeting.twig' %}
{% deprecated 'The "index.twig" template is deprecated, use "greeting.twig" instead.' %}
--TEMPLATE(greeting.twig)--
Hello Fabien
--DATA--
return array()
--EXPECT--
Hello Fabien
+71
View File
@@ -0,0 +1,71 @@
<?php
/*
* 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.
*/
class Twig_Tests_Node_DeprecatedTest extends Twig_Test_NodeTestCase
{
public function testConstructor()
{
$expr = new Twig_Node_Expression_Constant('foo', 1);
$node = new Twig_Node_Deprecated($expr, 1);
$this->assertEquals($expr, $node->getNode('expr'));
}
public function getTests()
{
$tests = array();
$expr = new Twig_Node_Expression_Constant('This section is deprecated', 1);
$node = new Twig_Node_Deprecated($expr, 1, 'deprecated');
$node->setTemplateName('foo.twig');
$tests[] = array($node, <<<EOF
// line 1
@trigger_error("This section is deprecated"." (\"foo.twig\" at line 1).", E_USER_DEPRECATED);
EOF
);
$t = new Twig_Node(array(
new Twig_Node_Expression_Constant(true, 1),
new Twig_Node_Deprecated($expr, 2, 'deprecated'),
), array(), 1);
$node = new Twig_Node_If($t, null, 1);
$node->setTemplateName('foo.twig');
$tests[] = array($node, <<<EOF
// line 1
if (true) {
// line 2
@trigger_error("This section is deprecated"." (\"foo.twig\" at line 2).", E_USER_DEPRECATED);
}
EOF
);
$environment = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$environment->addFunction(new Twig_SimpleFunction('foo', 'foo', array()));
$expr = new Twig_Node_Expression_Function('foo', new Twig_Node(), 1);
$node = new Twig_Node_Deprecated($expr, 1, 'deprecated');
$node->setTemplateName('foo.twig');
$compiler = $this->getCompiler($environment);
$varName = $compiler->getVarName();
$tests[] = array($node, <<<EOF
// line 1
\$$varName = foo();
@trigger_error(\$$varName." (\"foo.twig\" at line 1).", E_USER_DEPRECATED);
EOF
, $environment);
return $tests;
}
}