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

Parser.php

Zuletzt modifiziert: 01.05.2026, 11:26 - Dateigröße: 12.71 KiB


 001  <?php
 002   
 003  /*
 004   * This file is part of Twig.
 005   *
 006   * (c) Fabien Potencier
 007   * (c) Armin Ronacher
 008   *
 009   * For the full copyright and license information, please view the LICENSE
 010   * file that was distributed with this source code.
 011   */
 012   
 013  namespace Twig;
 014   
 015  use Twig\Error\SyntaxError;
 016  use Twig\Node\BlockNode;
 017  use Twig\Node\BlockReferenceNode;
 018  use Twig\Node\BodyNode;
 019  use Twig\Node\Expression\AbstractExpression;
 020  use Twig\Node\MacroNode;
 021  use Twig\Node\ModuleNode;
 022  use Twig\Node\Node;
 023  use Twig\Node\NodeCaptureInterface;
 024  use Twig\Node\NodeOutputInterface;
 025  use Twig\Node\PrintNode;
 026  use Twig\Node\SpacelessNode;
 027  use Twig\Node\TextNode;
 028  use Twig\TokenParser\TokenParserInterface;
 029   
 030  /**
 031   * Default parser implementation.
 032   *
 033   * @author Fabien Potencier <fabien@symfony.com>
 034   */
 035  class Parser
 036  {
 037      private $stack = [];
 038      private $stream;
 039      private $parent;
 040      private $handlers;
 041      private $visitors;
 042      private $expressionParser;
 043      private $blocks;
 044      private $blockStack;
 045      private $macros;
 046      private $env;
 047      private $importedSymbols;
 048      private $traits;
 049      private $embeddedTemplates = [];
 050      private $varNameSalt = 0;
 051   
 052      public function __construct(Environment $env)
 053      {
 054          $this->env = $env;
 055      }
 056   
 057      public function getVarName()
 058      {
 059          return sprintf('__internal_parse_%d', $this->varNameSalt++);
 060      }
 061   
 062      public function parse(TokenStream $stream, $test = null, $dropNeedle = false)
 063      {
 064          $vars = get_object_vars($this);
 065          unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']);
 066          $this->stack[] = $vars;
 067   
 068          // tag handlers
 069          if (null === $this->handlers) {
 070              $this->handlers = [];
 071              foreach ($this->env->getTokenParsers() as $handler) {
 072                  $handler->setParser($this);
 073   
 074                  $this->handlers[$handler->getTag()] = $handler;
 075              }
 076          }
 077   
 078          // node visitors
 079          if (null === $this->visitors) {
 080              $this->visitors = $this->env->getNodeVisitors();
 081          }
 082   
 083          if (null === $this->expressionParser) {
 084              $this->expressionParser = new ExpressionParser($this, $this->env);
 085          }
 086   
 087          $this->stream = $stream;
 088          $this->parent = null;
 089          $this->blocks = [];
 090          $this->macros = [];
 091          $this->traits = [];
 092          $this->blockStack = [];
 093          $this->importedSymbols = [[]];
 094          $this->embeddedTemplates = [];
 095   
 096          try {
 097              $body = $this->subparse($test, $dropNeedle);
 098   
 099              if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) {
 100                  $body = new Node();
 101              }
 102          } catch (SyntaxError $e) {
 103              if (!$e->getSourceContext()) {
 104                  $e->setSourceContext($this->stream->getSourceContext());
 105              }
 106   
 107              if (!$e->getTemplateLine()) {
 108                  $e->setTemplateLine($this->stream->getCurrent()->getLine());
 109              }
 110   
 111              throw $e;
 112          }
 113   
 114          $node = new ModuleNode(new BodyNode([$body]), $this->parent, new Node($this->blocks), new Node($this->macros), new Node($this->traits), $this->embeddedTemplates, $stream->getSourceContext());
 115   
 116          $traverser = new NodeTraverser($this->env, $this->visitors);
 117   
 118          $node = $traverser->traverse($node);
 119   
 120          // restore previous stack so previous parse() call can resume working
 121          foreach (array_pop($this->stack) as $key => $val) {
 122              $this->$key = $val;
 123          }
 124   
 125          return $node;
 126      }
 127   
 128      public function subparse($test, $dropNeedle = false)
 129      {
 130          $lineno = $this->getCurrentToken()->getLine();
 131          $rv = [];
 132          while (!$this->stream->isEOF()) {
 133              switch ($this->getCurrentToken()->getType()) {
 134                  case /* Token::TEXT_TYPE */ 0:
 135                      $token = $this->stream->next();
 136                      $rv[] = new TextNode($token->getValue(), $token->getLine());
 137                      break;
 138   
 139                  case /* Token::VAR_START_TYPE */ 2:
 140                      $token = $this->stream->next();
 141                      $expr = $this->expressionParser->parseExpression();
 142                      $this->stream->expect(/* Token::VAR_END_TYPE */ 4);
 143                      $rv[] = new PrintNode($expr, $token->getLine());
 144                      break;
 145   
 146                  case /* Token::BLOCK_START_TYPE */ 1:
 147                      $this->stream->next();
 148                      $token = $this->getCurrentToken();
 149   
 150                      if (/* Token::NAME_TYPE */ 5 !== $token->getType()) {
 151                          throw new SyntaxError('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext());
 152                      }
 153   
 154                      if (null !== $test && $test($token)) {
 155                          if ($dropNeedle) {
 156                              $this->stream->next();
 157                          }
 158   
 159                          if (1 === \count($rv)) {
 160                              return $rv[0];
 161                          }
 162   
 163                          return new Node($rv, [], $lineno);
 164                      }
 165   
 166                      if (!isset($this->handlers[$token->getValue()])) {
 167                          if (null !== $test) {
 168                              $e = new SyntaxError(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
 169   
 170                              if (\is_array($test) && isset($test[0]) && $test[0] instanceof TokenParserInterface) {
 171                                  $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno));
 172                              }
 173                          } else {
 174                              $e = new SyntaxError(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
 175                              $e->addSuggestions($token->getValue(), array_keys($this->env->getTags()));
 176                          }
 177   
 178                          throw $e;
 179                      }
 180   
 181                      $this->stream->next();
 182   
 183                      $subparser = $this->handlers[$token->getValue()];
 184                      $node = $subparser->parse($token);
 185                      if (null !== $node) {
 186                          $rv[] = $node;
 187                      }
 188                      break;
 189   
 190                  default:
 191                      throw new SyntaxError('Lexer or parser ended up in unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext());
 192              }
 193          }
 194   
 195          if (1 === \count($rv)) {
 196              return $rv[0];
 197          }
 198   
 199          return new Node($rv, [], $lineno);
 200      }
 201   
 202      public function getBlockStack()
 203      {
 204          return $this->blockStack;
 205      }
 206   
 207      public function peekBlockStack()
 208      {
 209          return isset($this->blockStack[\count($this->blockStack) - 1]) ? $this->blockStack[\count($this->blockStack) - 1] : null;
 210      }
 211   
 212      public function popBlockStack()
 213      {
 214          array_pop($this->blockStack);
 215      }
 216   
 217      public function pushBlockStack($name)
 218      {
 219          $this->blockStack[] = $name;
 220      }
 221   
 222      public function hasBlock($name)
 223      {
 224          return isset($this->blocks[$name]);
 225      }
 226   
 227      public function getBlock($name)
 228      {
 229          return $this->blocks[$name];
 230      }
 231   
 232      public function setBlock($name, BlockNode $value)
 233      {
 234          $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine());
 235      }
 236   
 237      public function hasMacro($name)
 238      {
 239          return isset($this->macros[$name]);
 240      }
 241   
 242      public function setMacro($name, MacroNode $node)
 243      {
 244          $this->macros[$name] = $node;
 245      }
 246   
 247      /**
 248       * @deprecated since Twig 2.7 as there are no reserved macro names anymore, will be removed in 3.0.
 249       */
 250      public function isReservedMacroName($name)
 251      {
 252          @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.7 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED);
 253   
 254          return false;
 255      }
 256   
 257      public function addTrait($trait)
 258      {
 259          $this->traits[] = $trait;
 260      }
 261   
 262      public function hasTraits()
 263      {
 264          return \count($this->traits) > 0;
 265      }
 266   
 267      public function embedTemplate(ModuleNode $template)
 268      {
 269          $template->setIndex(mt_rand());
 270   
 271          $this->embeddedTemplates[] = $template;
 272      }
 273   
 274      public function addImportedSymbol($type, $alias, $name = null, AbstractExpression $node = null)
 275      {
 276          $this->importedSymbols[0][$type][$alias] = ['name' => $name, 'node' => $node];
 277      }
 278   
 279      public function getImportedSymbol($type, $alias)
 280      {
 281          // if the symbol does not exist in the current scope (0), try in the main/global scope (last index)
 282          return $this->importedSymbols[0][$type][$alias] ?? ($this->importedSymbols[\count($this->importedSymbols) - 1][$type][$alias] ?? null);
 283      }
 284   
 285      public function isMainScope()
 286      {
 287          return 1 === \count($this->importedSymbols);
 288      }
 289   
 290      public function pushLocalScope()
 291      {
 292          array_unshift($this->importedSymbols, []);
 293      }
 294   
 295      public function popLocalScope()
 296      {
 297          array_shift($this->importedSymbols);
 298      }
 299   
 300      /**
 301       * @return ExpressionParser
 302       */
 303      public function getExpressionParser()
 304      {
 305          return $this->expressionParser;
 306      }
 307   
 308      public function getParent()
 309      {
 310          return $this->parent;
 311      }
 312   
 313      public function setParent($parent)
 314      {
 315          $this->parent = $parent;
 316      }
 317   
 318      /**
 319       * @return TokenStream
 320       */
 321      public function getStream()
 322      {
 323          return $this->stream;
 324      }
 325   
 326      /**
 327       * @return Token
 328       */
 329      public function getCurrentToken()
 330      {
 331          return $this->stream->getCurrent();
 332      }
 333   
 334      private function filterBodyNodes(Node $node, bool $nested = false)
 335      {
 336          // check that the body does not contain non-empty output nodes
 337          if (
 338              ($node instanceof TextNode && !ctype_space($node->getAttribute('data')))
 339              ||
 340              // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
 341              (!$node instanceof TextNode && !$node instanceof BlockReferenceNode && ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode))
 342          ) {
 343              if (false !== strpos((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) {
 344                  $t = substr($node->getAttribute('data'), 3);
 345                  if ('' === $t || ctype_space($t)) {
 346                      // bypass empty nodes starting with a BOM
 347                      return;
 348                  }
 349              }
 350   
 351              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());
 352          }
 353   
 354          // bypass nodes that "capture" the output
 355          if ($node instanceof NodeCaptureInterface) {
 356              // a "block" tag in such a node will serve as a block definition AND be displayed in place as well
 357              return $node;
 358          }
 359   
 360          // to be removed completely in Twig 3.0
 361          if (!$nested && $node instanceof SpacelessNode) {
 362              @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);
 363          }
 364   
 365          // "block" tags that are not captured (see above) are only used for defining
 366          // the content of the block. In such a case, nesting it does not work as
 367          // expected as the definition is not part of the default template code flow.
 368          if ($nested && ($node instanceof BlockReferenceNode || $node instanceof \Twig_Node_BlockReference)) {
 369              //throw new SyntaxError('A block definition cannot be nested under non-capturing nodes.', $node->getTemplateLine(), $this->stream->getSourceContext());
 370              @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);
 371   
 372              return;
 373          }
 374   
 375          // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
 376          if ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode) {
 377              return;
 378          }
 379   
 380          // here, $nested means "being at the root level of a child template"
 381          // we need to discard the wrapping "Twig_Node" for the "body" node
 382          $nested = $nested || ('Twig_Node' !== \get_class($node) && Node::class !== \get_class($node));
 383          foreach ($node as $k => $n) {
 384              if (null !== $n && null === $this->filterBodyNodes($n, $nested)) {
 385                  $node->removeNode($k);
 386              }
 387          }
 388   
 389          return $node;
 390      }
 391  }
 392   
 393  class_alias('Twig\Parser', 'Twig_Parser');
 394