1<?php 2 3/* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Twig\ExpressionParser\Infix; 13 14use Twig\Error\SyntaxError; 15use Twig\Node\Expression\ArrayExpression; 16use Twig\Node\Expression\Binary\SetBinary; 17use Twig\Node\Expression\Unary\SpreadUnary; 18use Twig\Node\Expression\Variable\ContextVariable; 19use Twig\Node\Expression\Variable\LocalVariable; 20use Twig\Node\Nodes; 21use Twig\Parser; 22use Twig\Token; 23 24trait ArgumentsTrait 25{ 26 private function parseCallableArguments(Parser $parser, int $line, bool $parseOpenParenthesis = true): ArrayExpression 27 { 28 $arguments = new ArrayExpression([], $line); 29 foreach ($this->parseNamedArguments($parser, $parseOpenParenthesis) as $k => $n) { 30 $arguments->addElement($n, new LocalVariable($k, $line)); 31 } 32 33 return $arguments; 34 } 35 36 private function parseNamedArguments(Parser $parser, bool $parseOpenParenthesis = true): Nodes 37 { 38 $args = []; 39 $stream = $parser->getStream(); 40 if ($parseOpenParenthesis) { 41 $stream->expect(Token::OPERATOR_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); 42 } 43 $hasSpread = false; 44 while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) { 45 if ($args) { 46 $stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); 47 48 // if the comma above was a trailing comma, early exit the argument parse loop 49 if ($stream->test(Token::PUNCTUATION_TYPE, ')')) { 50 break; 51 } 52 } 53 54 $value = $parser->parseExpression(); 55 if ($value instanceof SpreadUnary) { 56 $hasSpread = true; 57 } elseif ($hasSpread) { 58 throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); 59 } 60 61 $name = null; 62 if ($value instanceof SetBinary) { 63 $name = $value->getNode('left')->getAttribute('name'); 64 $value = $value->getNode('right'); 65 } elseif (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':'))) { 66 if (!$value instanceof ContextVariable) { 67 throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', $value::class), $token->getLine(), $stream->getSourceContext()); 68 } 69 $name = $value->getAttribute('name'); 70 $value = $parser->parseExpression(); 71 } 72 73 if (null === $name) { 74 $args[] = $value; 75 } else { 76 $args[$name] = $value; 77 } 78 } 79 $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); 80 81 return new Nodes($args); 82 } 83} 84