[ Index ]

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


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1