[ Index ]

PHP Cross Reference of phpBB-3.3.3-deutsch

title

Body

[close]

/vendor/twig/twig/src/ -> Parser.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of Twig.
   5   *
   6   * (c) Fabien Potencier
   7   * (c) Armin Ronacher
   8   *
   9   * For the full copyright and license information, please view the LICENSE
  10   * file that was distributed with this source code.
  11   */
  12  
  13  namespace Twig;
  14  
  15  use Twig\Error\SyntaxError;
  16  use Twig\Node\BlockNode;
  17  use Twig\Node\BlockReferenceNode;
  18  use Twig\Node\BodyNode;
  19  use Twig\Node\Expression\AbstractExpression;
  20  use Twig\Node\MacroNode;
  21  use Twig\Node\ModuleNode;
  22  use Twig\Node\Node;
  23  use Twig\Node\NodeCaptureInterface;
  24  use Twig\Node\NodeOutputInterface;
  25  use Twig\Node\PrintNode;
  26  use Twig\Node\SpacelessNode;
  27  use Twig\Node\TextNode;
  28  use Twig\TokenParser\TokenParserInterface;
  29  
  30  /**
  31   * Default parser implementation.
  32   *
  33   * @author Fabien Potencier <fabien@symfony.com>
  34   */
  35  class Parser
  36  {
  37      private $stack = [];
  38      private $stream;
  39      private $parent;
  40      private $handlers;
  41      private $visitors;
  42      private $expressionParser;
  43      private $blocks;
  44      private $blockStack;
  45      private $macros;
  46      private $env;
  47      private $importedSymbols;
  48      private $traits;
  49      private $embeddedTemplates = [];
  50      private $varNameSalt = 0;
  51  
  52      public function __construct(Environment $env)
  53      {
  54          $this->env = $env;
  55      }
  56  
  57      public function getVarName()
  58      {
  59          return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->stream->getSourceContext()->getCode().$this->varNameSalt++));
  60      }
  61  
  62      public function parse(TokenStream $stream, $test = null, $dropNeedle = false)
  63      {
  64          $vars = get_object_vars($this);
  65          unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
  66          $this->stack[] = $vars;
  67  
  68          // tag handlers
  69          if (null === $this->handlers) {
  70              $this->handlers = [];
  71              foreach ($this->env->getTokenParsers() as $handler) {
  72                  $handler->setParser($this);
  73  
  74                  $this->handlers[$handler->getTag()] = $handler;
  75              }
  76          }
  77  
  78          // node visitors
  79          if (null === $this->visitors) {
  80              $this->visitors = $this->env->getNodeVisitors();
  81          }
  82  
  83          if (null === $this->expressionParser) {
  84              $this->expressionParser = new ExpressionParser($this, $this->env);
  85          }
  86  
  87          $this->stream = $stream;
  88          $this->parent = null;
  89          $this->blocks = [];
  90          $this->macros = [];
  91          $this->traits = [];
  92          $this->blockStack = [];
  93          $this->importedSymbols = [[]];
  94          $this->embeddedTemplates = [];
  95          $this->varNameSalt = 0;
  96  
  97          try {
  98              $body = $this->subparse($test, $dropNeedle);
  99  
 100              if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) {
 101                  $body = new Node();
 102              }
 103          } catch (SyntaxError $e) {
 104              if (!$e->getSourceContext()) {
 105                  $e->setSourceContext($this->stream->getSourceContext());
 106              }
 107  
 108              if (!$e->getTemplateLine()) {
 109                  $e->setTemplateLine($this->stream->getCurrent()->getLine());
 110              }
 111  
 112              throw $e;
 113          }
 114  
 115          $node = new ModuleNode(new BodyNode([$body]), $this->parent, new Node($this->blocks), new Node($this->macros), new Node($this->traits), $this->embeddedTemplates, $stream->getSourceContext());
 116  
 117          $traverser = new NodeTraverser($this->env, $this->visitors);
 118  
 119          $node = $traverser->traverse($node);
 120  
 121          // restore previous stack so previous parse() call can resume working
 122          foreach (array_pop($this->stack) as $key => $val) {
 123              $this->$key = $val;
 124          }
 125  
 126          return $node;
 127      }
 128  
 129      public function subparse($test, $dropNeedle = false)
 130      {
 131          $lineno = $this->getCurrentToken()->getLine();
 132          $rv = [];
 133          while (!$this->stream->isEOF()) {
 134              switch ($this->getCurrentToken()->getType()) {
 135                  case /* Token::TEXT_TYPE */ 0:
 136                      $token = $this->stream->next();
 137                      $rv[] = new TextNode($token->getValue(), $token->getLine());
 138                      break;
 139  
 140                  case /* Token::VAR_START_TYPE */ 2:
 141                      $token = $this->stream->next();
 142                      $expr = $this->expressionParser->parseExpression();
 143                      $this->stream->expect(/* Token::VAR_END_TYPE */ 4);
 144                      $rv[] = new PrintNode($expr, $token->getLine());
 145                      break;
 146  
 147                  case /* Token::BLOCK_START_TYPE */ 1:
 148                      $this->stream->next();
 149                      $token = $this->getCurrentToken();
 150  
 151                      if (/* Token::NAME_TYPE */ 5 !== $token->getType()) {
 152                          throw new SyntaxError('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext());
 153                      }
 154  
 155                      if (null !== $test && $test($token)) {
 156                          if ($dropNeedle) {
 157                              $this->stream->next();
 158                          }
 159  
 160                          if (1 === \count($rv)) {
 161                              return $rv[0];
 162                          }
 163  
 164                          return new Node($rv, [], $lineno);
 165                      }
 166  
 167                      if (!isset($this->handlers[$token->getValue()])) {
 168                          if (null !== $test) {
 169                              $e = new SyntaxError(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
 170  
 171                              if (\is_array($test) && isset($test[0]) && $test[0] instanceof TokenParserInterface) {
 172                                  $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno));
 173                              }
 174                          } else {
 175                              $e = new SyntaxError(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
 176                              $e->addSuggestions($token->getValue(), array_keys($this->env->getTags()));
 177                          }
 178  
 179                          throw $e;
 180                      }
 181  
 182                      $this->stream->next();
 183  
 184                      $subparser = $this->handlers[$token->getValue()];
 185                      $node = $subparser->parse($token);
 186                      if (null !== $node) {
 187                          $rv[] = $node;
 188                      }
 189                      break;
 190  
 191                  default:
 192                      throw new SyntaxError('Lexer or parser ended up in unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext());
 193              }
 194          }
 195  
 196          if (1 === \count($rv)) {
 197              return $rv[0];
 198          }
 199  
 200          return new Node($rv, [], $lineno);
 201      }
 202  
 203      public function getBlockStack()
 204      {
 205          return $this->blockStack;
 206      }
 207  
 208      public function peekBlockStack()
 209      {
 210          return isset($this->blockStack[\count($this->blockStack) - 1]) ? $this->blockStack[\count($this->blockStack) - 1] : null;
 211      }
 212  
 213      public function popBlockStack()
 214      {
 215          array_pop($this->blockStack);
 216      }
 217  
 218      public function pushBlockStack($name)
 219      {
 220          $this->blockStack[] = $name;
 221      }
 222  
 223      public function hasBlock($name)
 224      {
 225          return isset($this->blocks[$name]);
 226      }
 227  
 228      public function getBlock($name)
 229      {
 230          return $this->blocks[$name];
 231      }
 232  
 233      public function setBlock($name, BlockNode $value)
 234      {
 235          $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine());
 236      }
 237  
 238      public function hasMacro($name)
 239      {
 240          return isset($this->macros[$name]);
 241      }
 242  
 243      public function setMacro($name, MacroNode $node)
 244      {
 245          $this->macros[$name] = $node;
 246      }
 247  
 248      /**
 249       * @deprecated since Twig 2.7 as there are no reserved macro names anymore, will be removed in 3.0.
 250       */
 251      public function isReservedMacroName($name)
 252      {
 253          @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.7 and will be removed in 3.0.', __METHOD__), E_USER_DEPRECATED);
 254  
 255          return false;
 256      }
 257  
 258      public function addTrait($trait)
 259      {
 260          $this->traits[] = $trait;
 261      }
 262  
 263      public function hasTraits()
 264      {
 265          return \count($this->traits) > 0;
 266      }
 267  
 268      public function embedTemplate(ModuleNode $template)
 269      {
 270          $template->setIndex(mt_rand());
 271  
 272          $this->embeddedTemplates[] = $template;
 273      }
 274  
 275      public function addImportedSymbol($type, $alias, $name = null, AbstractExpression $node = null)
 276      {
 277          $this->importedSymbols[0][$type][$alias] = ['name' => $name, 'node' => $node];
 278      }
 279  
 280      public function getImportedSymbol($type, $alias)
 281      {
 282          // if the symbol does not exist in the current scope (0), try in the main/global scope (last index)
 283          return $this->importedSymbols[0][$type][$alias] ?? ($this->importedSymbols[\count($this->importedSymbols) - 1][$type][$alias] ?? null);
 284      }
 285  
 286      public function isMainScope()
 287      {
 288          return 1 === \count($this->importedSymbols);
 289      }
 290  
 291      public function pushLocalScope()
 292      {
 293          array_unshift($this->importedSymbols, []);
 294      }
 295  
 296      public function popLocalScope()
 297      {
 298          array_shift($this->importedSymbols);
 299      }
 300  
 301      /**
 302       * @return ExpressionParser
 303       */
 304      public function getExpressionParser()
 305      {
 306          return $this->expressionParser;
 307      }
 308  
 309      public function getParent()
 310      {
 311          return $this->parent;
 312      }
 313  
 314      public function setParent($parent)
 315      {
 316          $this->parent = $parent;
 317      }
 318  
 319      /**
 320       * @return TokenStream
 321       */
 322      public function getStream()
 323      {
 324          return $this->stream;
 325      }
 326  
 327      /**
 328       * @return Token
 329       */
 330      public function getCurrentToken()
 331      {
 332          return $this->stream->getCurrent();
 333      }
 334  
 335      private function filterBodyNodes(Node $node, bool $nested = false)
 336      {
 337          // check that the body does not contain non-empty output nodes
 338          if (
 339              ($node instanceof TextNode && !ctype_space($node->getAttribute('data')))
 340              ||
 341              // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
 342              (!$node instanceof TextNode && !$node instanceof BlockReferenceNode && ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode))
 343          ) {
 344              if (false !== strpos((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) {
 345                  $t = substr($node->getAttribute('data'), 3);
 346                  if ('' === $t || ctype_space($t)) {
 347                      // bypass empty nodes starting with a BOM
 348                      return;
 349                  }
 350              }
 351  
 352              throw new SyntaxError('A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag?', $node->getTemplateLine(), $this->stream->getSourceContext());
 353          }
 354  
 355          // bypass nodes that "capture" the output
 356          if ($node instanceof NodeCaptureInterface) {
 357              // a "block" tag in such a node will serve as a block definition AND be displayed in place as well
 358              return $node;
 359          }
 360  
 361          // to be removed completely in Twig 3.0
 362          if (!$nested && $node instanceof SpacelessNode) {
 363              @trigger_error(sprintf('Using the spaceless tag at the root level of a child template in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), E_USER_DEPRECATED);
 364          }
 365  
 366          // "block" tags that are not captured (see above) are only used for defining
 367          // the content of the block. In such a case, nesting it does not work as
 368          // expected as the definition is not part of the default template code flow.
 369          if ($nested && ($node instanceof BlockReferenceNode || $node instanceof \Twig_Node_BlockReference)) {
 370              //throw new SyntaxError('A block definition cannot be nested under non-capturing nodes.', $node->getTemplateLine(), $this->stream->getSourceContext());
 371              @trigger_error(sprintf('Nesting a block definition under a non-capturing node in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), E_USER_DEPRECATED);
 372  
 373              return;
 374          }
 375  
 376          // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
 377          if ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode) {
 378              return;
 379          }
 380  
 381          // here, $nested means "being at the root level of a child template"
 382          // we need to discard the wrapping "Twig_Node" for the "body" node
 383          $nested = $nested || ('Twig_Node' !== \get_class($node) && Node::class !== \get_class($node));
 384          foreach ($node as $k => $n) {
 385              if (null !== $n && null === $this->filterBodyNodes($n, $nested)) {
 386                  $node->removeNode($k);
 387              }
 388          }
 389  
 390          return $node;
 391      }
 392  }
 393  
 394  class_alias('Twig\Parser', 'Twig_Parser');


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