composer package updates
[openemr.git] / vendor / twig / twig / lib / Twig / Node / Expression / Call.php
blobd962b6a501d6ca4ed6c11d271d3befdb547618ca
1 <?php
3 /*
4 * This file is part of Twig.
6 * (c) Fabien Potencier
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
11 abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
13 private $reflector;
15 protected function compileCallable(Twig_Compiler $compiler)
17 $closingParenthesis = false;
18 if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
19 if (is_string($callable) && false === strpos($callable, '::')) {
20 $compiler->raw($callable);
21 } else {
22 list($r, $callable) = $this->reflectCallable($callable);
23 if ($r instanceof ReflectionMethod && is_string($callable[0])) {
24 if ($r->isStatic()) {
25 $compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
26 } else {
27 $compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
29 } elseif ($r instanceof ReflectionMethod && $callable[0] instanceof Twig_ExtensionInterface) {
30 $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', get_class($callable[0]), $callable[1]));
31 } else {
32 $type = ucfirst($this->getAttribute('type'));
33 $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
34 $closingParenthesis = true;
37 } else {
38 $compiler->raw($this->getAttribute('thing')->compile());
41 $this->compileArguments($compiler);
43 if ($closingParenthesis) {
44 $compiler->raw(')');
48 protected function compileArguments(Twig_Compiler $compiler)
50 $compiler->raw('(');
52 $first = true;
54 if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
55 $compiler->raw('$this->env');
56 $first = false;
59 if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
60 if (!$first) {
61 $compiler->raw(', ');
63 $compiler->raw('$context');
64 $first = false;
67 if ($this->hasAttribute('arguments')) {
68 foreach ($this->getAttribute('arguments') as $argument) {
69 if (!$first) {
70 $compiler->raw(', ');
72 $compiler->string($argument);
73 $first = false;
77 if ($this->hasNode('node')) {
78 if (!$first) {
79 $compiler->raw(', ');
81 $compiler->subcompile($this->getNode('node'));
82 $first = false;
85 if ($this->hasNode('arguments')) {
86 $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
88 $arguments = $this->getArguments($callable, $this->getNode('arguments'));
90 foreach ($arguments as $node) {
91 if (!$first) {
92 $compiler->raw(', ');
94 $compiler->subcompile($node);
95 $first = false;
99 $compiler->raw(')');
102 protected function getArguments($callable, $arguments)
104 $callType = $this->getAttribute('type');
105 $callName = $this->getAttribute('name');
107 $parameters = array();
108 $named = false;
109 foreach ($arguments as $name => $node) {
110 if (!is_int($name)) {
111 $named = true;
112 $name = $this->normalizeName($name);
113 } elseif ($named) {
114 throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName));
117 $parameters[$name] = $node;
120 $isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
121 if (!$named && !$isVariadic) {
122 return $parameters;
125 if (!$callable) {
126 if ($named) {
127 $message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName);
128 } else {
129 $message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName);
132 throw new LogicException($message);
135 $callableParameters = $this->getCallableParameters($callable, $isVariadic);
136 $arguments = array();
137 $names = array();
138 $missingArguments = array();
139 $optionalArguments = array();
140 $pos = 0;
141 foreach ($callableParameters as $callableParameter) {
142 $names[] = $name = $this->normalizeName($callableParameter->name);
144 if (array_key_exists($name, $parameters)) {
145 if (array_key_exists($pos, $parameters)) {
146 throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName));
149 if (count($missingArguments)) {
150 throw new Twig_Error_Syntax(sprintf(
151 '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".',
152 $name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments))
156 $arguments = array_merge($arguments, $optionalArguments);
157 $arguments[] = $parameters[$name];
158 unset($parameters[$name]);
159 $optionalArguments = array();
160 } elseif (array_key_exists($pos, $parameters)) {
161 $arguments = array_merge($arguments, $optionalArguments);
162 $arguments[] = $parameters[$pos];
163 unset($parameters[$pos]);
164 $optionalArguments = array();
165 ++$pos;
166 } elseif ($callableParameter->isDefaultValueAvailable()) {
167 $optionalArguments[] = new Twig_Node_Expression_Constant($callableParameter->getDefaultValue(), -1);
168 } elseif ($callableParameter->isOptional()) {
169 if (empty($parameters)) {
170 break;
171 } else {
172 $missingArguments[] = $name;
174 } else {
175 throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName));
179 if ($isVariadic) {
180 $arbitraryArguments = new Twig_Node_Expression_Array(array(), -1);
181 foreach ($parameters as $key => $value) {
182 if (is_int($key)) {
183 $arbitraryArguments->addElement($value);
184 } else {
185 $arbitraryArguments->addElement($value, new Twig_Node_Expression_Constant($key, -1));
187 unset($parameters[$key]);
190 if ($arbitraryArguments->count()) {
191 $arguments = array_merge($arguments, $optionalArguments);
192 $arguments[] = $arbitraryArguments;
196 if (!empty($parameters)) {
197 $unknownParameter = null;
198 foreach ($parameters as $parameter) {
199 if ($parameter instanceof Twig_Node) {
200 $unknownParameter = $parameter;
201 break;
205 throw new Twig_Error_Syntax(sprintf(
206 'Unknown argument%s "%s" for %s "%s(%s)".',
207 count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
208 ), $unknownParameter ? $unknownParameter->getTemplateLine() : -1);
211 return $arguments;
214 protected function normalizeName($name)
216 return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
219 private function getCallableParameters($callable, $isVariadic)
221 list($r) = $this->reflectCallable($callable);
222 if (null === $r) {
223 return array();
226 $parameters = $r->getParameters();
227 if ($this->hasNode('node')) {
228 array_shift($parameters);
230 if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
231 array_shift($parameters);
233 if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
234 array_shift($parameters);
236 if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
237 foreach ($this->getAttribute('arguments') as $argument) {
238 array_shift($parameters);
241 if ($isVariadic) {
242 $argument = end($parameters);
243 if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) {
244 array_pop($parameters);
245 } else {
246 $callableName = $r->name;
247 if ($r instanceof ReflectionMethod) {
248 $callableName = $r->getDeclaringClass()->name.'::'.$callableName;
251 throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
255 return $parameters;
258 private function reflectCallable($callable)
260 if (null !== $this->reflector) {
261 return $this->reflector;
264 if (is_array($callable)) {
265 if (!method_exists($callable[0], $callable[1])) {
266 // __call()
267 return array(null, array());
269 $r = new ReflectionMethod($callable[0], $callable[1]);
270 } elseif (is_object($callable) && !$callable instanceof Closure) {
271 $r = new ReflectionObject($callable);
272 $r = $r->getMethod('__invoke');
273 $callable = array($callable, '__invoke');
274 } elseif (is_string($callable) && false !== $pos = strpos($callable, '::')) {
275 $class = substr($callable, 0, $pos);
276 $method = substr($callable, $pos + 2);
277 if (!method_exists($class, $method)) {
278 // __staticCall()
279 return array(null, array());
281 $r = new ReflectionMethod($callable);
282 $callable = array($class, $method);
283 } else {
284 $r = new ReflectionFunction($callable);
287 return $this->reflector = array($r, $callable);
291 class_alias('Twig_Node_Expression_Call', 'Twig\Node\Expression\CallExpression', false);