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

BBCodeMonkey.php

Zuletzt modifiziert: 09.10.2024, 12:59 - Dateigröße: 14.41 KiB


001  <?php
002   
003  /*
004  * @package   s9e\TextFormatter
005  * @copyright Copyright (c) 2010-2016 The s9e Authors
006  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
007  */
008  namespace s9e\TextFormatter\Plugins\BBCodes\Configurator;
009  use Exception;
010  use InvalidArgumentException;
011  use RuntimeException;
012  use s9e\TextFormatter\Configurator;
013  use s9e\TextFormatter\Configurator\Helpers\RegexpBuilder;
014  use s9e\TextFormatter\Configurator\Items\Attribute;
015  use s9e\TextFormatter\Configurator\Items\ProgrammableCallback;
016  use s9e\TextFormatter\Configurator\Items\Tag;
017  use s9e\TextFormatter\Configurator\Items\Template;
018  class BBCodeMonkey
019  {
020      const REGEXP = '(.).*?(?<!\\\\)(?>\\\\\\\\)*+\\g{-1}[DSUisu]*';
021      public $allowedFilters = array(
022          'addslashes',
023          'dechex',
024          'intval',
025          'json_encode',
026          'ltrim',
027          'mb_strtolower',
028          'mb_strtoupper',
029          'rawurlencode',
030          'rtrim',
031          'str_rot13',
032          'stripslashes',
033          'strrev',
034          'strtolower',
035          'strtotime',
036          'strtoupper',
037          'trim',
038          'ucfirst',
039          'ucwords',
040          'urlencode'
041      );
042      protected $configurator;
043      public $tokenRegexp = array(
044          'COLOR'      => '[a-zA-Z]+|#[0-9a-fA-F]+',
045          'EMAIL'      => '[^@]+@.+?',
046          'FLOAT'      => '(?>0|-?[1-9]\\d*)(?>\\.\\d+)?(?>e[1-9]\\d*)?',
047          'ID'         => '[-a-zA-Z0-9_]+',
048          'IDENTIFIER' => '[-a-zA-Z0-9_]+',
049          'INT'        => '0|-?[1-9]\\d*',
050          'INTEGER'    => '0|-?[1-9]\\d*',
051          'NUMBER'     => '\\d+',
052          'RANGE'      => '\\d+',
053          'SIMPLETEXT' => '[-a-zA-Z0-9+.,_ ]+',
054          'UINT'       => '0|[1-9]\\d*'
055      );
056      public $unfilteredTokens = array(
057          'ANYTHING',
058          'TEXT'
059      );
060      public function __construct(Configurator $configurator)
061      {
062          $this->configurator = $configurator;
063      }
064      public function create($usage, $template)
065      {
066          $_this = $this;
067          $config = $this->parse($usage);
068          if (!($template instanceof Template))
069              $template = new Template($template);
070          $template->replaceTokens(
071              '#\\{(?:[A-Z]+[A-Z_0-9]*|@[-\\w]+)\\}#',
072              function ($m) use ($config, $_this)
073              {
074                  $tokenId = \substr($m[0], 1, -1);
075                  if ($tokenId[0] === '@')
076                      return array('expression', $tokenId);
077                  if (isset($config['tokens'][$tokenId]))
078                      return array('expression', '@' . $config['tokens'][$tokenId]);
079                  if ($tokenId === $config['passthroughToken'])
080                      return array('passthrough');
081                  if ($_this->isFilter($tokenId))
082                      throw new RuntimeException('Token {' . $tokenId . '} is ambiguous or undefined');
083                  return array('expression', '$' . $tokenId);
084              }
085          );
086          $return = array(
087              'bbcode'     => $config['bbcode'],
088              'bbcodeName' => $config['bbcodeName'],
089              'tag'        => $config['tag']
090          );
091          $return['tag']->template = $template;
092          return $return;
093      }
094      protected function parse($usage)
095      {
096          $tag    = new Tag;
097          $bbcode = new BBCode;
098          $config = array(
099              'tag'              => $tag,
100              'bbcode'           => $bbcode,
101              'passthroughToken' => \null
102          );
103          $usage = \preg_replace_callback(
104              '#(\\{(?>HASH)?MAP=)([^:]+:[^,;}]+(?>,[^:]+:[^,;}]+)*)(?=[;}])#',
105              function ($m)
106              {
107                  return $m[1] . \base64_encode($m[2]);
108              },
109              $usage
110          );
111          $usage = \preg_replace_callback(
112              '#(\\{(?:PARSE|REGEXP)=)(' . self::REGEXP . '(?:,' . self::REGEXP . ')*)#',
113              function ($m)
114              {
115                  return $m[1] . \base64_encode($m[2]);
116              },
117              $usage
118          );
119          $regexp = '(^'
120                  . '\\[(?<bbcodeName>\\S+?)'
121                  . '(?<defaultAttribute>=\\S+?)?'
122                  . '(?<attributes>(?:\\s+[^=]+=\\S+?)*?)?'
123                  . '\\s*(?:/?\\]|\\]\\s*(?<content>.*?)\\s*(?<endTag>\\[/\\1]))$)i';
124          if (!\preg_match($regexp, \trim($usage), $m))
125              throw new InvalidArgumentException('Cannot interpret the BBCode definition');
126          $config['bbcodeName'] = BBCode::normalizeName($m['bbcodeName']);
127          $definitions = \preg_split('#\\s+#', \trim($m['attributes']), -1, \PREG_SPLIT_NO_EMPTY);
128          if (!empty($m['defaultAttribute']))
129              \array_unshift($definitions, $m['bbcodeName'] . $m['defaultAttribute']);
130          if (!empty($m['content']))
131          {
132              $regexp = '#^\\{' . RegexpBuilder::fromList($this->unfilteredTokens) . '[0-9]*\\}$#D';
133              if (\preg_match($regexp, $m['content']))
134                  $config['passthroughToken'] = \substr($m['content'], 1, -1);
135              else
136              {
137                  $definitions[] = 'content=' . $m['content'];
138                  $bbcode->contentAttributes[] = 'content';
139              }
140          }
141          $attributeDefinitions = array();
142          foreach ($definitions as $definition)
143          {
144              $pos   = \strpos($definition, '=');
145              $name  = \substr($definition, 0, $pos);
146              $value = \preg_replace('(^"(.*?)")s', '$1', \substr($definition, 1 + $pos));
147              $value = \preg_replace_callback(
148                  '#(\\{(?>HASHMAP|MAP|PARSE|REGEXP)=)([A-Za-z0-9+/]+=*)#',
149                  function ($m)
150                  {
151                      return $m[1] . \base64_decode($m[2]);
152                  },
153                  $value
154              );
155              if ($name[0] === '$')
156              {
157                  $optionName = \substr($name, 1);
158                  $bbcode->$optionName = $this->convertValue($value);
159              }
160              elseif ($name[0] === '#')
161              {
162                  $ruleName = \substr($name, 1);
163                  foreach (\explode(',', $value) as $value)
164                      $tag->rules->$ruleName($this->convertValue($value));
165              }
166              else
167              {
168                  $attrName = \strtolower(\trim($name));
169                  $attributeDefinitions[] = array($attrName, $value);
170              }
171          }
172          $tokens = $this->addAttributes($attributeDefinitions, $bbcode, $tag);
173          if (isset($tokens[$config['passthroughToken']]))
174              $config['passthroughToken'] = \null;
175          $config['tokens'] = \array_filter($tokens);
176          return $config;
177      }
178      protected function addAttributes(array $definitions, BBCode $bbcode, Tag $tag)
179      {
180          $composites = array();
181          $table = array();
182          foreach ($definitions as $_e874cdc7)
183          {
184              list($attrName, $definition) = $_e874cdc7;
185              if (!isset($bbcode->defaultAttribute))
186                  $bbcode->defaultAttribute = $attrName;
187              $tokens = self::parseTokens($definition);
188              if (empty($tokens))
189                  throw new RuntimeException('No valid tokens found in ' . $attrName . "'s definition " . $definition);
190              if ($tokens[0]['content'] === $definition)
191              {
192                  $token = $tokens[0];
193                  if ($token['type'] === 'PARSE')
194                      foreach ($token['regexps'] as $regexp)
195                          $tag->attributePreprocessors->add($attrName, $regexp);
196                  elseif (isset($tag->attributes[$attrName]))
197                      throw new RuntimeException("Attribute '" . $attrName . "' is declared twice");
198                  else
199                  {
200                      if (!empty($token['options']['useContent']))
201                          $bbcode->contentAttributes[] = $attrName;
202                      unset($token['options']['useContent']);
203                      $tag->attributes[$attrName] = $this->generateAttribute($token);
204                      $tokenId = $token['id'];
205                      $table[$tokenId] = (isset($table[$tokenId]))
206                                       ? \false
207                                       : $attrName;
208                  }
209              }
210              else
211                  $composites[] = array($attrName, $definition, $tokens);
212          }
213          foreach ($composites as $_2d84f0a0)
214          {
215              list($attrName, $definition, $tokens) = $_2d84f0a0;
216              $regexp  = '/^';
217              $lastPos = 0;
218              $usedTokens = array();
219              foreach ($tokens as $token)
220              {
221                  $tokenId   = $token['id'];
222                  $tokenType = $token['type'];
223                  if ($tokenType === 'PARSE')
224                      throw new RuntimeException('{PARSE} tokens can only be used has the sole content of an attribute');
225                  if (isset($usedTokens[$tokenId]))
226                      throw new RuntimeException('Token {' . $tokenId . '} used multiple times in attribute ' . $attrName . "'s definition");
227                  $usedTokens[$tokenId] = 1;
228                  if (isset($table[$tokenId]))
229                  {
230                      $matchName = $table[$tokenId];
231                      if ($matchName === \false)
232                          throw new RuntimeException('Token {' . $tokenId . "} used in attribute '" . $attrName . "' is ambiguous");
233                  }
234                  else
235                  {
236                      $i = 0;
237                      do
238                      {
239                          $matchName = $attrName . $i;
240                          ++$i;
241                      }
242                      while (isset($tag->attributes[$matchName]));
243                      $attribute = $tag->attributes->add($matchName);
244                      if (!\in_array($tokenType, $this->unfilteredTokens, \true))
245                      {
246                          $filter = $this->configurator->attributeFilters->get('#' . \strtolower($tokenType));
247                          $attribute->filterChain->append($filter);
248                      }
249                      $table[$tokenId] = $matchName;
250                  }
251                  $regexp .= \preg_quote(\substr($definition, $lastPos, $token['pos'] - $lastPos), '/');
252                  $expr = (isset($this->tokenRegexp[$tokenType]))
253                        ? $this->tokenRegexp[$tokenType]
254                        : '.+?';
255                  $regexp .= '(?<' . $matchName . '>' . $expr . ')';
256                  $lastPos = $token['pos'] + \strlen($token['content']);
257              }
258              $regexp .= \preg_quote(\substr($definition, $lastPos), '/') . '$/D';
259              $tag->attributePreprocessors->add($attrName, $regexp);
260          }
261          $newAttributes = array();
262          foreach ($tag->attributePreprocessors as $attributePreprocessor)
263              foreach ($attributePreprocessor->getAttributes() as $attrName => $regexp)
264              {
265                  if (isset($tag->attributes[$attrName]))
266                      continue;
267                  if (isset($newAttributes[$attrName])
268                   && $newAttributes[$attrName] !== $regexp)
269                      throw new RuntimeException("Ambiguous attribute '" . $attrName . "' created using different regexps needs to be explicitly defined");
270                  $newAttributes[$attrName] = $regexp;
271              }
272          foreach ($newAttributes as $attrName => $regexp)
273          {
274              $filter = $this->configurator->attributeFilters->get('#regexp');
275              $tag->attributes->add($attrName)->filterChain->append($filter)->setRegexp($regexp);
276          }
277          return $table;
278      }
279      protected function convertValue($value)
280      {
281          if ($value === 'true')
282              return \true;
283          if ($value === 'false')
284              return \false;
285          return $value;
286      }
287      protected static function parseTokens($definition)
288      {
289          $tokenTypes = array(
290              'choice' => 'CHOICE[0-9]*=(?<choices>.+?)',
291              'map'    => '(?:HASH)?MAP[0-9]*=(?<map>.+?)',
292              'parse'  => 'PARSE=(?<regexps>' . self::REGEXP . '(?:,' . self::REGEXP . ')*)',
293              'range'  => 'RAN(?:DOM|GE)[0-9]*=(?<min>-?[0-9]+),(?<max>-?[0-9]+)',
294              'regexp' => 'REGEXP[0-9]*=(?<regexp>' . self::REGEXP . ')',
295              'other'  => '(?<other>[A-Z_]+[0-9]*)'
296          );
297          \preg_match_all(
298              '#\\{(' . \implode('|', $tokenTypes) . ')(?<options>(?:;[^;]*)*)\\}#',
299              $definition,
300              $matches,
301              \PREG_SET_ORDER | \PREG_OFFSET_CAPTURE
302          );
303          $tokens = array();
304          foreach ($matches as $m)
305          {
306              if (isset($m['other'][0])
307               && \preg_match('#^(?:CHOICE|HASHMAP|MAP|REGEXP|PARSE|RANDOM|RANGE)#', $m['other'][0]))
308                  throw new RuntimeException("Malformed token '" . $m['other'][0] . "'");
309              $token = array(
310                  'pos'     => $m[0][1],
311                  'content' => $m[0][0],
312                  'options' => array()
313              );
314              $head = $m[1][0];
315              $pos  = \strpos($head, '=');
316              if ($pos === \false)
317                  $token['id'] = $head;
318              else
319              {
320                  $token['id'] = \substr($head, 0, $pos);
321                  foreach ($m as $k => $v)
322                      if (!\is_numeric($k) && $k !== 'options' && $v[1] !== -1)
323                          $token[$k] = $v[0];
324              }
325              $token['type'] = \rtrim($token['id'], '0123456789');
326              $options = (isset($m['options'][0])) ? $m['options'][0] : '';
327              foreach (\preg_split('#;+#', $options, -1, \PREG_SPLIT_NO_EMPTY) as $pair)
328              {
329                  $pos = \strpos($pair, '=');
330                  if ($pos === \false)
331                  {
332                      $k = $pair;
333                      $v = \true;
334                  }
335                  else
336                  {
337                      $k = \substr($pair, 0, $pos);
338                      $v = \substr($pair, 1 + $pos);
339                  }
340                  $token['options'][$k] = $v;
341              }
342              if ($token['type'] === 'PARSE')
343              {
344                  \preg_match_all('#' . self::REGEXP . '(?:,|$)#', $token['regexps'], $m);
345                  $regexps = array();
346                  foreach ($m[0] as $regexp)
347                      $regexps[] = \rtrim($regexp, ',');
348                  $token['regexps'] = $regexps;
349              }
350              $tokens[] = $token;
351          }
352          return $tokens;
353      }
354      protected function generateAttribute(array $token)
355      {
356          $attribute = new Attribute;
357          if (isset($token['options']['preFilter']))
358          {
359              $this->appendFilters($attribute, $token['options']['preFilter']);
360              unset($token['options']['preFilter']);
361          }
362          if ($token['type'] === 'REGEXP')
363          {
364              $filter = $this->configurator->attributeFilters->get('#regexp');
365              $attribute->filterChain->append($filter)->setRegexp($token['regexp']);
366          }
367          elseif ($token['type'] === 'RANGE')
368          {
369              $filter = $this->configurator->attributeFilters->get('#range');
370              $attribute->filterChain->append($filter)->setRange($token['min'], $token['max']);
371          }
372          elseif ($token['type'] === 'RANDOM')
373          {
374              $attribute->generator = new ProgrammableCallback('mt_rand');
375              $attribute->generator->addParameterByValue((int) $token['min']);
376              $attribute->generator->addParameterByValue((int) $token['max']);
377          }
378          elseif ($token['type'] === 'CHOICE')
379          {
380              $filter = $this->configurator->attributeFilters->get('#choice');
381              $attribute->filterChain->append($filter)->setValues(
382                  \explode(',', $token['choices']),
383                  !empty($token['options']['caseSensitive'])
384              );
385              unset($token['options']['caseSensitive']);
386          }
387          elseif ($token['type'] === 'HASHMAP' || $token['type'] === 'MAP')
388          {
389              $map = array();
390              foreach (\explode(',', $token['map']) as $pair)
391              {
392                  $pos = \strpos($pair, ':');
393                  if ($pos === \false)
394                      throw new RuntimeException("Invalid map assignment '" . $pair . "'");
395                  $map[\substr($pair, 0, $pos)] = \substr($pair, 1 + $pos);
396              }
397              if ($token['type'] === 'HASHMAP')
398              {
399                  $filter = $this->configurator->attributeFilters->get('#hashmap');
400                  $attribute->filterChain->append($filter)->setMap(
401                      $map,
402                      !empty($token['options']['strict'])
403                  );
404              }
405              else
406              {
407                  $filter = $this->configurator->attributeFilters->get('#map');
408                  $attribute->filterChain->append($filter)->setMap(
409                      $map,
410                      !empty($token['options']['caseSensitive']),
411                      !empty($token['options']['strict'])
412                  );
413              }
414              unset($token['options']['caseSensitive']);
415              unset($token['options']['strict']);
416          }
417          elseif (!\in_array($token['type'], $this->unfilteredTokens, \true))
418          {
419              $filter = $this->configurator->attributeFilters->get('#' . $token['type']);
420              $attribute->filterChain->append($filter);
421          }
422          if (isset($token['options']['postFilter']))
423          {
424              $this->appendFilters($attribute, $token['options']['postFilter']);
425              unset($token['options']['postFilter']);
426          }
427          if (isset($token['options']['required']))
428              $token['options']['required'] = (bool) $token['options']['required'];
429          elseif (isset($token['options']['optional']))
430              $token['options']['required'] = !$token['options']['optional'];
431          unset($token['options']['optional']);
432          foreach ($token['options'] as $k => $v)
433              $attribute->$k = $v;
434          return $attribute;
435      }
436      protected function appendFilters(Attribute $attribute, $filters)
437      {
438          foreach (\preg_split('#\\s*,\\s*#', $filters) as $filterName)
439          {
440              if (\substr($filterName, 0, 1) !== '#'
441               && !\in_array($filterName, $this->allowedFilters, \true))
442                  throw new RuntimeException("Filter '" . $filterName . "' is not allowed");
443              $filter = $this->configurator->attributeFilters->get($filterName);
444              $attribute->filterChain->append($filter);
445          }
446      }
447      public function isFilter($tokenId)
448      {
449          $filterName = \rtrim($tokenId, '0123456789');
450          if (\in_array($filterName, $this->unfilteredTokens, \true))
451              return \true;
452          try
453          {
454              if ($this->configurator->attributeFilters->get('#' . $filterName))
455                  return \true;
456          }
457          catch (Exception $e)
458          {
459              }
460          return \false;
461      }
462  }