-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR was merged into the 3.x branch. Discussion ---------- Refactor code Commits ------- 31037d0 Refactor code
- Loading branch information
Showing
9 changed files
with
58 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,9 +17,6 @@ | |
use Twig\Node\Expression\VariadicExpression; | ||
use Twig\Node\Node; | ||
use Twig\TwigCallableInterface; | ||
use Twig\TwigFilter; | ||
use Twig\TwigFunction; | ||
use Twig\TwigTest; | ||
|
||
/** | ||
* @author Fabien Potencier <[email protected]> | ||
|
@@ -28,59 +25,51 @@ | |
*/ | ||
final class CallableArgumentsExtractor | ||
{ | ||
private string $type; | ||
private string $name; | ||
private ReflectionCallable $rc; | ||
|
||
public function __construct( | ||
private Node $node, | ||
private TwigCallableInterface $twigCallable, | ||
) { | ||
$this->type = match (true) { | ||
$twigCallable instanceof TwigFunction => 'function', | ||
$twigCallable instanceof TwigFilter => 'filter', | ||
$twigCallable instanceof TwigTest => 'test', | ||
default => throw new \LogicException('Unknown callable type.'), | ||
}; | ||
$this->name = $twigCallable->getName(); | ||
$this->rc = new ReflectionCallable($twigCallable); | ||
} | ||
|
||
/** | ||
* @return array<Node> | ||
*/ | ||
public function extractArguments(Node $arguments): array | ||
{ | ||
$rc = new ReflectionCallable($this->twigCallable->getCallable(), $this->type, $this->name); | ||
$extractedArguments = []; | ||
$named = false; | ||
foreach ($arguments as $name => $node) { | ||
if (!\is_int($name)) { | ||
$named = true; | ||
$name = $this->normalizeName($name); | ||
} elseif ($named) { | ||
throw new SyntaxError(\sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->type, $this->name), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
throw new SyntaxError(\sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
} | ||
|
||
$extractedArguments[$name] = $node; | ||
} | ||
|
||
if (!$named && !$this->twigCallable->isVariadic()) { | ||
$min = $this->twigCallable->getMinimalNumberOfRequiredArguments(); | ||
if (\count($extractedArguments) < $rc->getReflector()->getNumberOfRequiredParameters() - $min) { | ||
throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $rc->getReflector()->getParameters()[$min + \count($extractedArguments)]->getName(), $this->type, $this->name), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
if (\count($extractedArguments) < $this->rc->getReflector()->getNumberOfRequiredParameters() - $min) { | ||
throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $this->rc->getReflector()->getParameters()[$min + \count($extractedArguments)]->getName(), $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
} | ||
|
||
return $extractedArguments; | ||
} | ||
|
||
if (!$callable = $this->twigCallable->getCallable()) { | ||
if ($named) { | ||
throw new SyntaxError(\sprintf('Named arguments are not supported for %s "%s".', $this->type, $this->name)); | ||
throw new SyntaxError(\sprintf('Named arguments are not supported for %s "%s".', $this->twigCallable->getType(), $this->twigCallable->getName())); | ||
} | ||
|
||
throw new SyntaxError(\sprintf('Arbitrary positional arguments are not supported for %s "%s".', $this->type, $this->name)); | ||
throw new SyntaxError(\sprintf('Arbitrary positional arguments are not supported for %s "%s".', $this->twigCallable->getType(), $this->twigCallable->getName())); | ||
} | ||
|
||
[$callableParameters, $isPhpVariadic] = $this->getCallableParameters($rc); | ||
[$callableParameters, $isPhpVariadic] = $this->getCallableParameters(); | ||
$arguments = []; | ||
$names = []; | ||
$missingArguments = []; | ||
|
@@ -100,13 +89,13 @@ public function extractArguments(Node $arguments): array | |
|
||
if (\array_key_exists($name, $extractedArguments)) { | ||
if (\array_key_exists($pos, $extractedArguments)) { | ||
throw new SyntaxError(\sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->type, $this->name), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
throw new SyntaxError(\sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
} | ||
|
||
if (\count($missingArguments)) { | ||
throw new SyntaxError(\sprintf( | ||
'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".', | ||
$name, $this->type, $this->name, implode(', ', $names), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments) | ||
$name, $this->twigCallable->getType(), $this->twigCallable->getName(), implode(', ', $names), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments) | ||
), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
} | ||
|
||
|
@@ -129,7 +118,7 @@ public function extractArguments(Node $arguments): array | |
|
||
$missingArguments[] = $name; | ||
} else { | ||
throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->type, $this->name), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); | ||
} | ||
} | ||
|
||
|
@@ -162,7 +151,7 @@ public function extractArguments(Node $arguments): array | |
throw new SyntaxError( | ||
\sprintf( | ||
'Unknown argument%s "%s" for %s "%s(%s)".', | ||
\count($extractedArguments) > 1 ? 's' : '', implode('", "', array_keys($extractedArguments)), $this->type, $this->name, implode(', ', $names) | ||
\count($extractedArguments) > 1 ? 's' : '', implode('", "', array_keys($extractedArguments)), $this->twigCallable->getType(), $this->twigCallable->getName(), implode(', ', $names) | ||
), | ||
$unknownArgument ? $unknownArgument->getTemplateLine() : $this->node->getTemplateLine(), | ||
$unknownArgument ? $unknownArgument->getSourceContext() : $this->node->getSourceContext() | ||
|
@@ -177,11 +166,9 @@ private function normalizeName(string $name): string | |
return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], $name)); | ||
} | ||
|
||
private function getCallableParameters(ReflectionCallable $rc): array | ||
private function getCallableParameters(): array | ||
{ | ||
$r = $rc->getReflector(); | ||
|
||
$parameters = $r->getParameters(); | ||
$parameters = $this->rc->getReflector()->getParameters(); | ||
if ($this->node->hasNode('node')) { | ||
array_shift($parameters); | ||
} | ||
|
@@ -208,7 +195,7 @@ private function getCallableParameters(ReflectionCallable $rc): array | |
array_pop($parameters); | ||
$isPhpVariadic = true; | ||
} else { | ||
throw new SyntaxError(\sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $rc->getName(), $this->type, $this->name)); | ||
throw new SyntaxError(\sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $this->rc->getName(), $this->twigCallable->getType(), $this->twigCallable->getName())); | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,8 @@ | |
|
||
namespace Twig\Util; | ||
|
||
use Twig\TwigCallableInterface; | ||
|
||
/** | ||
* @author Fabien Potencier <[email protected]> | ||
* | ||
|
@@ -22,8 +24,10 @@ final class ReflectionCallable | |
private $callable = null; | ||
private $name; | ||
|
||
public function __construct($callable, string $debugType = 'unknown', string $debugName = 'unknown') | ||
{ | ||
public function __construct( | ||
private TwigCallableInterface $twigCallable, | ||
) { | ||
$callable = $twigCallable->getCallable(); | ||
if (\is_string($callable) && false !== $pos = strpos($callable, '::')) { | ||
$callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)]; | ||
} | ||
|
@@ -40,7 +44,7 @@ public function __construct($callable, string $debugType = 'unknown', string $de | |
try { | ||
$closure = \Closure::fromCallable($callable); | ||
} catch (\TypeError $e) { | ||
throw new \LogicException(\sprintf('Callback for %s "%s" is not callable in the current scope.', $debugType, $debugName), 0, $e); | ||
throw new \LogicException(\sprintf('Callback for %s "%s" is not callable in the current scope.', $twigCallable->getType(), $twigCallable->getName()), 0, $e); | ||
} | ||
$this->reflector = $r = new \ReflectionFunction($closure); | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters