mirror of
https://github.com/twigphp/Twig.git
synced 2026-08-31 04:27:00 +00:00
Add a needs_is_sandboxed option for filters, functions, and tests
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# 3.25.0 (2026-XX-XX)
|
||||
|
||||
* Add a `needs_is_sandboxed` option for filters, functions, and tests
|
||||
* Use deterministic suffixes for generated embed classes
|
||||
|
||||
# 3.24.0 (2026-03-17)
|
||||
|
||||
@@ -216,6 +216,34 @@ the first argument to the filter call (or the second one if
|
||||
// ...
|
||||
}, ['needs_context' => true, 'needs_environment' => true]);
|
||||
|
||||
Sandbox-aware Filters
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 3.25
|
||||
|
||||
The ``needs_is_sandboxed`` option was added in Twig 3.25.
|
||||
|
||||
If you want to know whether the current template is sandboxed in your
|
||||
filter, set the ``needs_is_sandboxed`` option to ``true``; Twig will pass the
|
||||
current sandbox state as a boolean to the filter call (as the first
|
||||
argument, or after the charset, the environment, and the context if they
|
||||
are also requested)::
|
||||
|
||||
$filter = new \Twig\TwigFilter('rot13', function (bool $isSandboxed, $string) {
|
||||
if ($isSandboxed) {
|
||||
// adjust behavior when running in a sandboxed template
|
||||
}
|
||||
|
||||
return str_rot13($string);
|
||||
}, ['needs_is_sandboxed' => true]);
|
||||
|
||||
The sandbox state is resolved against the current template source, which
|
||||
means it takes both the global sandbox state and any ``SourcePolicy`` into
|
||||
account.
|
||||
|
||||
The same ``needs_is_sandboxed`` option is also available on functions and
|
||||
tests.
|
||||
|
||||
Automatic Escaping
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ abstract class AbstractTwigCallable implements TwigCallableInterface
|
||||
'needs_environment' => false,
|
||||
'needs_context' => false,
|
||||
'needs_charset' => false,
|
||||
'needs_is_sandboxed' => false,
|
||||
'is_variadic' => false,
|
||||
'deprecation_info' => null,
|
||||
'deprecated' => false,
|
||||
@@ -107,6 +108,11 @@ abstract class AbstractTwigCallable implements TwigCallableInterface
|
||||
return $this->options['needs_context'];
|
||||
}
|
||||
|
||||
public function needsIsSandboxed(): bool
|
||||
{
|
||||
return $this->options['needs_is_sandboxed'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
@@ -182,6 +188,6 @@ abstract class AbstractTwigCallable implements TwigCallableInterface
|
||||
|
||||
public function getMinimalNumberOfRequiredArguments(): int
|
||||
{
|
||||
return ($this->options['needs_charset'] ? 1 : 0) + ($this->options['needs_environment'] ? 1 : 0) + ($this->options['needs_context'] ? 1 : 0) + \count($this->arguments);
|
||||
return ($this->options['needs_charset'] ? 1 : 0) + ($this->options['needs_environment'] ? 1 : 0) + ($this->options['needs_context'] ? 1 : 0) + ($this->options['needs_is_sandboxed'] ? 1 : 0) + \count($this->arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ final class AsTwigFilter
|
||||
* @param bool|null $needsCharset Whether the filter needs the charset passed as the first argument
|
||||
* @param bool|null $needsEnvironment Whether the filter needs the environment passed as the first argument, or after the charset
|
||||
* @param bool|null $needsContext Whether the filter needs the context array passed as the first argument, or after the charset and the environment
|
||||
* @param bool|null $needsIsSandboxed Whether the filter needs the current sandbox state (a boolean) passed as the first argument, or after the charset, the environment, and the context
|
||||
* @param string[]|null $isSafe List of formats in which you want the raw output to be printed unescaped
|
||||
* @param string|array|null $isSafeCallback Function called at compilation time to determine if the filter is safe
|
||||
* @param string|null $preEscape Some filters may need to work on input that is already escaped or safe
|
||||
@@ -46,6 +47,7 @@ final class AsTwigFilter
|
||||
public ?bool $needsCharset = null,
|
||||
public ?bool $needsEnvironment = null,
|
||||
public ?bool $needsContext = null,
|
||||
public ?bool $needsIsSandboxed = null,
|
||||
public ?array $isSafe = null,
|
||||
public string|array|null $isSafeCallback = null,
|
||||
public ?string $preEscape = null,
|
||||
|
||||
@@ -35,6 +35,7 @@ final class AsTwigFunction
|
||||
* @param bool|null $needsCharset Whether the function needs the charset passed as the first argument
|
||||
* @param bool|null $needsEnvironment Whether the function needs the environment passed as the first argument, or after the charset
|
||||
* @param bool|null $needsContext Whether the function needs the context array passed as the first argument, or after the charset and the environment
|
||||
* @param bool|null $needsIsSandboxed Whether the function needs the current sandbox state (a boolean) passed as the first argument, or after the charset, the environment, and the context
|
||||
* @param string[]|null $isSafe List of formats in which you want the raw output to be printed unescaped
|
||||
* @param string|array|null $isSafeCallback Function called at compilation time to determine if the function is safe
|
||||
* @param DeprecatedCallableInfo|null $deprecationInfo Information about the deprecation
|
||||
@@ -44,6 +45,7 @@ final class AsTwigFunction
|
||||
public ?bool $needsCharset = null,
|
||||
public ?bool $needsEnvironment = null,
|
||||
public ?bool $needsContext = null,
|
||||
public ?bool $needsIsSandboxed = null,
|
||||
public ?array $isSafe = null,
|
||||
public string|array|null $isSafeCallback = null,
|
||||
public ?DeprecatedCallableInfo $deprecationInfo = null,
|
||||
|
||||
@@ -35,6 +35,7 @@ final class AsTwigTest
|
||||
* @param bool|null $needsCharset Whether the test needs the charset passed as the first argument
|
||||
* @param bool|null $needsEnvironment Whether the test needs the environment passed as the first argument, or after the charset
|
||||
* @param bool|null $needsContext Whether the test needs the context array passed as the first argument, or after the charset and the environment
|
||||
* @param bool|null $needsIsSandboxed Whether the test needs the current sandbox state (a boolean) passed as the first argument, or after the charset, the environment, and the context
|
||||
* @param DeprecatedCallableInfo|null $deprecationInfo Information about the deprecation
|
||||
*/
|
||||
public function __construct(
|
||||
@@ -42,6 +43,7 @@ final class AsTwigTest
|
||||
public ?bool $needsCharset = null,
|
||||
public ?bool $needsEnvironment = null,
|
||||
public ?bool $needsContext = null,
|
||||
public ?bool $needsIsSandboxed = null,
|
||||
public ?DeprecatedCallableInfo $deprecationInfo = null,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ final class AttributeExtension extends AbstractExtension
|
||||
'needs_context' => $attribute->needsContext ?? false,
|
||||
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
|
||||
'needs_charset' => $attribute->needsCharset ?? false,
|
||||
'needs_is_sandboxed' => $attribute->needsIsSandboxed ?? false,
|
||||
'is_variadic' => $method->isVariadic(),
|
||||
'is_safe' => $attribute->isSafe,
|
||||
'is_safe_callback' => $attribute->isSafeCallback,
|
||||
@@ -118,6 +119,7 @@ final class AttributeExtension extends AbstractExtension
|
||||
'needs_context' => $attribute->needsContext ?? false,
|
||||
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
|
||||
'needs_charset' => $attribute->needsCharset ?? false,
|
||||
'needs_is_sandboxed' => $attribute->needsIsSandboxed ?? false,
|
||||
'is_variadic' => $method->isVariadic(),
|
||||
'is_safe' => $attribute->isSafe,
|
||||
'is_safe_callback' => $attribute->isSafeCallback,
|
||||
@@ -139,6 +141,7 @@ final class AttributeExtension extends AbstractExtension
|
||||
'needs_context' => $attribute->needsContext ?? false,
|
||||
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
|
||||
'needs_charset' => $attribute->needsCharset ?? false,
|
||||
'needs_is_sandboxed' => $attribute->needsIsSandboxed ?? false,
|
||||
'is_variadic' => $method->isVariadic(),
|
||||
'deprecation_info' => $attribute->deprecationInfo,
|
||||
]);
|
||||
|
||||
@@ -100,6 +100,14 @@ abstract class CallExpression extends AbstractExpression
|
||||
$first = false;
|
||||
}
|
||||
|
||||
if (self::needsIsSandboxed($twigCallable)) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$compiler->raw('$this->env->hasExtension(\Twig\Extension\SandboxExtension::class) && $this->env->getExtension(\Twig\Extension\SandboxExtension::class)->isSandboxed($this->source)');
|
||||
$first = false;
|
||||
}
|
||||
|
||||
foreach ($twigCallable->getArguments() as $argument) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
@@ -290,6 +298,9 @@ abstract class CallExpression extends AbstractExpression
|
||||
if ($twigCallable->needsContext()) {
|
||||
array_shift($parameters);
|
||||
}
|
||||
if (self::needsIsSandboxed($twigCallable)) {
|
||||
array_shift($parameters);
|
||||
}
|
||||
foreach ($twigCallable->getArguments() as $argument) {
|
||||
array_shift($parameters);
|
||||
}
|
||||
@@ -320,6 +331,22 @@ abstract class CallExpression extends AbstractExpression
|
||||
return $this->reflector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* To be removed in 4.0 and replaced by $twigCallable->needsIsSandboxed().
|
||||
*/
|
||||
public static function needsIsSandboxed(TwigCallableInterface $twigCallable): bool
|
||||
{
|
||||
if (method_exists($twigCallable, 'needsIsSandboxed')) {
|
||||
return $twigCallable->needsIsSandboxed();
|
||||
}
|
||||
|
||||
trigger_deprecation('twig/twig', '3.25', 'Not implementing the "needsIsSandboxed()" method in "%s" is deprecated. This method will be part of the "%s" interface in 4.0.', $twigCallable::class, TwigCallableInterface::class);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the Twig callable based on attributes (as potentially, attributes changed between the creation and the compilation of the node).
|
||||
*
|
||||
@@ -334,6 +361,7 @@ abstract class CallExpression extends AbstractExpression
|
||||
$this->getAttribute('name'),
|
||||
$this->hasAttribute('callable') ? $this->getAttribute('callable') : $current->getCallable(),
|
||||
[
|
||||
'needs_is_sandboxed' => $this->hasAttribute('needs_is_sandboxed') ? $this->getAttribute('needs_is_sandboxed') : self::needsIsSandboxed($current),
|
||||
'is_variadic' => $this->hasAttribute('is_variadic') ? $this->getAttribute('is_variadic') : $current->isVariadic(),
|
||||
],
|
||||
))->withDynamicArguments($this->getAttribute('name'), $this->hasAttribute('dynamic_name') ? $this->getAttribute('dynamic_name') : $current->getDynamicName(), $this->hasAttribute('arguments') ? $this->getAttribute('arguments') : $current->getArguments()),
|
||||
@@ -344,6 +372,7 @@ abstract class CallExpression extends AbstractExpression
|
||||
'needs_environment' => $this->hasAttribute('needs_environment') ? $this->getAttribute('needs_environment') : $current->needsEnvironment(),
|
||||
'needs_context' => $this->hasAttribute('needs_context') ? $this->getAttribute('needs_context') : $current->needsContext(),
|
||||
'needs_charset' => $this->hasAttribute('needs_charset') ? $this->getAttribute('needs_charset') : $current->needsCharset(),
|
||||
'needs_is_sandboxed' => $this->hasAttribute('needs_is_sandboxed') ? $this->getAttribute('needs_is_sandboxed') : self::needsIsSandboxed($current),
|
||||
'is_variadic' => $this->hasAttribute('is_variadic') ? $this->getAttribute('is_variadic') : $current->isVariadic(),
|
||||
],
|
||||
))->withDynamicArguments($this->getAttribute('name'), $this->hasAttribute('dynamic_name') ? $this->getAttribute('dynamic_name') : $current->getDynamicName(), $this->hasAttribute('arguments') ? $this->getAttribute('arguments') : $current->getArguments()),
|
||||
@@ -354,6 +383,7 @@ abstract class CallExpression extends AbstractExpression
|
||||
'needs_environment' => $this->hasAttribute('needs_environment') ? $this->getAttribute('needs_environment') : $current->needsEnvironment(),
|
||||
'needs_context' => $this->hasAttribute('needs_context') ? $this->getAttribute('needs_context') : $current->needsContext(),
|
||||
'needs_charset' => $this->hasAttribute('needs_charset') ? $this->getAttribute('needs_charset') : $current->needsCharset(),
|
||||
'needs_is_sandboxed' => $this->hasAttribute('needs_is_sandboxed') ? $this->getAttribute('needs_is_sandboxed') : self::needsIsSandboxed($current),
|
||||
'is_variadic' => $this->hasAttribute('is_variadic') ? $this->getAttribute('is_variadic') : $current->isVariadic(),
|
||||
],
|
||||
))->withDynamicArguments($this->getAttribute('name'), $this->hasAttribute('dynamic_name') ? $this->getAttribute('dynamic_name') : $current->getDynamicName(), $this->hasAttribute('arguments') ? $this->getAttribute('arguments') : $current->getArguments()),
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Twig;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @method bool needsIsSandboxed() Whether the callable needs the current sandbox state passed as an argument. Not implementing this method is deprecated since Twig 3.25, it will be required in 4.0.
|
||||
*/
|
||||
interface TwigCallableInterface extends \Stringable
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Twig\Util;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\CallExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\VariadicExpression;
|
||||
use Twig\Node\Node;
|
||||
@@ -196,6 +197,9 @@ final class CallableArgumentsExtractor
|
||||
if ($this->twigCallable->needsContext()) {
|
||||
array_shift($parameters);
|
||||
}
|
||||
if (CallExpression::needsIsSandboxed($this->twigCallable)) {
|
||||
array_shift($parameters);
|
||||
}
|
||||
foreach ($this->twigCallable->getArguments() as $argument) {
|
||||
array_shift($parameters);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ class AttributeExtensionTest extends TestCase
|
||||
yield 'with env' => ['with_env_filter', 'withEnvFilter', ['needs_environment' => true]];
|
||||
yield 'with context' => ['with_context_filter', 'withContextFilter', ['needs_context' => true]];
|
||||
yield 'with env and context' => ['with_env_and_context_filter', 'withEnvAndContextFilter', ['needs_environment' => true, 'needs_context' => true]];
|
||||
yield 'with sandbox' => ['with_sandbox_filter', 'withSandboxFilter', ['needs_is_sandboxed' => true]];
|
||||
yield 'variadic' => ['variadic_filter', 'variadicFilter', ['is_variadic' => true]];
|
||||
yield 'deprecated' => ['deprecated_filter', 'deprecatedFilter', ['deprecation_info' => new DeprecatedCallableInfo('foo/bar', '1.2')]];
|
||||
yield 'pattern' => ['pattern_*_filter', 'patternFilter', []];
|
||||
@@ -76,6 +77,7 @@ class AttributeExtensionTest extends TestCase
|
||||
yield 'with env' => ['with_env_function', 'withEnvFunction', ['needs_environment' => true]];
|
||||
yield 'with context' => ['with_context_function', 'withContextFunction', ['needs_context' => true]];
|
||||
yield 'with env and context' => ['with_env_and_context_function', 'withEnvAndContextFunction', ['needs_environment' => true, 'needs_context' => true]];
|
||||
yield 'with sandbox' => ['with_sandbox_function', 'withSandboxFunction', ['needs_is_sandboxed' => true]];
|
||||
yield 'no argument' => ['no_arg_function', 'noArgFunction', []];
|
||||
yield 'variadic' => ['variadic_function', 'variadicFunction', ['is_variadic' => true]];
|
||||
yield 'deprecated' => ['deprecated_function', 'deprecatedFunction', ['deprecation_info' => new DeprecatedCallableInfo('foo/bar', '1.2')]];
|
||||
@@ -104,6 +106,7 @@ class AttributeExtensionTest extends TestCase
|
||||
yield 'with env' => ['with_env_test', 'withEnvTest', ['needs_environment' => true]];
|
||||
yield 'with context' => ['with_context_test', 'withContextTest', ['needs_context' => true]];
|
||||
yield 'with env and context' => ['with_env_and_context_test', 'withEnvAndContextTest', ['needs_environment' => true, 'needs_context' => true]];
|
||||
yield 'with sandbox' => ['with_sandbox_test', 'withSandboxTest', ['needs_is_sandboxed' => true]];
|
||||
yield 'variadic' => ['variadic_test', 'variadicTest', ['is_variadic' => true]];
|
||||
yield 'deprecated' => ['deprecated_test', 'deprecatedTest', ['deprecation_info' => new DeprecatedCallableInfo('foo/bar', '1.2')]];
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ class ExtensionWithAttributes
|
||||
{
|
||||
}
|
||||
|
||||
#[AsTwigFilter('with_sandbox_filter', needsIsSandboxed: true)]
|
||||
public function withSandboxFilter(bool $isSandboxed, string $string)
|
||||
{
|
||||
}
|
||||
|
||||
#[AsTwigFilter('variadic_filter')]
|
||||
public function variadicFilter(string ...$strings)
|
||||
{
|
||||
@@ -74,6 +79,11 @@ class ExtensionWithAttributes
|
||||
{
|
||||
}
|
||||
|
||||
#[AsTwigFunction('with_sandbox_function', needsIsSandboxed: true)]
|
||||
public function withSandboxFunction(bool $isSandboxed, string $string)
|
||||
{
|
||||
}
|
||||
|
||||
#[AsTwigFunction('no_arg_function')]
|
||||
public function noArgFunction()
|
||||
{
|
||||
@@ -114,6 +124,11 @@ class ExtensionWithAttributes
|
||||
{
|
||||
}
|
||||
|
||||
#[AsTwigTest('with_sandbox_test', needsIsSandboxed: true)]
|
||||
public function withSandboxTest(bool $isSandboxed, $argument)
|
||||
{
|
||||
}
|
||||
|
||||
#[AsTwigTest('deprecated_test', deprecationInfo: new DeprecatedCallableInfo('foo/bar', '1.2'))]
|
||||
public function deprecatedTest($value, $argument)
|
||||
{
|
||||
|
||||
@@ -606,6 +606,176 @@ EOF
|
||||
$this->expectException(SecurityError::class);
|
||||
$twig->load('1_basic')->render([]);
|
||||
}
|
||||
|
||||
public function testNeedsIsSandboxedFilterReceivesTrueWhenSandboxed()
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], ['index' => '{{ "foo"|sandbox_aware }}'], [], ['sandbox_aware']);
|
||||
$twig->addFilter(new \Twig\TwigFilter('sandbox_aware', static function (bool $isSandboxed, string $value) {
|
||||
return $value.':'.($isSandboxed ? 'on' : 'off');
|
||||
}, ['needs_is_sandboxed' => true]));
|
||||
|
||||
$this->assertSame('foo:on', $twig->load('index')->render([]));
|
||||
}
|
||||
|
||||
public function testNeedsIsSandboxedFilterReceivesFalseWhenNotSandboxed()
|
||||
{
|
||||
$twig = $this->getEnvironment(false, [], ['index' => '{{ "foo"|sandbox_aware }}']);
|
||||
$twig->addFilter(new \Twig\TwigFilter('sandbox_aware', static function (bool $isSandboxed, string $value) {
|
||||
return $value.':'.($isSandboxed ? 'on' : 'off');
|
||||
}, ['needs_is_sandboxed' => true]));
|
||||
|
||||
$this->assertSame('foo:off', $twig->load('index')->render([]));
|
||||
}
|
||||
|
||||
public function testNeedsIsSandboxedFilterFollowsSourcePolicy()
|
||||
{
|
||||
$twig = $this->getEnvironment(false, [], [
|
||||
'in' => '{{ "foo"|sandbox_aware }}',
|
||||
'out' => '{{ "foo"|sandbox_aware }}',
|
||||
], [], ['sandbox_aware'], [], [], [], new class implements \Twig\Sandbox\SourcePolicyInterface {
|
||||
public function enableSandbox(Source $source): bool
|
||||
{
|
||||
return 'in' === $source->getName();
|
||||
}
|
||||
});
|
||||
$twig->addFilter(new \Twig\TwigFilter('sandbox_aware', static function (bool $isSandboxed, string $value) {
|
||||
return $value.':'.($isSandboxed ? 'on' : 'off');
|
||||
}, ['needs_is_sandboxed' => true]));
|
||||
|
||||
$this->assertSame('foo:on', $twig->load('in')->render([]));
|
||||
$this->assertSame('foo:off', $twig->load('out')->render([]));
|
||||
}
|
||||
|
||||
public function testNeedsIsSandboxedFunctionWithoutSandboxExtension()
|
||||
{
|
||||
$loader = new ArrayLoader(['index' => '{{ sandbox_aware("foo") }}']);
|
||||
$twig = new Environment($loader, ['debug' => true, 'cache' => false, 'autoescape' => false]);
|
||||
$twig->addFunction(new \Twig\TwigFunction('sandbox_aware', static function (bool $isSandboxed, string $value) {
|
||||
return $value.':'.($isSandboxed ? 'on' : 'off');
|
||||
}, ['needs_is_sandboxed' => true]));
|
||||
|
||||
$this->assertSame('foo:off', $twig->load('index')->render([]));
|
||||
}
|
||||
|
||||
public function testNeedsIsSandboxedTestReceivesTrueWhenSandboxed()
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], ['index' => '{{ "foo" is sandbox_aware ? "on" : "off" }}']);
|
||||
$twig->addTest(new \Twig\TwigTest('sandbox_aware', static function (bool $isSandboxed, string $value) {
|
||||
return $isSandboxed && 'foo' === $value;
|
||||
}, ['needs_is_sandboxed' => true]));
|
||||
|
||||
$this->assertSame('on', $twig->load('index')->render([]));
|
||||
}
|
||||
|
||||
public function testNeedsIsSandboxedTestReceivesFalseWhenNotSandboxed()
|
||||
{
|
||||
$twig = $this->getEnvironment(false, [], ['index' => '{{ "foo" is sandbox_aware ? "on" : "off" }}']);
|
||||
$twig->addTest(new \Twig\TwigTest('sandbox_aware', static function (bool $isSandboxed, string $value) {
|
||||
return !$isSandboxed && 'foo' === $value;
|
||||
}, ['needs_is_sandboxed' => true]));
|
||||
|
||||
$this->assertSame('on', $twig->load('index')->render([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testNeedsIsSandboxedHelperTriggersDeprecationForCustomImplementation()
|
||||
{
|
||||
$callable = new LegacyTwigCallableWithoutNeedsIsSandboxed();
|
||||
|
||||
$this->expectDeprecation(\sprintf('Since twig/twig 3.25: Not implementing the "needsIsSandboxed()" method in "%s" is deprecated. This method will be part of the "Twig\TwigCallableInterface" interface in 4.0.', $callable::class));
|
||||
|
||||
$this->assertFalse(\Twig\Node\Expression\CallExpression::needsIsSandboxed($callable));
|
||||
}
|
||||
}
|
||||
|
||||
class LegacyTwigCallableWithoutNeedsIsSandboxed implements \Twig\TwigCallableInterface
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return 'filter';
|
||||
}
|
||||
|
||||
public function getDynamicName(): string
|
||||
{
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
public function getCallable()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function needsCharset(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function needsEnvironment(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function needsContext(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function withDynamicArguments(string $name, string $dynamicName, array $arguments): \Twig\TwigCallableInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getArguments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function isVariadic(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isDeprecated(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDeprecatingPackage(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getDeprecatedVersion(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getAlternative(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getMinimalNumberOfRequiredArguments(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return 'foo';
|
||||
}
|
||||
}
|
||||
|
||||
class ParentClass
|
||||
|
||||
@@ -87,6 +87,14 @@ class FilterTest extends NodeTestCase
|
||||
$node = self::createFilter($environment, new ConstantExpression('foo', 1), 'anonymous');
|
||||
$tests[] = [$node, '$this->env->getFilter(\'anonymous\')->getCallable()("foo")'];
|
||||
|
||||
// needs sandbox
|
||||
$node = self::createFilter($environment, $string, 'bar_sandbox');
|
||||
$tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_sandbox($this->env->hasExtension(\Twig\Extension\SandboxExtension::class) && $this->env->getExtension(\Twig\Extension\SandboxExtension::class)->isSandboxed($this->source), "abc")', $environment];
|
||||
|
||||
// needs charset, environment, context, and sandbox
|
||||
$node = self::createFilter($environment, $string, 'bar_all');
|
||||
$tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_all($this->env->getCharset(), $this->env, $context, $this->env->hasExtension(\Twig\Extension\SandboxExtension::class) && $this->env->getExtension(\Twig\Extension\SandboxExtension::class)->isSandboxed($this->source), "abc")', $environment];
|
||||
|
||||
// needs environment
|
||||
$node = self::createFilter($environment, $string, 'bar');
|
||||
$tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_dummy($this->env, "abc")', $environment];
|
||||
@@ -178,6 +186,8 @@ class FilterTest extends NodeTestCase
|
||||
$env->addFilter(new TwigFilter('bar', 'Twig\Tests\Node\Expression\twig_tests_filter_dummy', ['needs_environment' => true]));
|
||||
$env->addFilter(new TwigFilter('bar_closure', \Closure::fromCallable(twig_tests_filter_dummy::class), ['needs_environment' => true]));
|
||||
$env->addFilter(new TwigFilter('barbar', 'Twig\Tests\Node\Expression\twig_tests_filter_barbar', ['needs_context' => true, 'is_variadic' => true]));
|
||||
$env->addFilter(new TwigFilter('bar_sandbox', 'Twig\Tests\Node\Expression\twig_tests_filter_sandbox', ['needs_is_sandboxed' => true]));
|
||||
$env->addFilter(new TwigFilter('bar_all', 'Twig\Tests\Node\Expression\twig_tests_filter_all', ['needs_charset' => true, 'needs_environment' => true, 'needs_context' => true, 'needs_is_sandboxed' => true]));
|
||||
$env->addFilter(new TwigFilter('magic_static', __NAMESPACE__.'\ChildMagicCallStub::magicStaticCall'));
|
||||
if (\PHP_VERSION_ID >= 80111) {
|
||||
$env->addExtension(new FilterTestExtension());
|
||||
@@ -217,6 +227,14 @@ function twig_tests_filter_barbar($context, $string, $arg1 = null, $arg2 = null,
|
||||
{
|
||||
}
|
||||
|
||||
function twig_tests_filter_sandbox(bool $isSandboxed, $string)
|
||||
{
|
||||
}
|
||||
|
||||
function twig_tests_filter_all(string $charset, Environment $env, array $context, bool $isSandboxed, $string)
|
||||
{
|
||||
}
|
||||
|
||||
class ChildMagicCallStub extends ParentMagicCallStub
|
||||
{
|
||||
public static function identifier()
|
||||
|
||||
Reference in New Issue
Block a user