[ Index ]

PHP Cross Reference of phpBB-3.3.11-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_parse_%d', $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'], $vars['varNameSalt']);
  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  
  96          try {
  97              $body = $this->subparse($test, $dropNeedle);
  98  
  99              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');


Generated: Sat Nov 4 14:26:03 2023 Cross-referenced by PHPXref 0.7.1