Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecation notices #1769

Merged
merged 13 commits into from Aug 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 21 additions & 16 deletions .travis.yml
@@ -1,28 +1,33 @@
language: php

php:
- 5.2
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
- nightly
- 5.2
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
- nightly

allow_failures:
- php: nightly

env:
- TWIG_EXT=no
- TWIG_EXT=yes
- TWIG_EXT=no
- TWIG_EXT=yes

before_script:
- if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && sudo make install"; fi
- if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
- if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && sudo make install"; fi
- if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
- if [ ${TRAVIS_PHP_VERSION:0:3} == "5.2" ]; then sed -i.bak "s|vendor/autoload.php|test/bootstrap.php|" phpunit.xml.dist; fi

install:
# Composer is not available on PHP 5.2
- if [ ${TRAVIS_PHP_VERSION:0:3} != "5.2" ]; then travis_retry composer install; fi

matrix:
exclude:
- php: hhvm
env: TWIG_EXT=yes
- php: nightly
env: TWIG_EXT=yes
exclude:
- php: hhvm
env: TWIG_EXT=yes
- php: nightly
env: TWIG_EXT=yes
5 changes: 3 additions & 2 deletions CHANGELOG
@@ -1,6 +1,7 @@
* 1.20.1 (2015-XX-XX)
* 1.21.0 (2015-XX-XX)

* n/a
* added deprecation notices for deprecated features
* added a deprecation "framework" for filters/functions/tests and test fixtures

* 1.20.0 (2015-08-12)

