[ Index ]

PHP Cross Reference of phpBB-3.3.3-deutsch

title

Body

[close]

/vendor/twig/twig/src/Node/Expression/ -> CallExpression.php (source)

   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   */
  11  
  12  namespace Twig\Node\Expression;
  13  
  14  use Twig\Compiler;
  15  use Twig\Error\SyntaxError;
  16  use Twig\Extension\ExtensionInterface;
  17  use Twig\Node\Node;
  18  
  19  abstract class CallExpression extends AbstractExpression
  20  {
  21      private $reflector;
  22  
  23      protected function compileCallable(Compiler $compiler)
  24      {
  25          $callable = $this->getAttribute('callable');
  26  
  27          $closingParenthesis = false;
  28          $isArray = false;
  29          if (\is_string($callable) && false === strpos($callable, '::')) {
  30              $compiler->raw($callable);
  31          } else {
  32              list($r, $callable) = $this->reflectCallable($callable);
  33              if ($r instanceof \ReflectionMethod && \is_string($callable[0])) {
  34                  if ($r->isStatic()) {
  35                      $compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
  36                  } else {
  37                      $compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
  38                  }
  39              } elseif ($r instanceof \ReflectionMethod && $callable[0] instanceof ExtensionInterface) {
  40                  // For BC/FC with namespaced aliases
  41                  $class = (new \ReflectionClass(\get_class($callable[0])))->name;
  42                  if (!$compiler->getEnvironment()->hasExtension($class)) {
  43                      // Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
  44                      $compiler->raw(sprintf('$this->env->getExtension(\'%s\')', $class));
  45                  } else {
  46                      $compiler->raw(sprintf('$this->extensions[\'%s\']', ltrim($class, '\\')));
  47                  }
  48  
  49                  $compiler->raw(sprintf('->%s', $callable[1]));
  50              } else {
  51                  $closingParenthesis = true;
  52                  $isArray = true;
  53                  $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), ', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
  54              }
  55          }
  56  
  57          $this->compileArguments($compiler, $isArray);
  58  
  59          if ($closingParenthesis) {
  60              $compiler->raw(')');
  61          }
  62      }
  63  
  64      protected function compileArguments(Compiler $compiler, $isArray = false)
  65      {
  66          $compiler->raw($isArray ? '[' : '(');
  67  
  68          $first = true;
  69  
  70          if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
  71              $compiler->raw('$this->env');
  72              $first = false;
  73          }
  74  
  75          if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
  76              if (!$first) {
  77                  $compiler->raw(', ');
  78              }
  79              $compiler->raw('$context');
  80              $first = false;
  81          }
  82  
  83          if ($this->hasAttribute('arguments')) {
  84              foreach ($this->getAttribute('arguments') as $argument) {
  85                  if (!$first) {
  86                      $compiler->raw(', ');
  87                  }
  88                  $compiler->string($argument);
  89                  $first = false;
  90              }
  91          }
  92  
  93          if ($this->hasNode('node')) {
  94              if (!$first) {
  95                  $compiler->raw(', ');
  96              }
  97              $compiler->subcompile($this->getNode('node'));
  98              $first = false;
  99          }
 100  
 101          if ($this->hasNode('arguments')) {
 102              $callable = $this->getAttribute('callable');
 103              $arguments = $this->getArguments($callable, $this->getNode('arguments'));
 104              foreach ($arguments as $node) {
 105                  if (!$first) {
 106                      $compiler->raw(', ');
 107                  }
 108                  $compiler->subcompile($node);
 109                  $first = false;
 110              }
 111          }
 112  
 113          $compiler->raw($isArray ? ']' : ')');
 114      }
 115  
 116      protected function getArguments($callable, $arguments)
 117      {
 118          $callType = $this->getAttribute('type');
 119          $callName = $this->getAttribute('name');
 120  
 121          $parameters = [];
 122          $named = false;
 123          foreach ($arguments as $name => $node) {
 124              if (!\is_int($name)) {
 125                  $named = true;
 126                  $name = $this->normalizeName($name);
 127              } elseif ($named) {
 128                  throw new SyntaxError(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
 129              }
 130  
 131              $parameters[$name] = $node;
 132          }
 133  
 134          $isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
 135          if (!$named && !$isVariadic) {
 136              return $parameters;
 137          }
 138  
 139          if (!$callable) {
 140              if ($named) {
 141                  $message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName);
 142              } else {
 143                  $message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName);
 144              }
 145  
 146              throw new \LogicException($message);
 147          }
 148  
 149          list($callableParameters, $isPhpVariadic) = $this->getCallableParameters($callable, $isVariadic);
 150          $arguments = [];
 151          $names = [];
 152          $missingArguments = [];
 153          $optionalArguments = [];
 154          $pos = 0;
 155          foreach ($callableParameters as $callableParameter) {
 156              $names[] = $name = $this->normalizeName($callableParameter->name);
 157  
 158              if (\array_key_exists($name, $parameters)) {
 159                  if (\array_key_exists($pos, $parameters)) {
 160                      throw new SyntaxError(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
 161                  }
 162  
 163                  if (\count($missingArguments)) {
 164                      throw new SyntaxError(sprintf(
 165                          '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".',
 166                          $name, $callType, $callName, implode(', ', $names), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments)
 167                      ), $this->getTemplateLine(), $this->getSourceContext());
 168                  }
 169  
 170                  $arguments = array_merge($arguments, $optionalArguments);
 171                  $arguments[] = $parameters[$name];
 172                  unset($parameters[$name]);
 173                  $optionalArguments = [];
 174              } elseif (\array_key_exists($pos, $parameters)) {
 175                  $arguments = array_merge($arguments, $optionalArguments);
 176                  $arguments[] = $parameters[$pos];
 177                  unset($parameters[$pos]);
 178                  $optionalArguments = [];
 179                  ++$pos;
 180              } elseif ($callableParameter->isDefaultValueAvailable()) {
 181                  $optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), -1);
 182              } elseif ($callableParameter->isOptional()) {
 183                  if (empty($parameters)) {
 184                      break;
 185                  } else {
 186                      $missingArguments[] = $name;
 187                  }
 188              } else {
 189                  throw new SyntaxError(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
 190              }
 191          }
 192  
 193          if ($isVariadic) {
 194              $arbitraryArguments = $isPhpVariadic ? new VariadicExpression([], -1) : new ArrayExpression([], -1);
 195              foreach ($parameters as $key => $value) {
 196                  if (\is_int($key)) {
 197                      $arbitraryArguments->addElement($value);
 198                  } else {
 199                      $arbitraryArguments->addElement($value, new ConstantExpression($key, -1));
 200                  }
 201                  unset($parameters[$key]);
 202              }
 203  
 204              if ($arbitraryArguments->count()) {
 205                  $arguments = array_merge($arguments, $optionalArguments);
 206                  $arguments[] = $arbitraryArguments;
 207              }
 208          }
 209  
 210          if (!empty($parameters)) {
 211              $unknownParameter = null;
 212              foreach ($parameters as $parameter) {
 213                  if ($parameter instanceof Node) {
 214                      $unknownParameter = $parameter;
 215                      break;
 216                  }
 217              }
 218  
 219              throw new SyntaxError(
 220                  sprintf(
 221                      'Unknown argument%s "%s" for %s "%s(%s)".',
 222                      \count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
 223                  ),
 224                  $unknownParameter ? $unknownParameter->getTemplateLine() : $this->getTemplateLine(),
 225                  $unknownParameter ? $unknownParameter->getSourceContext() : $this->getSourceContext()
 226              );
 227          }
 228  
 229          return $arguments;
 230      }
 231  
 232      protected function normalizeName($name)
 233      {
 234          return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], $name));
 235      }
 236  
 237      private function getCallableParameters($callable, bool $isVariadic): array
 238      {
 239          list($r) = $this->reflectCallable($callable);
 240          if (null === $r) {
 241              return [[], false];
 242          }
 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                  $callableName = $r->name;
 270                  if ($r instanceof \ReflectionMethod) {
 271                      $callableName = $r->getDeclaringClass()->name.'::'.$callableName;
 272                  }
 273  
 274                  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')));
 275              }
 276          }
 277  
 278          return [$parameters, $isPhpVariadic];
 279      }
 280  
 281      private function reflectCallable($callable)
 282      {
 283          if (null !== $this->reflector) {
 284              return $this->reflector;
 285          }
 286  
 287          if (\is_array($callable)) {
 288              if (!method_exists($callable[0], $callable[1])) {
 289                  // __call()
 290                  return [null, []];
 291              }
 292              $r = new \ReflectionMethod($callable[0], $callable[1]);
 293          } elseif (\is_object($callable) && !$callable instanceof \Closure) {
 294              $r = new \ReflectionObject($callable);
 295              $r = $r->getMethod('__invoke');
 296              $callable = [$callable, '__invoke'];
 297          } elseif (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
 298              $class = substr($callable, 0, $pos);
 299              $method = substr($callable, $pos + 2);
 300              if (!method_exists($class, $method)) {
 301                  // __staticCall()
 302                  return [null, []];
 303              }
 304              $r = new \ReflectionMethod($callable);
 305              $callable = [$class, $method];
 306          } else {
 307              $r = new \ReflectionFunction($callable);
 308          }
 309  
 310          return $this->reflector = [$r, $callable];
 311      }
 312  }
 313  
 314  class_alias('Twig\Node\Expression\CallExpression', 'Twig_Node_Expression_Call');


Generated: Sun Feb 14 20:08:31 2021 Cross-referenced by PHPXref 0.7.1