Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   1  <?php
   2  
   3  /*
   4   * This file is part of Mustache.php.
   5   *
   6   * (c) 2010-2017 Justin Hileman
   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  /**
  13   * Mustache Compiler class.
  14   *
  15   * This class is responsible for turning a Mustache token parse tree into normal PHP source code.
  16   */
  17  class Mustache_Compiler
  18  {
  19      private $pragmas;
  20      private $defaultPragmas = array();
  21      private $sections;
  22      private $blocks;
  23      private $source;
  24      private $indentNextLine;
  25      private $customEscape;
  26      private $entityFlags;
  27      private $charset;
  28      private $strictCallables;
  29  
  30      /**
  31       * Compile a Mustache token parse tree into PHP source code.
  32       *
  33       * @param string $source          Mustache Template source code
  34       * @param string $tree            Parse tree of Mustache tokens
  35       * @param string $name            Mustache Template class name
  36       * @param bool   $customEscape    (default: false)
  37       * @param string $charset         (default: 'UTF-8')
  38       * @param bool   $strictCallables (default: false)
  39       * @param int    $entityFlags     (default: ENT_COMPAT)
  40       *
  41       * @return string Generated PHP source code
  42       */
  43      public function compile($source, array $tree, $name, $customEscape = false, $charset = 'UTF-8', $strictCallables = false, $entityFlags = ENT_COMPAT)
  44      {
  45          $this->pragmas         = $this->defaultPragmas;
  46          $this->sections        = array();
  47          $this->blocks          = array();
  48          $this->source          = $source;
  49          $this->indentNextLine  = true;
  50          $this->customEscape    = $customEscape;
  51          $this->entityFlags     = $entityFlags;
  52          $this->charset         = $charset;
  53          $this->strictCallables = $strictCallables;
  54  
  55          return $this->writeCode($tree, $name);
  56      }
  57  
  58      /**
  59       * Enable pragmas across all templates, regardless of the presence of pragma
  60       * tags in the individual templates.
  61       *
  62       * @internal Users should set global pragmas in Mustache_Engine, not here :)
  63       *
  64       * @param string[] $pragmas
  65       */
  66      public function setPragmas(array $pragmas)
  67      {
  68          $this->pragmas = array();
  69          foreach ($pragmas as $pragma) {
  70              $this->pragmas[$pragma] = true;
  71          }
  72          $this->defaultPragmas = $this->pragmas;
  73      }
  74  
  75      /**
  76       * Helper function for walking the Mustache token parse tree.
  77       *
  78       * @throws Mustache_Exception_SyntaxException upon encountering unknown token types
  79       *
  80       * @param array $tree  Parse tree of Mustache tokens
  81       * @param int   $level (default: 0)
  82       *
  83       * @return string Generated PHP source code
  84       */
  85      private function walk(array $tree, $level = 0)
  86      {
  87          $code = '';
  88          $level++;
  89          foreach ($tree as $node) {
  90              switch ($node[Mustache_Tokenizer::TYPE]) {
  91                  case Mustache_Tokenizer::T_PRAGMA:
  92                      $this->pragmas[$node[Mustache_Tokenizer::NAME]] = true;
  93                      break;
  94  
  95                  case Mustache_Tokenizer::T_SECTION:
  96                      $code .= $this->section(
  97                          $node[Mustache_Tokenizer::NODES],
  98                          $node[Mustache_Tokenizer::NAME],
  99                          isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
 100                          $node[Mustache_Tokenizer::INDEX],
 101                          $node[Mustache_Tokenizer::END],
 102                          $node[Mustache_Tokenizer::OTAG],
 103                          $node[Mustache_Tokenizer::CTAG],
 104                          $level
 105                      );
 106                      break;
 107  
 108                  case Mustache_Tokenizer::T_INVERTED:
 109                      $code .= $this->invertedSection(
 110                          $node[Mustache_Tokenizer::NODES],
 111                          $node[Mustache_Tokenizer::NAME],
 112                          isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
 113                          $level
 114                      );
 115                      break;
 116  
 117                  case Mustache_Tokenizer::T_PARTIAL:
 118                      $code .= $this->partial(
 119                          $node[Mustache_Tokenizer::NAME],
 120                          isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
 121                          $level
 122                      );
 123                      break;
 124  
 125                  case Mustache_Tokenizer::T_PARENT:
 126                      $code .= $this->parent(
 127                          $node[Mustache_Tokenizer::NAME],
 128                          isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
 129                          $node[Mustache_Tokenizer::NODES],
 130                          $level
 131                      );
 132                      break;
 133  
 134                  case Mustache_Tokenizer::T_BLOCK_ARG:
 135                      $code .= $this->blockArg(
 136                          $node[Mustache_Tokenizer::NODES],
 137                          $node[Mustache_Tokenizer::NAME],
 138                          $node[Mustache_Tokenizer::INDEX],
 139                          $node[Mustache_Tokenizer::END],
 140                          $node[Mustache_Tokenizer::OTAG],
 141                          $node[Mustache_Tokenizer::CTAG],
 142                          $level
 143                      );
 144                      break;
 145  
 146                  case Mustache_Tokenizer::T_BLOCK_VAR:
 147                      $code .= $this->blockVar(
 148                          $node[Mustache_Tokenizer::NODES],
 149                          $node[Mustache_Tokenizer::NAME],
 150                          $node[Mustache_Tokenizer::INDEX],
 151                          $node[Mustache_Tokenizer::END],
 152                          $node[Mustache_Tokenizer::OTAG],
 153                          $node[Mustache_Tokenizer::CTAG],
 154                          $level
 155                      );
 156                      break;
 157  
 158                  case Mustache_Tokenizer::T_COMMENT:
 159                      break;
 160  
 161                  case Mustache_Tokenizer::T_ESCAPED:
 162                  case Mustache_Tokenizer::T_UNESCAPED:
 163                  case Mustache_Tokenizer::T_UNESCAPED_2:
 164                      $code .= $this->variable(
 165                          $node[Mustache_Tokenizer::NAME],
 166                          isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
 167                          $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_ESCAPED,
 168                          $level
 169                      );
 170                      break;
 171  
 172                  case Mustache_Tokenizer::T_TEXT:
 173                      $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level);
 174                      break;
 175  
 176                  default:
 177                      throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node);
 178              }
 179          }
 180  
 181          return $code;
 182      }
 183  
 184      const KLASS = '<?php
 185  
 186          class %s extends Mustache_Template
 187          {
 188              private $lambdaHelper;%s
 189  
 190              public function renderInternal(Mustache_Context $context, $indent = \'\')
 191              {
 192                  $this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context);
 193                  $buffer = \'\';
 194          %s
 195  
 196                  return $buffer;
 197              }
 198          %s
 199          %s
 200          }';
 201  
 202      const KLASS_NO_LAMBDAS = '<?php
 203  
 204          class %s extends Mustache_Template
 205          {%s
 206              public function renderInternal(Mustache_Context $context, $indent = \'\')
 207              {
 208                  $buffer = \'\';
 209          %s
 210  
 211                  return $buffer;
 212              }
 213          }';
 214  
 215      const STRICT_CALLABLE = 'protected $strictCallables = true;';
 216  
 217      /**
 218       * Generate Mustache Template class PHP source.
 219       *
 220       * @param array  $tree Parse tree of Mustache tokens
 221       * @param string $name Mustache Template class name
 222       *
 223       * @return string Generated PHP source code
 224       */
 225      private function writeCode($tree, $name)
 226      {
 227          $code     = $this->walk($tree);
 228          $sections = implode("\n", $this->sections);
 229          $blocks   = implode("\n", $this->blocks);
 230          $klass    = empty($this->sections) && empty($this->blocks) ? self::KLASS_NO_LAMBDAS : self::KLASS;
 231  
 232          $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : '';
 233  
 234          return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections, $blocks);
 235      }
 236  
 237      const BLOCK_VAR = '
 238          $blockFunction = $context->findInBlock(%s);
 239          if (is_callable($blockFunction)) {
 240              $buffer .= call_user_func($blockFunction, $context);
 241          %s}
 242      ';
 243  
 244      const BLOCK_VAR_ELSE = '} else {%s';
 245  
 246      /**
 247       * Generate Mustache Template inheritance block variable PHP source.
 248       *
 249       * @param array  $nodes Array of child tokens
 250       * @param string $id    Section name
 251       * @param int    $start Section start offset
 252       * @param int    $end   Section end offset
 253       * @param string $otag  Current Mustache opening tag
 254       * @param string $ctag  Current Mustache closing tag
 255       * @param int    $level
 256       *
 257       * @return string Generated PHP source code
 258       */
 259      private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level)
 260      {
 261          $id = var_export($id, true);
 262  
 263          $else = $this->walk($nodes, $level);
 264          if ($else !== '') {
 265              $else = sprintf($this->prepare(self::BLOCK_VAR_ELSE, $level + 1, false, true), $else);
 266          }
 267  
 268          return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $else);
 269      }
 270  
 271      const BLOCK_ARG = '%s => array($this, \'block%s\'),';
 272  
 273      /**
 274       * Generate Mustache Template inheritance block argument PHP source.
 275       *
 276       * @param array  $nodes Array of child tokens
 277       * @param string $id    Section name
 278       * @param int    $start Section start offset
 279       * @param int    $end   Section end offset
 280       * @param string $otag  Current Mustache opening tag
 281       * @param string $ctag  Current Mustache closing tag
 282       * @param int    $level
 283       *
 284       * @return string Generated PHP source code
 285       */
 286      private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level)
 287      {
 288          $key = $this->block($nodes);
 289          $keystr = var_export($key, true);
 290          $id = var_export($id, true);
 291  
 292          return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key);
 293      }
 294  
 295      const BLOCK_FUNCTION = '
 296          public function block%s($context)
 297          {
 298              $indent = $buffer = \'\';%s
 299  
 300              return $buffer;
 301          }
 302      ';
 303  
 304      /**
 305       * Generate Mustache Template inheritance block function PHP source.
 306       *
 307       * @param array $nodes Array of child tokens
 308       *
 309       * @return string key of new block function
 310       */
 311      private function block($nodes)
 312      {
 313          $code = $this->walk($nodes, 0);
 314          $key = ucfirst(md5($code));
 315  
 316          if (!isset($this->blocks[$key])) {
 317              $this->blocks[$key] = sprintf($this->prepare(self::BLOCK_FUNCTION, 0), $key, $code);
 318          }
 319  
 320          return $key;
 321      }
 322  
 323      const SECTION_CALL = '
 324          // %s section
 325          $value = $context->%s(%s);%s
 326          $buffer .= $this->section%s($context, $indent, $value);
 327      ';
 328  
 329      const SECTION = '
 330          private function section%s(Mustache_Context $context, $indent, $value)
 331          {
 332              $buffer = \'\';
 333  
 334              if (%s) {
 335                  $source = %s;
 336                  $result = call_user_func($value, $source, %s);
 337                  if (strpos($result, \'{{\') === false) {
 338                      $buffer .= $result;
 339                  } else {
 340                      $buffer .= $this->mustache
 341                          ->loadLambda((string) $result%s)
 342                          ->renderInternal($context);
 343                  }
 344              } elseif (!empty($value)) {
 345                  $values = $this->isIterable($value) ? $value : array($value);
 346                  foreach ($values as $value) {
 347                      $context->push($value);
 348                      %s
 349                      $context->pop();
 350                  }
 351              }
 352  
 353              return $buffer;
 354          }
 355      ';
 356  
 357      /**
 358       * Generate Mustache Template section PHP source.
 359       *
 360       * @param array    $nodes   Array of child tokens
 361       * @param string   $id      Section name
 362       * @param string[] $filters Array of filters
 363       * @param int      $start   Section start offset
 364       * @param int      $end     Section end offset
 365       * @param string   $otag    Current Mustache opening tag
 366       * @param string   $ctag    Current Mustache closing tag
 367       * @param int      $level
 368       *
 369       * @return string Generated section PHP source code
 370       */
 371      private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $level)
 372      {
 373          $source   = var_export(substr($this->source, $start, $end - $start), true);
 374          $callable = $this->getCallable();
 375  
 376          if ($otag !== '{{' || $ctag !== '}}') {
 377              $delimTag = var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true);
 378              $helper = sprintf('$this->lambdaHelper->withDelimiters(%s)', $delimTag);
 379              $delims = ', ' . $delimTag;
 380          } else {
 381              $helper = '$this->lambdaHelper';
 382              $delims = '';
 383          }
 384  
 385          $key = ucfirst(md5($delims . "\n" . $source));
 386  
 387          if (!isset($this->sections[$key])) {
 388              $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $helper, $delims, $this->walk($nodes, 2));
 389          }
 390  
 391          $method  = $this->getFindMethod($id);
 392          $id      = var_export($id, true);
 393          $filters = $this->getFilters($filters, $level);
 394  
 395          return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key);
 396      }
 397  
 398      const INVERTED_SECTION = '
 399          // %s inverted section
 400          $value = $context->%s(%s);%s
 401          if (empty($value)) {
 402              %s
 403          }
 404      ';
 405  
 406      /**
 407       * Generate Mustache Template inverted section PHP source.
 408       *
 409       * @param array    $nodes   Array of child tokens
 410       * @param string   $id      Section name
 411       * @param string[] $filters Array of filters
 412       * @param int      $level
 413       *
 414       * @return string Generated inverted section PHP source code
 415       */
 416      private function invertedSection($nodes, $id, $filters, $level)
 417      {
 418          $method  = $this->getFindMethod($id);
 419          $id      = var_export($id, true);
 420          $filters = $this->getFilters($filters, $level);
 421  
 422          return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $filters, $this->walk($nodes, $level));
 423      }
 424  
 425      const PARTIAL_INDENT = ', $indent . %s';
 426      const PARTIAL = '
 427          if ($partial = $this->mustache->loadPartial(%s)) {
 428              $buffer .= $partial->renderInternal($context%s);
 429          }
 430      ';
 431  
 432      /**
 433       * Generate Mustache Template partial call PHP source.
 434       *
 435       * @param string $id     Partial name
 436       * @param string $indent Whitespace indent to apply to partial
 437       * @param int    $level
 438       *
 439       * @return string Generated partial call PHP source code
 440       */
 441      private function partial($id, $indent, $level)
 442      {
 443          if ($indent !== '') {
 444              $indentParam = sprintf(self::PARTIAL_INDENT, var_export($indent, true));
 445          } else {
 446              $indentParam = '';
 447          }
 448  
 449          return sprintf(
 450              $this->prepare(self::PARTIAL, $level),
 451              var_export($id, true),
 452              $indentParam
 453          );
 454      }
 455  
 456      const PARENT = '
 457          if ($parent = $this->mustache->loadPartial(%s)) {
 458              $context->pushBlockContext(array(%s
 459              ));
 460              $buffer .= $parent->renderInternal($context, $indent);
 461              $context->popBlockContext();
 462          }
 463      ';
 464  
 465      const PARENT_NO_CONTEXT = '
 466          if ($parent = $this->mustache->loadPartial(%s)) {
 467              $buffer .= $parent->renderInternal($context, $indent);
 468          }
 469      ';
 470  
 471      /**
 472       * Generate Mustache Template inheritance parent call PHP source.
 473       *
 474       * @param string $id       Parent tag name
 475       * @param string $indent   Whitespace indent to apply to parent
 476       * @param array  $children Child nodes
 477       * @param int    $level
 478       *
 479       * @return string Generated PHP source code
 480       */
 481      private function parent($id, $indent, array $children, $level)
 482      {
 483          $realChildren = array_filter($children, array(__CLASS__, 'onlyBlockArgs'));
 484  
 485          if (empty($realChildren)) {
 486              return sprintf($this->prepare(self::PARENT_NO_CONTEXT, $level), var_export($id, true));
 487          }
 488  
 489          return sprintf(
 490              $this->prepare(self::PARENT, $level),
 491              var_export($id, true),
 492              $this->walk($realChildren, $level + 1)
 493          );
 494      }
 495  
 496      /**
 497       * Helper method for filtering out non-block-arg tokens.
 498       *
 499       * @param array $node
 500       *
 501       * @return bool True if $node is a block arg token
 502       */
 503      private static function onlyBlockArgs(array $node)
 504      {
 505          return $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_BLOCK_ARG;
 506      }
 507  
 508      const VARIABLE = '
 509          $value = $this->resolveValue($context->%s(%s), $context);%s
 510          $buffer .= %s%s;
 511      ';
 512  
 513      /**
 514       * Generate Mustache Template variable interpolation PHP source.
 515       *
 516       * @param string   $id      Variable name
 517       * @param string[] $filters Array of filters
 518       * @param bool     $escape  Escape the variable value for output?
 519       * @param int      $level
 520       *
 521       * @return string Generated variable interpolation PHP source
 522       */
 523      private function variable($id, $filters, $escape, $level)
 524      {
 525          $method  = $this->getFindMethod($id);
 526          $id      = ($method !== 'last') ? var_export($id, true) : '';
 527          $filters = $this->getFilters($filters, $level);
 528          $value   = $escape ? $this->getEscape() : '$value';
 529  
 530          return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
 531      }
 532  
 533      const FILTER = '
 534          $filter = $context->%s(%s);
 535          if (!(%s)) {
 536              throw new Mustache_Exception_UnknownFilterException(%s);
 537          }
 538          $value = call_user_func($filter, $value);%s
 539      ';
 540  
 541      /**
 542       * Generate Mustache Template variable filtering PHP source.
 543       *
 544       * @param string[] $filters Array of filters
 545       * @param int      $level
 546       *
 547       * @return string Generated filter PHP source
 548       */
 549      private function getFilters(array $filters, $level)
 550      {
 551          if (empty($filters)) {
 552              return '';
 553          }
 554  
 555          $name     = array_shift($filters);
 556          $method   = $this->getFindMethod($name);
 557          $filter   = ($method !== 'last') ? var_export($name, true) : '';
 558          $callable = $this->getCallable('$filter');
 559          $msg      = var_export($name, true);
 560  
 561          return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilters($filters, $level));
 562      }
 563  
 564      const LINE = '$buffer .= "\n";';
 565      const TEXT = '$buffer .= %s%s;';
 566  
 567      /**
 568       * Generate Mustache Template output Buffer call PHP source.
 569       *
 570       * @param string $text
 571       * @param int    $level
 572       *
 573       * @return string Generated output Buffer call PHP source
 574       */
 575      private function text($text, $level)
 576      {
 577          $indentNextLine = (substr($text, -1) === "\n");
 578          $code = sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true));
 579          $this->indentNextLine = $indentNextLine;
 580  
 581          return $code;
 582      }
 583  
 584      /**
 585       * Prepare PHP source code snippet for output.
 586       *
 587       * @param string $text
 588       * @param int    $bonus          Additional indent level (default: 0)
 589       * @param bool   $prependNewline Prepend a newline to the snippet? (default: true)
 590       * @param bool   $appendNewline  Append a newline to the snippet? (default: false)
 591       *
 592       * @return string PHP source code snippet
 593       */
 594      private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false)
 595      {
 596          $text = ($prependNewline ? "\n" : '') . trim($text);
 597          if ($prependNewline) {
 598              $bonus++;
 599          }
 600          if ($appendNewline) {
 601              $text .= "\n";
 602          }
 603  
 604          return preg_replace("/\n( {8})?/", "\n" . str_repeat(' ', $bonus * 4), $text);
 605      }
 606  
 607      const DEFAULT_ESCAPE = 'htmlspecialchars(%s, %s, %s)';
 608      const CUSTOM_ESCAPE  = 'call_user_func($this->mustache->getEscape(), %s)';
 609  
 610      /**
 611       * Get the current escaper.
 612       *
 613       * @param string $value (default: '$value')
 614       *
 615       * @return string Either a custom callback, or an inline call to `htmlspecialchars`
 616       */
 617      private function getEscape($value = '$value')
 618      {
 619          if ($this->customEscape) {
 620              return sprintf(self::CUSTOM_ESCAPE, $value);
 621          }
 622  
 623          return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true));
 624      }
 625  
 626      /**
 627       * Select the appropriate Context `find` method for a given $id.
 628       *
 629       * The return value will be one of `find`, `findDot`, `findAnchoredDot` or `last`.
 630       *
 631       * @see Mustache_Context::find
 632       * @see Mustache_Context::findDot
 633       * @see Mustache_Context::last
 634       *
 635       * @param string $id Variable name
 636       *
 637       * @return string `find` method name
 638       */
 639      private function getFindMethod($id)
 640      {
 641          if ($id === '.') {
 642              return 'last';
 643          }
 644  
 645          if (isset($this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) && $this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) {
 646              if (substr($id, 0, 1) === '.') {
 647                  return 'findAnchoredDot';
 648              }
 649          }
 650  
 651          if (strpos($id, '.') === false) {
 652              return 'find';
 653          }
 654  
 655          return 'findDot';
 656      }
 657  
 658      const IS_CALLABLE        = '!is_string(%s) && is_callable(%s)';
 659      const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)';
 660  
 661      /**
 662       * Helper function to compile strict vs lax "is callable" logic.
 663       *
 664       * @param string $variable (default: '$value')
 665       *
 666       * @return string "is callable" logic
 667       */
 668      private function getCallable($variable = '$value')
 669      {
 670          $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE;
 671  
 672          return sprintf($tpl, $variable, $variable);
 673      }
 674  
 675      const LINE_INDENT = '$indent . ';
 676  
 677      /**
 678       * Get the current $indent prefix to write to the buffer.
 679       *
 680       * @return string "$indent . " or ""
 681       */
 682      private function flushIndent()
 683      {
 684          if (!$this->indentNextLine) {
 685              return '';
 686          }
 687  
 688          $this->indentNextLine = false;
 689  
 690          return self::LINE_INDENT;
 691      }
 692  }