Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
CallExpression.php
001 <?php
002
003 /*
004 * This file is part of Twig.
005 *
006 * (c) Fabien Potencier
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Twig\Node\Expression;
013
014 use Twig\Compiler;
015 use Twig\Error\SyntaxError;
016 use Twig\Extension\ExtensionInterface;
017 use Twig\Node\Node;
018
019 abstract class CallExpression extends AbstractExpression
020 {
021 private $reflector;
022
023 protected function compileCallable(Compiler $compiler)
024 {
025 $callable = $this->getAttribute('callable');
026
027 if (\is_string($callable) && false === strpos($callable, '::')) {
028 $compiler->raw($callable);
029 } else {
030 [$r, $callable] = $this->reflectCallable($callable);
031
032 if (\is_string($callable)) {
033 $compiler->raw($callable);
034 } elseif (\is_array($callable) && \is_string($callable[0])) {
035 if (!$r instanceof \ReflectionMethod || $r->isStatic()) {
036 $compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
037 } else {
038 $compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
039 }
040 } elseif (\is_array($callable) && $callable[0] instanceof ExtensionInterface) {
041 $class = \get_class($callable[0]);
042 if (!$compiler->getEnvironment()->hasExtension($class)) {
043 // Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
044 $compiler->raw(sprintf('$this->env->getExtension(\'%s\')', $class));
045 } else {
046 $compiler->raw(sprintf('$this->extensions[\'%s\']', ltrim($class, '\\')));
047 }
048
049 $compiler->raw(sprintf('->%s', $callable[1]));
050 } else {
051 $compiler->raw(sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
052 }
053 }
054
055 $this->compileArguments($compiler);
056 }
057
058 protected function compileArguments(Compiler $compiler, $isArray = false)
059 {
060 $compiler->raw($isArray ? '[' : '(');
061
062 $first = true;
063
064 if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
065 $compiler->raw('$this->env');
066 $first = false;
067 }
068
069 if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
070 if (!$first) {
071 $compiler->raw(', ');
072 }
073 $compiler->raw('$context');
074 $first = false;
075 }
076
077 if ($this->hasAttribute('arguments')) {
078 foreach ($this->getAttribute('arguments') as $argument) {
079 if (!$first) {
080 $compiler->raw(', ');
081 }
082 $compiler->string($argument);
083 $first = false;
084 }
085 }
086
087 if ($this->hasNode('node')) {
088 if (!$first) {
089 $compiler->raw(', ');
090 }
091 $compiler->subcompile($this->getNode('node'));
092 $first = false;
093 }
094
095 if ($this->hasNode('arguments')) {
096 $callable = $this->getAttribute('callable');
097 $arguments = $this->getArguments($callable, $this->getNode('arguments'));
098 foreach ($arguments as $node) {
099 if (!$first) {
100 $compiler->raw(', ');
101 }
102 $compiler->subcompile($node);
103 $first = false;
104 }
105 }
106
107 $compiler->raw($isArray ? ']' : ')');
108 }
109
110 protected function getArguments($callable, $arguments)
111 {
112 $callType = $this->getAttribute('type');
113 $callName = $this->getAttribute('name');
114
115 $parameters = [];
116 $named = false;
117 foreach ($arguments as $name => $node) {
118 if (!\is_int($name)) {
119 $named = true;
120 $name = $this->normalizeName($name);
121 } elseif ($named) {
122 throw new SyntaxError(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
123 }
124
125 $parameters[$name] = $node;
126 }
127
128 $isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
129 if (!$named && !$isVariadic) {
130 return $parameters;
131 }
132
133 if (!$callable) {
134 if ($named) {
135 $message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName);
136 } else {
137 $message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName);
138 }
139
140 throw new \LogicException($message);
141 }
142
143 list($callableParameters, $isPhpVariadic) = $this->getCallableParameters($callable, $isVariadic);
144 $arguments = [];
145 $names = [];
146 $missingArguments = [];
147 $optionalArguments = [];
148 $pos = 0;
149 foreach ($callableParameters as $callableParameter) {
150 $name = $this->normalizeName($callableParameter->name);
151 if (\PHP_VERSION_ID >= 80000 && 'range' === $callable) {
152 if ('start' === $name) {
153 $name = 'low';
154 } elseif ('end' === $name) {
155 $name = 'high';
156 }
157 }
158
159 $names[] = $name;
160
161 if (\array_key_exists($name, $parameters)) {
162 if (\array_key_exists($pos, $parameters)) {
163 throw new SyntaxError(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
164 }
165
166 if (\count($missingArguments)) {
167 throw new SyntaxError(sprintf(
168 '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".',
169 $name, $callType, $callName, implode(', ', $names), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments)
170 ), $this->getTemplateLine(), $this->getSourceContext());
171 }
172
173 $arguments = array_merge($arguments, $optionalArguments);
174 $arguments[] = $parameters[$name];
175 unset($parameters[$name]);
176 $optionalArguments = [];
177 } elseif (\array_key_exists($pos, $parameters)) {
178 $arguments = array_merge($arguments, $optionalArguments);
179 $arguments[] = $parameters[$pos];
180 unset($parameters[$pos]);
181 $optionalArguments = [];
182 ++$pos;
183 } elseif ($callableParameter->isDefaultValueAvailable()) {
184 $optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), -1);
185 } elseif ($callableParameter->isOptional()) {
186 if (empty($parameters)) {
187 break;
188 } else {
189 $missingArguments[] = $name;
190 }
191 } else {
192 throw new SyntaxError(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
193 }
194 }
195
196 if ($isVariadic) {
197 $arbitraryArguments = $isPhpVariadic ? new VariadicExpression([], -1) : new ArrayExpression([], -1);
198 foreach ($parameters as $key => $value) {
199 if (\is_int($key)) {
200 $arbitraryArguments->addElement($value);
201 } else {
202 $arbitraryArguments->addElement($value, new ConstantExpression($key, -1));
203 }
204 unset($parameters[$key]);
205 }
206
207 if ($arbitraryArguments->count()) {
208 $arguments = array_merge($arguments, $optionalArguments);
209 $arguments[] = $arbitraryArguments;
210 }
211 }
212
213 if (!empty($parameters)) {
214 $unknownParameter = null;
215 foreach ($parameters as $parameter) {
216 if ($parameter instanceof Node) {
217 $unknownParameter = $parameter;
218 break;
219 }
220 }
221
222 throw new SyntaxError(
223 sprintf(
224 'Unknown argument%s "%s" for %s "%s(%s)".',
225 \count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
226 ),
227 $unknownParameter ? $unknownParameter->getTemplateLine() : $this->getTemplateLine(),
228 $unknownParameter ? $unknownParameter->getSourceContext() : $this->getSourceContext()
229 );
230 }
231
232 return $arguments;
233 }
234
235 protected function normalizeName($name)
236 {
237 return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], $name));
238 }
239
240 private function getCallableParameters($callable, bool $isVariadic): array
241 {
242 [$r, , $callableName] = $this->reflectCallable($callable);
243
244 $parameters = $r->getParameters();
245 if ($this->hasNode('node')) {
246 array_shift($parameters);
247 }
248 if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
249 array_shift($parameters);
250 }
251 if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
252 array_shift($parameters);
253 }
254 if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
255 foreach ($this->getAttribute('arguments') as $argument) {
256 array_shift($parameters);
257 }
258 }
259 $isPhpVariadic = false;
260 if ($isVariadic) {
261 $argument = end($parameters);
262 $isArray = $argument && $argument->hasType() && 'array' === $argument->getType()->getName();
263 if ($isArray && $argument->isDefaultValueAvailable() && [] === $argument->getDefaultValue()) {
264 array_pop($parameters);
265 } elseif ($argument && $argument->isVariadic()) {
266 array_pop($parameters);
267 $isPhpVariadic = true;
268 } else {
269 throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
270 }
271 }
272
273 return [$parameters, $isPhpVariadic];
274 }
275
276 private function reflectCallable($callable)
277 {
278 if (null !== $this->reflector) {
279 return $this->reflector;
280 }
281
282 if (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
283 $callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)];
284 }
285
286 if (\is_array($callable) && method_exists($callable[0], $callable[1])) {
287 $r = new \ReflectionMethod($callable[0], $callable[1]);
288
289 return $this->reflector = [$r, $callable, $r->class.'::'.$r->name];
290 }
291
292 $checkVisibility = $callable instanceof \Closure;
293 try {
294 $closure = \Closure::fromCallable($callable);
295 } catch (\TypeError $e) {
296 throw new \LogicException(sprintf('Callback for %s "%s" is not callable in the current scope.', $this->getAttribute('type'), $this->getAttribute('name')), 0, $e);
297 }
298 $r = new \ReflectionFunction($closure);
299
300 if (false !== strpos($r->name, '{closure}')) {
301 return $this->reflector = [$r, $callable, 'Closure'];
302 }
303
304 if ($object = $r->getClosureThis()) {
305 $callable = [$object, $r->name];
306 $callableName = (\function_exists('get_debug_type') ? get_debug_type($object) : \get_class($object)).'::'.$r->name;
307 } elseif (\PHP_VERSION_ID >= 80111 && $class = $r->getClosureCalledClass()) {
308 $callableName = $class->name.'::'.$r->name;
309 } elseif (\PHP_VERSION_ID < 80111 && $class = $r->getClosureScopeClass()) {
310 $callableName = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name;
311 } else {
312 $callable = $callableName = $r->name;
313 }
314
315 if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) {
316 $callable = $r->getClosure();
317 }
318
319 return $this->reflector = [$r, $callable, $callableName];
320 }
321 }
322
323 class_alias('Twig\Node\Expression\CallExpression', 'Twig_Node_Expression_Call');
324