getLine(); if (!$expr instanceof NameExpression) { throw new SyntaxError('Function name must be an identifier.', $line, $parser->getStream()->getSourceContext()); } $name = $expr->getAttribute('name'); if (null !== $alias = $parser->getImportedSymbol('function', $name)) { return new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], $this->parseCallableArguments($parser, $line, false), $line); } $args = $this->parseNamedArguments($parser, false); $function = $parser->getFunction($name, $line); if ($function->getParserCallable()) { $fakeNode = new EmptyNode($line); $fakeNode->setSourceContext($parser->getStream()->getSourceContext()); $node = ($function->getParserCallable())($parser, $fakeNode, $args, $line); // remember the original function name so the sandbox can enforce // the `allowedFunctions` allow-list even though the parser callable // returned a specialized node (e.g. `parent`, `block`, `attribute`). $node->setAttribute('sandboxed_function_name', $name); return $node; } if (!isset($this->readyNodes[$class = $function->getNodeClass()])) { $this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class); } if (!$ready = $this->readyNodes[$class]) { trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigFunction" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class); } return new $class($ready ? $function : $function->getName(), $args, $line); } public function getName(): string { return '('; } public function getDescription(): string { return 'Twig function call'; } public function getPrecedence(): int { return 512; } public function getAssociativity(): InfixAssociativity { return InfixAssociativity::Left; } }