ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Call.php
Go to the documentation of this file.
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  */
12 {
13  private $reflector;
14 
15  protected function compileCallable(Twig_Compiler $compiler)
16  {
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]));
28  }
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;
35  }
36  }
37  } else {
38  $compiler->raw($this->getAttribute('thing')->compile());
39  }
40 
41  $this->compileArguments($compiler);
42 
43  if ($closingParenthesis) {
44  $compiler->raw(')');
45  }
46  }
47 
48  protected function compileArguments(Twig_Compiler $compiler)
49  {
50  $compiler->raw('(');
51 
52  $first = true;
53 
54  if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
55  $compiler->raw('$this->env');
56  $first = false;
57  }
58 
59  if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
60  if (!$first) {
61  $compiler->raw(', ');
62  }
63  $compiler->raw('$context');
64  $first = false;
65  }
66 
67  if ($this->hasAttribute('arguments')) {
68  foreach ($this->getAttribute('arguments') as $argument) {
69  if (!$first) {
70  $compiler->raw(', ');
71  }
72  $compiler->string($argument);
73  $first = false;
74  }
75  }
76 
77  if ($this->hasNode('node')) {
78  if (!$first) {
79  $compiler->raw(', ');
80  }
81  $compiler->subcompile($this->getNode('node'));
82  $first = false;
83  }
84 
85  if ($this->hasNode('arguments')) {
86  $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
87 
88  $arguments = $this->getArguments($callable, $this->getNode('arguments'));
89 
90  foreach ($arguments as $node) {
91  if (!$first) {
92  $compiler->raw(', ');
93  }
94  $compiler->subcompile($node);
95  $first = false;
96  }
97  }
98 
99  $compiler->raw(')');
100  }
101 
102  protected function getArguments($callable, $arguments)
103  {
104  $callType = $this->getAttribute('type');
105  $callName = $this->getAttribute('name');
106 
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));
115  }
116 
117  $parameters[$name] = $node;
118  }
119 
120  $isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
121  if (!$named && !$isVariadic) {
122  return $parameters;
123  }
124 
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);
130  }
131 
132  throw new LogicException($message);
133  }
134 
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);
143 
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));
147  }
148 
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))
153  );
154  }
155 
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;
173  }
174  } else {
175  throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName));
176  }
177  }
178 
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));
186  }
187  unset($parameters[$key]);
188  }
189 
190  if ($arbitraryArguments->count()) {
191  $arguments = array_merge($arguments, $optionalArguments);
192  $arguments[] = $arbitraryArguments;
193  }
194  }
195 
196  if (!empty($parameters)) {
197  $unknownParameter = null;
198  foreach ($parameters as $parameter) {
199  if ($parameter instanceof Twig_Node) {
200  $unknownParameter = $parameter;
201  break;
202  }
203  }
204 
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);
209  }
210 
211  return $arguments;
212  }
213 
214  protected function normalizeName($name)
215  {
216  return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
217  }
218 
219  private function getCallableParameters($callable, $isVariadic)
220  {
221  list($r) = $this->reflectCallable($callable);
222  if (null === $r) {
223  return array();
224  }
225 
226  $parameters = $r->getParameters();
227  if ($this->hasNode('node')) {
228  array_shift($parameters);
229  }
230  if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
231  array_shift($parameters);
232  }
233  if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
234  array_shift($parameters);
235  }
236  if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
237  foreach ($this->getAttribute('arguments') as $argument) {
238  array_shift($parameters);
239  }
240  }
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;
249  }
250 
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')));
252  }
253  }
254 
255  return $parameters;
256  }
257 
258  private function reflectCallable($callable)
259  {
260  if (null !== $this->reflector) {
261  return $this->reflector;
262  }
263 
264  if (is_array($callable)) {
265  if (!method_exists($callable[0], $callable[1])) {
266  // __call()
267  return array(null, array());
268  }
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());
280  }
281  $r = new ReflectionMethod($callable);
282  $callable = array($class, $method);
283  } else {
284  $r = new ReflectionFunction($callable);
285  }
286 
287  return $this->reflector = array($r, $callable);
288  }
289 }
290 
291 class_alias('Twig_Node_Expression_Call', 'Twig\Node\Expression\CallExpression', false);
raw($string)
Adds a raw string to the compiled code.
Definition: Compiler.php:112
Interface implemented by extension classes.
subcompile(Twig_NodeInterface $node, $raw=true)
Definition: Compiler.php:94
Represents a node in the AST.
Definition: Node.php:18
$type
string($value)
Adds a quoted string to the compiled code.
Definition: Compiler.php:157
reflectCallable($callable)
Definition: Call.php:258
hasAttribute($name)
Definition: Node.php:144
compileArguments(Twig_Compiler $compiler)
Definition: Call.php:48
Exception thrown when a syntax error occurs during lexing or parsing of a template.
Definition: Syntax.php:18
Abstract class for all nodes that represents an expression.
Definition: Expression.php:18
$r
Definition: example_031.php:79
catch(Exception $e) $message
compile(Twig_Compiler $compiler)
Compiles the node to PHP.
Definition: Node.php:114
getAttribute($name)
Definition: Node.php:152
hasNode($name)
Definition: Node.php:178
getNode($name)
Definition: Node.php:186
getCallableParameters($callable, $isVariadic)
Definition: Call.php:219
compileCallable(Twig_Compiler $compiler)
Definition: Call.php:15
count()
Definition: Node.php:209
getArguments($callable, $arguments)
Definition: Call.php:102
$key
Definition: croninfo.php:18