Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

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