File: /var/www/payments-gateway/vendor/twig/twig/src/Extension/AttributeExtension.php
<?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\Extension;
use Twig\Attribute\AsTwigFilter;
use Twig\Attribute\AsTwigFunction;
use Twig\Attribute\AsTwigTest;
use Twig\Environment;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;
/**
* Define Twig filters, functions, and tests with PHP attributes.
*
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
final class AttributeExtension extends AbstractExtension
{
private array $filters;
private array $functions;
private array $tests;
/**
* Use a runtime class using PHP attributes to define filters, functions, and tests.
*
* @param class-string $class
*/
public function __construct(private string $class)
{
}
/**
* @return class-string
*/
public function getClass(): string
{
return $this->class;
}
public function getFilters(): array
{
if (!isset($this->filters)) {
$this->initFromAttributes();
}
return $this->filters;
}
public function getFunctions(): array
{
if (!isset($this->functions)) {
$this->initFromAttributes();
}
return $this->functions;
}
public function getTests(): array
{
if (!isset($this->tests)) {
$this->initFromAttributes();
}
return $this->tests;
}
public function getLastModified(): int
{
return max(
filemtime(__FILE__),
is_file($filename = (new \ReflectionClass($this->getClass()))->getFileName()) ? filemtime($filename) : 0,
);
}
private function initFromAttributes(): void
{
$filters = $functions = $tests = [];
$reflectionClass = new \ReflectionClass($this->getClass());
foreach ($reflectionClass->getMethods() as $method) {
foreach ($method->getAttributes(AsTwigFilter::class) as $reflectionAttribute) {
/** @var AsTwigFilter $attribute */
$attribute = $reflectionAttribute->newInstance();
$callable = new TwigFilter($attribute->name, [$reflectionClass->name, $method->getName()], [
'needs_context' => $attribute->needsContext ?? false,
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
'needs_charset' => $attribute->needsCharset ?? false,
'is_variadic' => $method->isVariadic(),
'is_safe' => $attribute->isSafe,
'is_safe_callback' => $attribute->isSafeCallback,
'pre_escape' => $attribute->preEscape,
'preserves_safety' => $attribute->preservesSafety,
'deprecation_info' => $attribute->deprecationInfo,
]);
if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) {
throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigFilter, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters()));
}
$filters[$attribute->name] = $callable;
}
foreach ($method->getAttributes(AsTwigFunction::class) as $reflectionAttribute) {
/** @var AsTwigFunction $attribute */
$attribute = $reflectionAttribute->newInstance();
$callable = new TwigFunction($attribute->name, [$reflectionClass->name, $method->getName()], [
'needs_context' => $attribute->needsContext ?? false,
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
'needs_charset' => $attribute->needsCharset ?? false,
'is_variadic' => $method->isVariadic(),
'is_safe' => $attribute->isSafe,
'is_safe_callback' => $attribute->isSafeCallback,
'deprecation_info' => $attribute->deprecationInfo,
]);
if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) {
throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigFunction, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters()));
}
$functions[$attribute->name] = $callable;
}
foreach ($method->getAttributes(AsTwigTest::class) as $reflectionAttribute) {
/** @var AsTwigTest $attribute */
$attribute = $reflectionAttribute->newInstance();
$callable = new TwigTest($attribute->name, [$reflectionClass->name, $method->getName()], [
'needs_context' => $attribute->needsContext ?? false,
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
'needs_charset' => $attribute->needsCharset ?? false,
'is_variadic' => $method->isVariadic(),
'deprecation_info' => $attribute->deprecationInfo,
]);
if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) {
throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigTest, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters()));
}
$tests[$attribute->name] = $callable;
}
}
// Assign all at the end to avoid inconsistent state in case of exception
$this->filters = array_values($filters);
$this->functions = array_values($functions);
$this->tests = array_values($tests);
}
/**
* Detect if the first argument of the method is the environment.
*/
private function needsEnvironment(\ReflectionFunctionAbstract $function): bool
{
if (!$parameters = $function->getParameters()) {
return false;
}
return $parameters[0]->getType() instanceof \ReflectionNamedType
&& Environment::class === $parameters[0]->getType()->getName()
&& !$parameters[0]->isVariadic();
}
}