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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

CallExpression.php

Zuletzt modifiziert: 02.04.2025, 15:04 - Dateigröße: 12.63 KiB


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