diff options
Diffstat (limited to 'inc/Twig/Node/Expression/Call.php')
-rw-r--r-- | inc/Twig/Node/Expression/Call.php | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/inc/Twig/Node/Expression/Call.php b/inc/Twig/Node/Expression/Call.php new file mode 100644 index 00000000..87b62deb --- /dev/null +++ b/inc/Twig/Node/Expression/Call.php | |||
@@ -0,0 +1,178 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2012 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 | abstract class Twig_Node_Expression_Call extends Twig_Node_Expression | ||
12 | { | ||
13 | protected function compileCallable(Twig_Compiler $compiler) | ||
14 | { | ||
15 | $callable = $this->getAttribute('callable'); | ||
16 | |||
17 | $closingParenthesis = false; | ||
18 | if ($callable) { | ||
19 | if (is_string($callable)) { | ||
20 | $compiler->raw($callable); | ||
21 | } elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) { | ||
22 | $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1])); | ||
23 | } else { | ||
24 | $type = ucfirst($this->getAttribute('type')); | ||
25 | $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name'))); | ||
26 | $closingParenthesis = true; | ||
27 | } | ||
28 | } else { | ||
29 | $compiler->raw($this->getAttribute('thing')->compile()); | ||
30 | } | ||
31 | |||
32 | $this->compileArguments($compiler); | ||
33 | |||
34 | if ($closingParenthesis) { | ||
35 | $compiler->raw(')'); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | protected function compileArguments(Twig_Compiler $compiler) | ||
40 | { | ||
41 | $compiler->raw('('); | ||
42 | |||
43 | $first = true; | ||
44 | |||
45 | if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { | ||
46 | $compiler->raw('$this->env'); | ||
47 | $first = false; | ||
48 | } | ||
49 | |||
50 | if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { | ||
51 | if (!$first) { | ||
52 | $compiler->raw(', '); | ||
53 | } | ||
54 | $compiler->raw('$context'); | ||
55 | $first = false; | ||
56 | } | ||
57 | |||
58 | if ($this->hasAttribute('arguments')) { | ||
59 | foreach ($this->getAttribute('arguments') as $argument) { | ||
60 | if (!$first) { | ||
61 | $compiler->raw(', '); | ||
62 | } | ||
63 | $compiler->string($argument); | ||
64 | $first = false; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | if ($this->hasNode('node')) { | ||
69 | if (!$first) { | ||
70 | $compiler->raw(', '); | ||
71 | } | ||
72 | $compiler->subcompile($this->getNode('node')); | ||
73 | $first = false; | ||
74 | } | ||
75 | |||
76 | if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) { | ||
77 | $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null; | ||
78 | |||
79 | $arguments = $this->getArguments($callable, $this->getNode('arguments')); | ||
80 | |||
81 | foreach ($arguments as $node) { | ||
82 | if (!$first) { | ||
83 | $compiler->raw(', '); | ||
84 | } | ||
85 | $compiler->subcompile($node); | ||
86 | $first = false; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | $compiler->raw(')'); | ||
91 | } | ||
92 | |||
93 | protected function getArguments($callable, $arguments) | ||
94 | { | ||
95 | $parameters = array(); | ||
96 | $named = false; | ||
97 | foreach ($arguments as $name => $node) { | ||
98 | if (!is_int($name)) { | ||
99 | $named = true; | ||
100 | $name = $this->normalizeName($name); | ||
101 | } elseif ($named) { | ||
102 | throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name'))); | ||
103 | } | ||
104 | |||
105 | $parameters[$name] = $node; | ||
106 | } | ||
107 | |||
108 | if (!$named) { | ||
109 | return $parameters; | ||
110 | } | ||
111 | |||
112 | if (!$callable) { | ||
113 | throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name'))); | ||
114 | } | ||
115 | |||
116 | // manage named arguments | ||
117 | if (is_array($callable)) { | ||
118 | $r = new ReflectionMethod($callable[0], $callable[1]); | ||
119 | } elseif (is_object($callable) && !$callable instanceof Closure) { | ||
120 | $r = new ReflectionObject($callable); | ||
121 | $r = $r->getMethod('__invoke'); | ||
122 | } else { | ||
123 | $r = new ReflectionFunction($callable); | ||
124 | } | ||
125 | |||
126 | $definition = $r->getParameters(); | ||
127 | if ($this->hasNode('node')) { | ||
128 | array_shift($definition); | ||
129 | } | ||
130 | if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { | ||
131 | array_shift($definition); | ||
132 | } | ||
133 | if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { | ||
134 | array_shift($definition); | ||
135 | } | ||
136 | if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) { | ||
137 | foreach ($this->getAttribute('arguments') as $argument) { | ||
138 | array_shift($definition); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | $arguments = array(); | ||
143 | $pos = 0; | ||
144 | foreach ($definition as $param) { | ||
145 | $name = $this->normalizeName($param->name); | ||
146 | |||
147 | if (array_key_exists($name, $parameters)) { | ||
148 | if (array_key_exists($pos, $parameters)) { | ||
149 | throw new Twig_Error_Syntax(sprintf('Arguments "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name'))); | ||
150 | } | ||
151 | |||
152 | $arguments[] = $parameters[$name]; | ||
153 | unset($parameters[$name]); | ||
154 | } elseif (array_key_exists($pos, $parameters)) { | ||
155 | $arguments[] = $parameters[$pos]; | ||
156 | unset($parameters[$pos]); | ||
157 | ++$pos; | ||
158 | } elseif ($param->isDefaultValueAvailable()) { | ||
159 | $arguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1); | ||
160 | } elseif ($param->isOptional()) { | ||
161 | break; | ||
162 | } else { | ||
163 | throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name'))); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | foreach (array_keys($parameters) as $name) { | ||
168 | throw new Twig_Error_Syntax(sprintf('Unknown argument "%s" for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name'))); | ||
169 | } | ||
170 | |||
171 | return $arguments; | ||
172 | } | ||
173 | |||
174 | protected function normalizeName($name) | ||
175 | { | ||
176 | return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name)); | ||
177 | } | ||
178 | } | ||