Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

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

Lexer.php

Zuletzt modifiziert: 09.10.2024, 12:57 - Dateigröße: 15.90 KiB


001  <?php
002   
003  /*
004   * This file is part of Twig.
005   *
006   * (c) 2009 Fabien Potencier
007   * (c) 2009 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  /**
014   * Lexes a template string.
015   *
016   * @author Fabien Potencier <fabien@symfony.com>
017   */
018  class Twig_Lexer implements Twig_LexerInterface
019  {
020      protected $tokens;
021      protected $code;
022      protected $cursor;
023      protected $lineno;
024      protected $end;
025      protected $state;
026      protected $states;
027      protected $brackets;
028      protected $env;
029      protected $filename;
030      protected $options;
031      protected $regexes;
032      protected $position;
033      protected $positions;
034      protected $currentVarBlockLine;
035   
036      const STATE_DATA = 0;
037      const STATE_BLOCK = 1;
038      const STATE_VAR = 2;
039      const STATE_STRING = 3;
040      const STATE_INTERPOLATION = 4;
041   
042      const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
043      const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
044      const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
045      const REGEX_DQ_STRING_DELIM = '/"/A';
046      const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
047      const PUNCTUATION = '()[]{}?:.,|';
048   
049      public function __construct(Twig_Environment $env, array $options = array())
050      {
051          $this->env = $env;
052   
053          $this->options = array_merge(array(
054              'tag_comment' => array('{#', '#}'),
055              'tag_block' => array('{%', '%}'),
056              'tag_variable' => array('{{', '}}'),
057              'whitespace_trim' => '-',
058              'interpolation' => array('#{', '}'),
059          ), $options);
060   
061          $this->regexes = array(
062              'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
063              'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
064              'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
065              'operator' => $this->getOperatorRegex(),
066              'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
067              'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
068              'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
069              'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
070              'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
071              'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
072          );
073      }
074   
075      /**
076       * {@inheritdoc}
077       */
078      public function tokenize($code, $filename = null)
079      {
080          if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
081              $mbEncoding = mb_internal_encoding();
082              mb_internal_encoding('ASCII');
083          } else {
084              $mbEncoding = null;
085          }
086   
087          $this->code = str_replace(array("\r\n", "\r"), "\n", $code);
088          $this->filename = $filename;
089          $this->cursor = 0;
090          $this->lineno = 1;
091          $this->end = strlen($this->code);
092          $this->tokens = array();
093          $this->state = self::STATE_DATA;
094          $this->states = array();
095          $this->brackets = array();
096          $this->position = -1;
097   
098          // find all token starts in one go
099          preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
100          $this->positions = $matches;
101   
102          while ($this->cursor < $this->end) {
103              // dispatch to the lexing functions depending
104              // on the current state
105              switch ($this->state) {
106                  case self::STATE_DATA:
107                      $this->lexData();
108                      break;
109   
110                  case self::STATE_BLOCK:
111                      $this->lexBlock();
112                      break;
113   
114                  case self::STATE_VAR:
115                      $this->lexVar();
116                      break;
117   
118                  case self::STATE_STRING:
119                      $this->lexString();
120                      break;
121   
122                  case self::STATE_INTERPOLATION:
123                      $this->lexInterpolation();
124                      break;
125              }
126          }
127   
128          $this->pushToken(Twig_Token::EOF_TYPE);
129   
130          if (!empty($this->brackets)) {
131              list($expect, $lineno) = array_pop($this->brackets);
132              throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->filename);
133          }
134   
135          if ($mbEncoding) {
136              mb_internal_encoding($mbEncoding);
137          }
138   
139          return new Twig_TokenStream($this->tokens, $this->filename);
140      }
141   
142      protected function lexData()
143      {
144          // if no matches are left we return the rest of the template as simple text token
145          if ($this->position == count($this->positions[0]) - 1) {
146              $this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor));
147              $this->cursor = $this->end;
148   
149              return;
150          }
151   
152          // Find the first token after the current cursor
153          $position = $this->positions[0][++$this->position];
154          while ($position[1] < $this->cursor) {
155              if ($this->position == count($this->positions[0]) - 1) {
156                  return;
157              }
158              $position = $this->positions[0][++$this->position];
159          }
160   
161          // push the template text first
162          $text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
163          if (isset($this->positions[2][$this->position][0])) {
164              $text = rtrim($text);
165          }
166          $this->pushToken(Twig_Token::TEXT_TYPE, $text);
167          $this->moveCursor($textContent.$position[0]);
168   
169          switch ($this->positions[1][$this->position][0]) {
170              case $this->options['tag_comment'][0]:
171                  $this->lexComment();
172                  break;
173   
174              case $this->options['tag_block'][0]:
175                  // raw data?
176                  if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
177                      $this->moveCursor($match[0]);
178                      $this->lexRawData($match[1]);
179                  // {% line \d+ %}
180                  } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
181                      $this->moveCursor($match[0]);
182                      $this->lineno = (int) $match[1];
183                  } else {
184                      $this->pushToken(Twig_Token::BLOCK_START_TYPE);
185                      $this->pushState(self::STATE_BLOCK);
186                      $this->currentVarBlockLine = $this->lineno;
187                  }
188                  break;
189   
190              case $this->options['tag_variable'][0]:
191                  $this->pushToken(Twig_Token::VAR_START_TYPE);
192                  $this->pushState(self::STATE_VAR);
193                  $this->currentVarBlockLine = $this->lineno;
194                  break;
195          }
196      }
197   
198      protected function lexBlock()
199      {
200          if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) {
201              $this->pushToken(Twig_Token::BLOCK_END_TYPE);
202              $this->moveCursor($match[0]);
203              $this->popState();
204          } else {
205              $this->lexExpression();
206          }
207      }
208   
209      protected function lexVar()
210      {
211          if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) {
212              $this->pushToken(Twig_Token::VAR_END_TYPE);
213              $this->moveCursor($match[0]);
214              $this->popState();
215          } else {
216              $this->lexExpression();
217          }
218      }
219   
220      protected function lexExpression()
221      {
222          // whitespace
223          if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) {
224              $this->moveCursor($match[0]);
225   
226              if ($this->cursor >= $this->end) {
227                  throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->filename);
228              }
229          }
230   
231          // operators
232          if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
233              $this->pushToken(Twig_Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
234              $this->moveCursor($match[0]);
235          }
236          // names
237          elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) {
238              $this->pushToken(Twig_Token::NAME_TYPE, $match[0]);
239              $this->moveCursor($match[0]);
240          }
241          // numbers
242          elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
243              $number = (float) $match[0];  // floats
244              if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
245                  $number = (int) $match[0]; // integers lower than the maximum
246              }
247              $this->pushToken(Twig_Token::NUMBER_TYPE, $number);
248              $this->moveCursor($match[0]);
249          }
250          // punctuation
251          elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
252              // opening bracket
253              if (false !== strpos('([{', $this->code[$this->cursor])) {
254                  $this->brackets[] = array($this->code[$this->cursor], $this->lineno);
255              }
256              // closing bracket
257              elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
258                  if (empty($this->brackets)) {
259                      throw new Twig_Error_Syntax(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->filename);
260                  }
261   
262                  list($expect, $lineno) = array_pop($this->brackets);
263                  if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
264                      throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->filename);
265                  }
266              }
267   
268              $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
269              ++$this->cursor;
270          }
271          // strings
272          elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) {
273              $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
274              $this->moveCursor($match[0]);
275          }
276          // opening double quoted string
277          elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
278              $this->brackets[] = array('"', $this->lineno);
279              $this->pushState(self::STATE_STRING);
280              $this->moveCursor($match[0]);
281          }
282          // unlexable
283          else {
284              throw new Twig_Error_Syntax(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->filename);
285          }
286      }
287   
288      protected function lexRawData($tag)
289      {
290          if ('raw' === $tag) {
291              @trigger_error(sprintf('Twig Tag "raw" is deprecated since version 1.21. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
292          }
293   
294          if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
295              throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block.', $tag), $this->lineno, $this->filename);
296          }
297   
298          $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
299          $this->moveCursor($text.$match[0][0]);
300   
301          if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
302              $text = rtrim($text);
303          }
304   
305          $this->pushToken(Twig_Token::TEXT_TYPE, $text);
306      }
307   
308      protected function lexComment()
309      {
310          if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
311              throw new Twig_Error_Syntax('Unclosed comment.', $this->lineno, $this->filename);
312          }
313   
314          $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
315      }
316   
317      protected function lexString()
318      {
319          if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) {
320              $this->brackets[] = array($this->options['interpolation'][0], $this->lineno);
321              $this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
322              $this->moveCursor($match[0]);
323              $this->pushState(self::STATE_INTERPOLATION);
324          } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
325              $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
326              $this->moveCursor($match[0]);
327          } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
328              list($expect, $lineno) = array_pop($this->brackets);
329              if ($this->code[$this->cursor] != '"') {
330                  throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->filename);
331              }
332   
333              $this->popState();
334              ++$this->cursor;
335          }
336      }
337   
338      protected function lexInterpolation()
339      {
340          $bracket = end($this->brackets);
341          if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) {
342              array_pop($this->brackets);
343              $this->pushToken(Twig_Token::INTERPOLATION_END_TYPE);
344              $this->moveCursor($match[0]);
345              $this->popState();
346          } else {
347              $this->lexExpression();
348          }
349      }
350   
351      protected function pushToken($type, $value = '')
352      {
353          // do not push empty text tokens
354          if (Twig_Token::TEXT_TYPE === $type && '' === $value) {
355              return;
356          }
357   
358          $this->tokens[] = new Twig_Token($type, $value, $this->lineno);
359      }
360   
361      protected function moveCursor($text)
362      {
363          $this->cursor += strlen($text);
364          $this->lineno += substr_count($text, "\n");
365      }
366   
367      protected function getOperatorRegex()
368      {
369          $operators = array_merge(
370              array('='),
371              array_keys($this->env->getUnaryOperators()),
372              array_keys($this->env->getBinaryOperators())
373          );
374   
375          $operators = array_combine($operators, array_map('strlen', $operators));
376          arsort($operators);
377   
378          $regex = array();
379          foreach ($operators as $operator => $length) {
380              // an operator that ends with a character must be followed by
381              // a whitespace or a parenthesis
382              if (ctype_alpha($operator[$length - 1])) {
383                  $r = preg_quote($operator, '/').'(?=[\s()])';
384              } else {
385                  $r = preg_quote($operator, '/');
386              }
387   
388              // an operator with a space can be any amount of whitespaces
389              $r = preg_replace('/\s+/', '\s+', $r);
390   
391              $regex[] = $r;
392          }
393   
394          return '/'.implode('|', $regex).'/A';
395      }
396   
397      protected function pushState($state)
398      {
399          $this->states[] = $this->state;
400          $this->state = $state;
401      }
402   
403      protected function popState()
404      {
405          if (0 === count($this->states)) {
406              throw new Exception('Cannot pop state without a previous state');
407          }
408   
409          $this->state = array_pop($this->states);
410      }
411  }
412