mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-15 03:46:39 +00:00
Merge branch '3.x' into 4.x
* 3.x: Improve the way one can deprecate a Twig callable Bump version Prepare the 3.14.0 release Fix a security issue when an included sandboxed template has been loaded before without the sandbox context Fix test fix the version mergeGlobals() is deprecated since Tweak code Fix CS Add more tests Remove unused private methods Bump version
This commit is contained in:
+16
-10
@@ -277,21 +277,27 @@ filter: ``('a', 'b', 'foo')``.
|
||||
Deprecated Filters
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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::
|
||||
.. versionadded:: 3.15
|
||||
|
||||
The ``deprecation_info`` option was added in Twig 3.15.
|
||||
|
||||
You can mark a filter as being deprecated by setting the ``deprecation_info``
|
||||
option::
|
||||
|
||||
$filter = new \Twig\TwigFilter('obsolete', function () {
|
||||
// ...
|
||||
}, ['deprecated' => true, 'alternative' => 'new_one']);
|
||||
}, ['deprecation_info' => new DeprecatedCallableInfo('twig/twig', '3.11', 'new_one')]);
|
||||
|
||||
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::
|
||||
The ``DeprecatedCallableInfo`` constructor takes the following parameters:
|
||||
|
||||
$filter = new \Twig\TwigFilter('obsolete', function () {
|
||||
// ...
|
||||
}, ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar']);
|
||||
* The Composer package name that defines the filter;
|
||||
* The version when the filter was deprecated.
|
||||
|
||||
Optionally, you can also provide the following parameters about an alternative:
|
||||
|
||||
* The package name that contains the alternative filter;
|
||||
* The alternative filter name that replaces the deprecated one;
|
||||
* The package version that added the alternative filter.
|
||||
|
||||
When a filter is deprecated, Twig emits a deprecation notice when compiling a
|
||||
template using it. See :ref:`deprecation-notices` for more information.
|
||||
|
||||
@@ -39,10 +39,35 @@ abstract class AbstractTwigCallable implements TwigCallableInterface
|
||||
'needs_context' => false,
|
||||
'needs_charset' => false,
|
||||
'is_variadic' => false,
|
||||
'deprecation_info' => null,
|
||||
'deprecated' => false,
|
||||
'deprecating_package' => '',
|
||||
'alternative' => null,
|
||||
], $options);
|
||||
|
||||
if ($this->options['deprecation_info'] && !$this->options['deprecation_info'] instanceof DeprecatedCallableInfo) {
|
||||
throw new \LogicException(\sprintf('The "deprecation_info" option must be an instance of "%s".', DeprecatedCallableInfo::class));
|
||||
}
|
||||
|
||||
if ($this->options['deprecated']) {
|
||||
if ($this->options['deprecation_info']) {
|
||||
throw new \LogicException('When setting the "deprecation_info" option, you need to remove the obsolete deprecated options.');
|
||||
}
|
||||
|
||||
trigger_deprecation('twig/twig', '3.15', 'Using the "deprecated", "deprecating_package", and "alternative" options is deprecated, pass a "deprecation_info" one instead.');
|
||||
|
||||
$this->options['deprecation_info'] = new DeprecatedCallableInfo(
|
||||
$this->options['deprecating_package'],
|
||||
$this->options['deprecated'],
|
||||
null,
|
||||
$this->options['alternative'],
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->options['deprecation_info']) {
|
||||
$this->options['deprecation_info']->setName($name);
|
||||
$this->options['deprecation_info']->setType($this->getType());
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
@@ -107,21 +132,41 @@ abstract class AbstractTwigCallable implements TwigCallableInterface
|
||||
|
||||
public function isDeprecated(): bool
|
||||
{
|
||||
return (bool) $this->options['deprecated'];
|
||||
return (bool) $this->options['deprecation_info'];
|
||||
}
|
||||
|
||||
public function triggerDeprecation(?string $file = null, ?int $line = null): void
|
||||
{
|
||||
$this->options['deprecation_info']->triggerDeprecation($file, $line);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.15
|
||||
*/
|
||||
public function getDeprecatingPackage(): string
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class);
|
||||
|
||||
return $this->options['deprecating_package'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.15
|
||||
*/
|
||||
public function getDeprecatedVersion(): string
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class);
|
||||
|
||||
return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.15
|
||||
*/
|
||||
public function getAlternative(): ?string
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class);
|
||||
|
||||
return $this->options['alternative'];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class DeprecatedCallableInfo
|
||||
{
|
||||
private string $type;
|
||||
private string $name;
|
||||
|
||||
public function __construct(
|
||||
private string $package,
|
||||
private string $version,
|
||||
private ?string $altName = null,
|
||||
private ?string $altPackage = null,
|
||||
private ?string $altVersion = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function setType(string $type): void
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function triggerDeprecation(?string $file = null, ?int $line = null): void
|
||||
{
|
||||
$message = \sprintf('Twig %s "%s" is deprecated', ucfirst($this->type), $this->name);
|
||||
|
||||
if ($this->altName) {
|
||||
$message .= \sprintf('; use "%s"', $this->altName);
|
||||
if ($this->altPackage) {
|
||||
$message .= \sprintf(' from the "%s" package', $this->altPackage);
|
||||
}
|
||||
if ($this->altVersion) {
|
||||
$message .= \sprintf(' (available since version %s)', $this->altVersion);
|
||||
}
|
||||
$message .= ' instead';
|
||||
}
|
||||
|
||||
if ($file) {
|
||||
$message .= \sprintf(' in %s', $file);
|
||||
if ($line) {
|
||||
$message .= \sprintf(' at line %d', $line);
|
||||
}
|
||||
}
|
||||
|
||||
$message .= '.';
|
||||
|
||||
trigger_deprecation($this->package, $this->version, $message);
|
||||
}
|
||||
}
|
||||
@@ -815,6 +815,19 @@ class Environment
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
* @deprecated since Twig 3.14
|
||||
*/
|
||||
public function mergeGlobals(array $context): array
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.14', 'The "%s" method is deprecated.', __METHOD__);
|
||||
|
||||
return $context + $this->getGlobals();
|
||||
}
|
||||
|
||||
/**
|
||||
>>>>>>> 3.x
|
||||
* @internal
|
||||
*
|
||||
* @return array<string, array{precedence: int, class: class-string<AbstractUnary>}>
|
||||
|
||||
@@ -719,15 +719,8 @@ class ExpressionParser
|
||||
|
||||
if ($test->isDeprecated()) {
|
||||
$stream = $this->parser->getStream();
|
||||
$message = \sprintf('Twig Test "%s" is deprecated', $test->getName());
|
||||
|
||||
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_deprecation($test->getDeprecatingPackage(), $test->getDeprecatedVersion(), $message);
|
||||
$test->triggerDeprecation($src->getPath() ?: $src->getName(), $stream->getCurrent()->getLine());
|
||||
}
|
||||
|
||||
return $test;
|
||||
@@ -743,14 +736,8 @@ class ExpressionParser
|
||||
}
|
||||
|
||||
if ($function->isDeprecated()) {
|
||||
$message = \sprintf('Twig Function "%s" is deprecated', $function->getName());
|
||||
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_deprecation($function->getDeprecatingPackage(), $function->getDeprecatedVersion(), $message);
|
||||
$function->triggerDeprecation($src->getPath() ?: $src->getName(), $line);
|
||||
}
|
||||
|
||||
return $function;
|
||||
@@ -766,14 +753,8 @@ class ExpressionParser
|
||||
}
|
||||
|
||||
if ($filter->isDeprecated()) {
|
||||
$message = \sprintf('Twig Filter "%s" is deprecated', $filter->getName());
|
||||
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_deprecation($filter->getDeprecatingPackage(), $filter->getDeprecatedVersion(), $message);
|
||||
$filter->triggerDeprecation($src->getPath() ?: $src->getName(), $line);
|
||||
}
|
||||
|
||||
return $filter;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\DeprecatedCallableInfo;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
@@ -1386,13 +1387,6 @@ final class CoreExtension extends AbstractExtension
|
||||
if (!$alreadySandboxed = $sandbox->isSandboxed()) {
|
||||
$sandbox->enableSandbox();
|
||||
}
|
||||
|
||||
foreach ((\is_array($template) ? $template : [$template]) as $name) {
|
||||
// if a Template instance is passed, it might have been instantiated outside of a sandbox, check security
|
||||
if ($name instanceof TemplateWrapper || $name instanceof Template) {
|
||||
$name->unwrap()->checkSecurity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -1403,9 +1397,15 @@ final class CoreExtension extends AbstractExtension
|
||||
if (!$ignoreMissing) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
return $loaded ? $loaded->render($variables) : '';
|
||||
if ($isSandboxed) {
|
||||
$loaded->unwrap()->checkSecurity();
|
||||
}
|
||||
|
||||
return $loaded->render($variables);
|
||||
} finally {
|
||||
if ($isSandboxed && !$alreadySandboxed) {
|
||||
$sandbox->disableSandbox();
|
||||
|
||||
@@ -406,14 +406,6 @@ final class ModuleNode extends Node
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node instanceof TextNode && ctype_space($node->getAttribute('data'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node instanceof BlockReferenceNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$traitable = false;
|
||||
break;
|
||||
}
|
||||
@@ -482,19 +474,4 @@ final class ModuleNode extends Node
|
||||
throw new \LogicException('Trait templates can only be constant nodes.');
|
||||
}
|
||||
}
|
||||
|
||||
private function hasNodeOutputNodes(Node $node): bool
|
||||
{
|
||||
if ($node instanceof NodeOutputInterface) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($node as $child) {
|
||||
if ($this->hasNodeOutputNodes($child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ use Twig\TokenStream;
|
||||
* {% types {foo: 'int', bar?: 'string'} %}
|
||||
*
|
||||
* @author Jeroen Versteeg <jeroen@alisqi.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class TypesTokenParser extends AbstractTokenParser
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Twig\Tests;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Twig\DeprecatedCallableInfo;
|
||||
|
||||
class DeprecatedCallableInfoTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideTestsForTriggerDeprecation
|
||||
*/
|
||||
public function testTriggerDeprecation($expected, DeprecatedCallableInfo $info)
|
||||
{
|
||||
$info->setType('function');
|
||||
$info->setName('foo');
|
||||
|
||||
$deprecations = [];
|
||||
try {
|
||||
set_error_handler(function ($type, $msg) use (&$deprecations) {
|
||||
if (\E_USER_DEPRECATED === $type) {
|
||||
$deprecations[] = $msg;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$info->triggerDeprecation('foo.twig', 1);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$this->assertSame([$expected], $deprecations);
|
||||
}
|
||||
|
||||
public static function provideTestsForTriggerDeprecation(): iterable
|
||||
{
|
||||
yield ['Since foo/bar 1.1: Twig Function "foo" is deprecated in foo.twig at line 1.', new DeprecatedCallableInfo('foo/bar', '1.1')];
|
||||
yield ['Since foo/bar 1.1: Twig Function "foo" is deprecated; use "alt_foo" from the "all/bar" package (available since version 12.10) instead in foo.twig at line 1.', new DeprecatedCallableInfo('foo/bar', '1.1', 'alt_foo', 'all/bar', '12.10')];
|
||||
yield ['Since foo/bar 1.1: Twig Function "foo" is deprecated; use "alt_foo" from the "all/bar" package instead in foo.twig at line 1.', new DeprecatedCallableInfo('foo/bar', '1.1', 'alt_foo', 'all/bar')];
|
||||
yield ['Since foo/bar 1.1: Twig Function "foo" is deprecated; use "alt_foo" instead in foo.twig at line 1.', new DeprecatedCallableInfo('foo/bar', '1.1', 'alt_foo')];
|
||||
}
|
||||
|
||||
public function testTriggerDeprecationWithoutFileOrLine()
|
||||
{
|
||||
$info = new DeprecatedCallableInfo('foo/bar', '1.1');
|
||||
$info->setType('function');
|
||||
$info->setName('foo');
|
||||
|
||||
$deprecations = [];
|
||||
try {
|
||||
set_error_handler(function ($type, $msg) use (&$deprecations) {
|
||||
if (\E_USER_DEPRECATED === $type) {
|
||||
$deprecations[] = $msg;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$info->triggerDeprecation();
|
||||
$info->triggerDeprecation('foo.twig');
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$this->assertSame([
|
||||
'Since foo/bar 1.1: Twig Function "foo" is deprecated.',
|
||||
'Since foo/bar 1.1: Twig Function "foo" is deprecated in foo.twig.',
|
||||
], $deprecations);
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,13 @@ namespace Twig\Tests\Extension;
|
||||
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Extension\CoreExtension;
|
||||
use Twig\Extension\SandboxExtension;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Sandbox\SecurityError;
|
||||
use Twig\Sandbox\SecurityPolicy;
|
||||
|
||||
class CoreTest extends TestCase
|
||||
{
|
||||
@@ -337,6 +342,40 @@ class CoreTest extends TestCase
|
||||
[1, 42, "\x00\x34\x32"],
|
||||
];
|
||||
}
|
||||
|
||||
public function testSandboxedInclude()
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader([
|
||||
'index' => '{{ include("included", sandboxed: true) }}',
|
||||
'included' => '{{ "included"|e }}',
|
||||
]));
|
||||
$policy = new SecurityPolicy(allowedFunctions: ['include']);
|
||||
$sandbox = new SandboxExtension($policy, false);
|
||||
$twig->addExtension($sandbox);
|
||||
|
||||
// We expect a compile error
|
||||
$this->expectException(SecurityError::class);
|
||||
$twig->render('index');
|
||||
}
|
||||
|
||||
public function testSandboxedIncludeWithPreloadedTemplate()
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader([
|
||||
'index' => '{{ include("included", sandboxed: true) }}',
|
||||
'included' => '{{ "included"|e }}',
|
||||
]));
|
||||
$policy = new SecurityPolicy(allowedFunctions: ['include']);
|
||||
$sandbox = new SandboxExtension($policy, false);
|
||||
$twig->addExtension($sandbox);
|
||||
|
||||
// The template is loaded without the sandbox enabled
|
||||
// so, no compile error
|
||||
$twig->load('included');
|
||||
|
||||
// We expect a runtime error
|
||||
$this->expectException(SecurityError::class);
|
||||
$twig->render('index');
|
||||
}
|
||||
}
|
||||
|
||||
final class CoreTestIteratorAggregate implements \IteratorAggregate
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
--TEST--
|
||||
Functions can be deprecated_function
|
||||
--DEPRECATION--
|
||||
Since foo/bar 1.1: Twig Function "deprecated_function" is deprecated. Use "not_deprecated_function" instead in index.twig at line 2.
|
||||
Since foo/bar 1.1: Twig Function "deprecated_function" is deprecated. Use "not_deprecated_function" instead in index.twig at line 4.
|
||||
Since foo/bar 1.1: Twig Function "deprecated_function" is deprecated; use "not_deprecated_function" instead in index.twig at line 2.
|
||||
Since foo/bar 1.1: Twig Function "deprecated_function" is deprecated; use "not_deprecated_function" instead in index.twig at line 4.
|
||||
--TEMPLATE--
|
||||
{{ deprecated_function() }}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
--TEMPLATE(foo.twig)--
|
||||
|
||||
|
||||
{{ foo|e }}
|
||||
{{ foo|e }}
|
||||
{{ 'foo'|e }}
|
||||
{{ 'foo'|e }}
|
||||
--DATA--
|
||||
return []
|
||||
--EXCEPTION--
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
conditional "block" tag with "extends" tag (nested)
|
||||
--TEMPLATE--
|
||||
{% extends "layout.twig" %}
|
||||
|
||||
{% block content_base %}
|
||||
{{ parent() -}}
|
||||
index
|
||||
{% endblock %}
|
||||
|
||||
{% block content_layout -%}
|
||||
{{ parent() -}}
|
||||
nested_index
|
||||
{% endblock %}
|
||||
--TEMPLATE(layout.twig)--
|
||||
{% extends "base.twig" %}
|
||||
|
||||
{% block content_base %}
|
||||
{{ parent() -}}
|
||||
layout
|
||||
{% if true -%}
|
||||
{% block content_layout -%}
|
||||
nested_layout
|
||||
{% endblock -%}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
--TEMPLATE(base.twig)--
|
||||
{% block content_base %}
|
||||
base
|
||||
{% endblock %}
|
||||
--DATA--
|
||||
return []
|
||||
--EXPECT--
|
||||
base
|
||||
layout
|
||||
nested_layout
|
||||
nested_index
|
||||
index
|
||||
@@ -11,6 +11,7 @@ namespace Twig\Tests;
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Twig\DeprecatedCallableInfo;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\Extension\DebugExtension;
|
||||
use Twig\Extension\SandboxExtension;
|
||||
@@ -183,7 +184,7 @@ class TwigTestExtension extends AbstractExtension
|
||||
new TwigFunction('*_path', [$this, 'dynamic_path']),
|
||||
new TwigFunction('*_foo_*_bar', [$this, 'dynamic_foo']),
|
||||
new TwigFunction('anon_foo', function ($name) { return '*'.$name.'*'; }),
|
||||
new TwigFunction('deprecated_function', function () { return 'foo'; }, ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar', 'alternative' => 'not_deprecated_function']),
|
||||
new TwigFunction('deprecated_function', function () { return 'foo'; }, ['deprecation_info' => new DeprecatedCallableInfo('foo/bar', '1.1', 'not_deprecated_function')]),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Twig\Tests\Util;
|
||||
*/
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Twig\DeprecatedCallableInfo;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\TwigFunction;
|
||||
@@ -22,7 +23,7 @@ class DeprecationCollectorTest extends TestCase
|
||||
public function testCollect()
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader());
|
||||
$twig->addFunction(new TwigFunction('deprec', [$this, 'deprec'], ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar']));
|
||||
$twig->addFunction(new TwigFunction('deprec', [$this, 'deprec'], ['deprecation_info' => new DeprecatedCallableInfo('foo/bar', '1.1')]));
|
||||
|
||||
$collector = new DeprecationCollector($twig);
|
||||
$deprecations = $collector->collect(new Iterator());
|
||||
|
||||
Reference in New Issue
Block a user