Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

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: 01.05.2026, 11:28 - 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