diff --git a/CHANGELOG b/CHANGELOG index 93264d3ef..a8cb1f81d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ * Fix wrong array index * Bump minimum PHP version to 8.1 + * Add support for registering callbacks for undefined functions, filters or token parsers in the IntegrationTestCase # 3.19.0 (2025-01-28) @@ -44,7 +45,7 @@ * Deprecate returning `null` from `TwigFilter::getSafe()` and `TwigFunction::getSafe()`, return `[]` instead # 3.15.0 (2024-11-17) - + * [BC BREAK] Add support for accessing class constants with the dot operator; this can be a BC break if you don't use UPPERCASE constant names * Add Spanish inflector support for the `plural` and `singular` filters in the String extension diff --git a/src/Environment.php b/src/Environment.php index 1e69ea5d2..fddab050a 100644 --- a/src/Environment.php +++ b/src/Environment.php @@ -736,6 +736,9 @@ class Environment return $this->extensionSet->getTokenParser($name); } + /** + * @param callable(string): (TokenParserInterface|false) $callable + */ public function registerUndefinedTokenParserCallback(callable $callable): void { $this->extensionSet->registerUndefinedTokenParserCallback($callable); @@ -775,6 +778,9 @@ class Environment return $this->extensionSet->getFilter($name); } + /** + * @param callable(string): (TwigFilter|false) $callable + */ public function registerUndefinedFilterCallback(callable $callable): void { $this->extensionSet->registerUndefinedFilterCallback($callable); @@ -838,6 +844,9 @@ class Environment return $this->extensionSet->getFunction($name); } + /** + * @param callable(string): (TwigFunction|false) $callable + */ public function registerUndefinedFunctionCallback(callable $callable): void { $this->extensionSet->registerUndefinedFunctionCallback($callable); diff --git a/src/ExtensionSet.php b/src/ExtensionSet.php index 2b17182b2..b069232b4 100644 --- a/src/ExtensionSet.php +++ b/src/ExtensionSet.php @@ -52,8 +52,11 @@ final class ExtensionSet private $binaryOperators; /** @var array|null */ private $globals; + /** @var array */ private $functionCallbacks = []; + /** @var array */ private $filterCallbacks = []; + /** @var array */ private $parserCallbacks = []; private $lastModified = 0; @@ -198,6 +201,9 @@ final class ExtensionSet return null; } + /** + * @param callable(string): (TwigFunction|false) $callable + */ public function registerUndefinedFunctionCallback(callable $callable): void { $this->functionCallbacks[] = $callable; @@ -251,6 +257,9 @@ final class ExtensionSet return null; } + /** + * @param callable(string): (TwigFilter|false) $callable + */ public function registerUndefinedFilterCallback(callable $callable): void { $this->filterCallbacks[] = $callable; @@ -317,6 +326,9 @@ final class ExtensionSet return null; } + /** + * @param callable(string): (TokenParserInterface|false) $callable + */ public function registerUndefinedTokenParserCallback(callable $callable): void { $this->parserCallbacks[] = $callable; diff --git a/src/Test/IntegrationTestCase.php b/src/Test/IntegrationTestCase.php index 1fb3c313b..f4a5dc7e5 100644 --- a/src/Test/IntegrationTestCase.php +++ b/src/Test/IntegrationTestCase.php @@ -17,6 +17,7 @@ use Twig\Error\Error; use Twig\Extension\ExtensionInterface; use Twig\Loader\ArrayLoader; use Twig\RuntimeLoader\RuntimeLoaderInterface; +use Twig\TokenParser\TokenParserInterface; use Twig\TwigFilter; use Twig\TwigFunction; use Twig\TwigTest; @@ -84,6 +85,30 @@ abstract class IntegrationTestCase extends TestCase return []; } + /** + * @return array + */ + protected function getUndefinedFilterCallbacks(): array + { + return []; + } + + /** + * @return array + */ + protected function getUndefinedFunctionCallbacks(): array + { + return []; + } + + /** + * @return array + */ + protected function getUndefinedTokenParserCallbacks(): array + { + return []; + } + /** * @dataProvider getTests * @@ -222,6 +247,18 @@ abstract class IntegrationTestCase extends TestCase $twig->addFunction($function); } + foreach ($this->getUndefinedFilterCallbacks() as $callback) { + $twig->registerUndefinedFilterCallback($callback); + } + + foreach ($this->getUndefinedFunctionCallbacks() as $callback) { + $twig->registerUndefinedFunctionCallback($callback); + } + + foreach ($this->getUndefinedTokenParserCallbacks() as $callback) { + $twig->registerUndefinedTokenParserCallback($callback); + } + $deprecations = []; try { $prevHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$deprecations, &$prevHandler) {