Expand Down
4 changes: 4 additions & 0 deletions composer.json
Expand Up @@ -29,6 +29,10 @@
"require": {
"php": ">=5.2.7"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7",
"symfony/debug": "~2.7"
},
"autoload": {
"psr-0" : {
"Twig_" : "lib/"
Expand Down
17 changes: 17 additions & 0 deletions doc/advanced.rst
Expand Up @@ -267,6 +267,23 @@ arguments, but after the environment and the context. For instance, a call to
``'foo'|a_path_b()`` will result in the following arguments to be passed to
the filter: ``('a', 'b', 'foo')``.

Deprecated Filters
~~~~~~~~~~~~~~~~~~

.. versionadded:: 1.21
Support for deprecated filters was added in Twig 1.21.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is a problem, but normally you have an empty line after the block start.
From what I know the first contains options like :lineno: but I'm not sure if Sphixdoc will accept tex directly after the block start :)

.. versionadded:: 1.21

    Support for deprecated filters was added in Twig 1.21.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, versionadded is special and does not work if you put an empty line before the directive body


You can mark a filter as being deprecated by setting the ``deprecated`` option
to ``true``. You can also give an alternative filter that replaces the
deprecated one when that makes sense::

$filter = new Twig_SimpleFilter('obsolete', function () {
// ...
}, array('deprecated' => true, 'alternative' => 'new_one'));

When a filter is deprecated, Twig emits a deprecation notice when compiling a
template using it. See :ref:`deprecation-notices` for more information.

Functions
---------

Expand Down
6 changes: 6 additions & 0 deletions doc/deprecated.rst
Expand Up @@ -5,6 +5,12 @@ This document lists all deprecated features in Twig. Deprecated features are
kept for backward compatibility and removed in the next major release (a
feature that was deprecated in Twig 1.x is removed in Twig 2.0).

Deprecation Notices
-------------------

As of Twig 1.21, Twig generates deprecation notices when a template uses
deprecated features. See :ref:`deprecation-notices` for more information.

Token Parsers
-------------

Expand Down
58 changes: 58 additions & 0 deletions doc/recipes.rst
@@ -1,6 +1,64 @@
Recipes
=======

.. _deprecation-notices:

Displaying Deprecation Notices
------------------------------

.. versionadded:: 1.21
This works as of Twig 1.21.

Deprecated features generate deprecation notices (via a call to the
``trigger_error()`` PHP function). By default, they are silenced and never
displayed nor logged.

To easily remove all deprecated feature usages from your templates, write and
run a script along the lines of the following::

require_once __DIR__.'/vendor/autoload.php';

$twig = create_your_twig_env();

$deprecations = new Twig_Util_DeprecationCollector($twig);

print_r($deprecations->collectDir(__DIR__.'/templates'));

The ``collectDir()`` method compiles all templates found in a directory,
catches deprecation notices, and return them.

.. tip::

If your templates are not stored on the filesystem, use the ``collect()``
method instead which takes an ``Iterator``; the iterator must return
template names as keys and template contents as values (as done by
``Twig_Util_TemplateDirIterator``).

However, this code won't find all deprecations (like using deprecated some Twig
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like using some deprecated Twig instead of like using deprecated some Twig

classes). To catch all notices, register a custom error handler like the one
below::

$deprecations = array();
set_error_handler(function ($type, $msg) use (&$deprecations) {
if (E_USER_DEPRECATED === $type) {
$deprecations[] = $msg;
}
});

// run your application

print_r($deprecations);

Note that most deprecation notices are triggered during **compilation**, so
they won't be generated when templates are already cached.

.. tip::

If you want to manage the deprecation notices from your PHPUnit tests, have
a look at the `symfony/phpunit-bridge
<https://github.com/symfony/phpunit-bridge>`_ package, which eases the
process a lot.

Making a Layout conditional
---------------------------

Expand Down
6 changes: 6 additions & 0 deletions lib/Twig/Autoloader.php
Expand Up @@ -9,10 +9,14 @@
* file that was distributed with this source code.
*/

@trigger_error('The Twig_Autoloader class is deprecated and will be removed in 2.0. Use Composer instead.', E_USER_DEPRECATED);

/**
* Autoloads Twig classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated Use Composer instead. Will be removed in Twig 2.0.
*/
class Twig_Autoloader
{
Expand All @@ -23,6 +27,8 @@ class Twig_Autoloader
*/
public static function register($prepend = false)
{
@trigger_error('Using Twig_Autoloader is deprecated. Use Composer instead.', E_USER_DEPRECATED);

if (PHP_VERSION_ID < 50300) {
spl_autoload_register(array(__CLASS__, 'autoload'));
} else {
Expand Down
4 changes: 2 additions & 2 deletions lib/Twig/BaseNodeVisitor.php
Expand Up @@ -43,8 +43,8 @@ final public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
/**
* Called before child nodes are visited.
*
* @param Twig_Node $node The node to visit
* @param Twig_Environment $env The Twig environment instance
* @param Twig_Node $node The node to visit
* @param Twig_Environment $env The Twig environment instance
*
* @return Twig_Node The modified node
*/
Expand Down
28 changes: 24 additions & 4 deletions lib/Twig/Environment.php
Expand Up @@ -86,6 +86,8 @@ public function __construct(Twig_LoaderInterface $loader = null, $options = arra
{
if (null !== $loader) {
$this->setLoader($loader);
} else {
@trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated.', E_USER_DEPRECATED);
}

$options = array_merge(array(
Expand Down Expand Up @@ -448,6 +450,8 @@ public function resolveTemplate($names)
*/
public function clearTemplateCache()
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);

$this->loadedTemplates = array();
}

Expand Down Expand Up @@ -711,6 +715,8 @@ public function addExtension(Twig_ExtensionInterface $extension)
*/
public function removeExtension($name)
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);

if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
}
Expand Down Expand Up @@ -830,6 +836,8 @@ public function addFilter($name, $filter = null)
if ($name instanceof Twig_SimpleFilter) {
$filter = $name;
$name = $filter->getName();
} else {
@trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".', __METHOD__, $name), E_USER_DEPRECATED);
}

if ($this->extensionInitialized) {
Expand Down Expand Up @@ -919,6 +927,8 @@ public function addTest($name, $test = null)
if ($name instanceof Twig_SimpleTest) {
$test = $name;
$name = $test->getName();
} else {
@trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".', __METHOD__, $name), E_USER_DEPRECATED);
}

if ($this->extensionInitialized) {
Expand Down Expand Up @@ -977,6 +987,8 @@ public function addFunction($name, $function = null)
if ($name instanceof Twig_SimpleFunction) {
$function = $name;
$name = $function->getName();
} else {
@trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), E_USER_DEPRECATED);
}

if ($this->extensionInitialized) {
Expand Down Expand Up @@ -1067,11 +1079,11 @@ public function addGlobal($name, $value)
$this->globals = $this->initGlobals();
}

/* This condition must be uncommented in Twig 2.0
if (!array_key_exists($name, $this->globals)) {
throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
// The deprecation notice must be turned into the following exception in Twig 2.0
@trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated.', $name), E_USER_DEPRECATED);
//throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see the point here, to use a deprecated error notice instead of an exception and then suggest to actually use the same exception (unless I read it wrong) in 2.0 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Taluu the deprecation does not prevent it from working in 1.x. We are deprecating this possibility but not breaking it.
2.0 will forbid it, which will allow to simplify some code (as we will have the list of globals known without allowing to add new ones)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my bad, didn't see that this condition was commented (see removed line 1072), I thought that previously this exception was already thrown, that is what was bothering me.

}
*/
}

if ($this->extensionInitialized || $this->runtimeInitialized) {
Expand Down Expand Up @@ -1186,7 +1198,7 @@ protected function initExtensions()
}

$this->extensionInitialized = true;
$this->parsers = new Twig_TokenParserBroker();
$this->parsers = new Twig_TokenParserBroker(array(), array(), false);
$this->filters = array();
$this->functions = array();
$this->tests = array();
Expand All @@ -1206,6 +1218,8 @@ protected function initExtension(Twig_ExtensionInterface $extension)
foreach ($extension->getFilters() as $name => $filter) {
if ($filter instanceof Twig_SimpleFilter) {
$name = $filter->getName();
} else {
@trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated. Use Twig_SimpleFilter instead.', get_class($filter), $name), E_USER_DEPRECATED);
}

$this->filters[$name] = $filter;
Expand All @@ -1215,6 +1229,8 @@ protected function initExtension(Twig_ExtensionInterface $extension)
foreach ($extension->getFunctions() as $name => $function) {
if ($function instanceof Twig_SimpleFunction) {
$name = $function->getName();
} else {
@trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated. Use Twig_SimpleFunction instead.', get_class($filter), $name), E_USER_DEPRECATED);
}

$this->functions[$name] = $function;
Expand All @@ -1224,6 +1240,8 @@ protected function initExtension(Twig_ExtensionInterface $extension)
foreach ($extension->getTests() as $name => $test) {
if ($test instanceof Twig_SimpleTest) {
$name = $test->getName();
} else {
@trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated. Use Twig_SimpleTest instead.', get_class($filter), $name), E_USER_DEPRECATED);
}

$this->tests[$name] = $test;
Expand All @@ -1234,6 +1252,8 @@ protected function initExtension(Twig_ExtensionInterface $extension)
if ($parser instanceof Twig_TokenParserInterface) {
$this->parsers->addTokenParser($parser);
} elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
@trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated.', E_USER_DEPRECATED);

$this->parsers->addTokenParserBroker($parser);
} else {
throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
Expand Down
20 changes: 20 additions & 0 deletions lib/Twig/ExpressionParser.php
Expand Up @@ -578,6 +578,16 @@ protected function getFunctionNodeClass($name, $line)
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
}

if ($function instanceof Twig_SimpleFunction && $function->isDeprecated()) {
$message = sprintf('Twig Function "%s" is deprecated', $function->getName());
if ($test->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $function->getAlternative());
}
$message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line);

@trigger_error($message, E_USER_DEPRECATED);
}

if ($function instanceof Twig_SimpleFunction) {
return $function->getNodeClass();
}
Expand All @@ -598,6 +608,16 @@ protected function getFilterNodeClass($name, $line)
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
}

if ($filter instanceof Twig_SimpleFilter && $filter->isDeprecated()) {
$message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
if ($test->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $filter->getAlternative());
}
$message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line);

@trigger_error($message, E_USER_DEPRECATED);
}

if ($filter instanceof Twig_SimpleFilter) {
return $filter->getNodeClass();
}
Expand Down