Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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

Parser.php

Zuletzt modifiziert: 02.04.2025, 15:04 - Dateigröße: 9.13 KiB


001  <?php
002   
003  /**
004  * @package   s9e\TextFormatter
005  * @copyright Copyright (c) 2010-2022 The s9e authors
006  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
007  */
008  namespace s9e\TextFormatter\Plugins\BBCodes;
009   
010  use RuntimeException;
011  use s9e\TextFormatter\Parser\Tag;
012  use s9e\TextFormatter\Plugins\ParserBase;
013   
014  class Parser extends ParserBase
015  {
016      /**
017      * @var array Attributes of the BBCode being parsed
018      */
019      protected $attributes;
020   
021      /**
022      * @var array Configuration for the BBCode being parsed
023      */
024      protected $bbcodeConfig;
025   
026      /**
027      * @var string Name of the BBCode being parsed
028      */
029      protected $bbcodeName;
030   
031      /**
032      * @var string Suffix of the BBCode being parsed, including its colon
033      */
034      protected $bbcodeSuffix;
035   
036      /**
037      * @var integer Position of the cursor in the original text
038      */
039      protected $pos;
040   
041      /**
042      * @var integer Position of the start of the BBCode being parsed
043      */
044      protected $startPos;
045   
046      /**
047      * @var string Text being parsed
048      */
049      protected $text;
050   
051      /**
052      * @var integer Length of the text being parsed
053      */
054      protected $textLen;
055   
056      /**
057      * @var string Text being parsed, normalized to uppercase
058      */
059      protected $uppercaseText;
060   
061      /**
062      * {@inheritdoc}
063      */
064      public function parse($text, array $matches)
065      {
066          $this->text          = $text;
067          $this->textLen       = strlen($text);
068          $this->uppercaseText = '';
069          foreach ($matches as $m)
070          {
071              $this->bbcodeName = strtoupper($m[1][0]);
072              if (!isset($this->config['bbcodes'][$this->bbcodeName]))
073              {
074                  continue;
075              }
076              $this->bbcodeConfig = $this->config['bbcodes'][$this->bbcodeName];
077              $this->startPos     = $m[0][1];
078              $this->pos          = $this->startPos + strlen($m[0][0]);
079   
080              try
081              {
082                  $this->parseBBCode();
083              }
084              catch (RuntimeException $e)
085              {
086                  // Do nothing
087              }
088          }
089      }
090   
091      /**
092      * Add the end tag that matches current BBCode
093      *
094      * @return Tag
095      */
096      protected function addBBCodeEndTag()
097      {
098          return $this->parser->addEndTag($this->getTagName(), $this->startPos, $this->pos - $this->startPos);
099      }
100   
101      /**
102      * Add the self-closing tag that matches current BBCode
103      *
104      * @return Tag
105      */
106      protected function addBBCodeSelfClosingTag()
107      {
108          $tag = $this->parser->addSelfClosingTag($this->getTagName(), $this->startPos, $this->pos - $this->startPos);
109          $tag->setAttributes($this->attributes);
110   
111          return $tag;
112      }
113   
114      /**
115      * Add the start tag that matches current BBCode
116      *
117      * @return Tag
118      */
119      protected function addBBCodeStartTag()
120      {
121          $prio = ($this->bbcodeSuffix !== '') ? -10 : 0;
122          $tag  = $this->parser->addStartTag($this->getTagName(), $this->startPos, $this->pos - $this->startPos, $prio);
123          $tag->setAttributes($this->attributes);
124   
125          return $tag;
126      }
127   
128      /**
129      * Parse the end tag that matches given BBCode name and suffix starting at current position
130      *
131      * @return Tag|null
132      */
133      protected function captureEndTag()
134      {
135          if (empty($this->uppercaseText))
136          {
137              $this->uppercaseText = strtoupper($this->text);
138          }
139          $match     = '[/' . $this->bbcodeName . $this->bbcodeSuffix . ']';
140          $endTagPos = strpos($this->uppercaseText, $match, $this->pos);
141          if ($endTagPos === false)
142          {
143              return;
144          }
145   
146          return $this->parser->addEndTag($this->getTagName(), $endTagPos, strlen($match));
147      }
148   
149      /**
150      * Get the tag name for current BBCode
151      *
152      * @return string
153      */
154      protected function getTagName()
155      {
156          // Use the configured tagName if available, or reuse the BBCode's name otherwise
157          return (isset($this->bbcodeConfig['tagName']))
158               ? $this->bbcodeConfig['tagName']
159               : $this->bbcodeName;
160      }
161   
162      /**
163      * Parse attributes starting at current position
164      *
165      * @return void
166      */
167      protected function parseAttributes()
168      {
169          $firstPos = $this->pos;
170          $this->attributes = [];
171          while ($this->pos < $this->textLen)
172          {
173              $c = $this->text[$this->pos];
174              if (strpos(" \n\t", $c) !== false)
175              {
176                  ++$this->pos;
177                  continue;
178              }
179              if (strpos('/]', $c) !== false)
180              {
181                  return;
182              }
183   
184              // Capture the attribute name
185              $spn = strspn($this->text, 'abcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-', $this->pos);
186              if ($spn)
187              {
188                  $attrName = strtolower(substr($this->text, $this->pos, $spn));
189                  $this->pos += $spn;
190                  if ($this->pos >= $this->textLen)
191                  {
192                      // The attribute name extends to the end of the text
193                      throw new RuntimeException;
194                  }
195                  if ($this->text[$this->pos] !== '=')
196                  {
197                      // It's an attribute name not followed by an equal sign, ignore it
198                      continue;
199                  }
200              }
201              elseif ($c === '=' && $this->pos === $firstPos)
202              {
203                  // This is the default param, e.g. [quote=foo]
204                  $attrName = (isset($this->bbcodeConfig['defaultAttribute']))
205                            ? $this->bbcodeConfig['defaultAttribute']
206                            : strtolower($this->bbcodeName);
207              }
208              else
209              {
210                  throw new RuntimeException;
211              }
212   
213              // Move past the = and make sure we're not at the end of the text
214              if (++$this->pos >= $this->textLen)
215              {
216                  throw new RuntimeException;
217              }
218   
219              $this->attributes[$attrName] = $this->parseAttributeValue();
220          }
221      }
222   
223      /**
224      * Parse the attribute value starting at current position
225      *
226      * @return string
227      */
228      protected function parseAttributeValue()
229      {
230          // Test whether the value is in quotes
231          if ($this->text[$this->pos] === '"' || $this->text[$this->pos] === "'")
232          {
233              return $this->parseQuotedAttributeValue();
234          }
235   
236          // Capture everything up to whichever comes first:
237          //  - an endline
238          //  - whitespace followed by a slash and a closing bracket
239          //  - a closing bracket, optionally preceded by whitespace
240          //  - whitespace followed by another attribute (name followed by equal sign)
241          //
242          // NOTE: this is for compatibility with some forums (such as vBulletin it seems)
243          //       that do not put attribute values in quotes, e.g.
244          //       [quote=John Smith;123456] (quoting "John Smith" from post #123456)
245          preg_match('((?:[^\\s\\]]|[ \\t](?!\\s*(?:[-\\w]+=|/?\\])))*)', $this->text, $m, 0, $this->pos);
246   
247          $attrValue  = $m[0];
248          $this->pos += strlen($attrValue);
249   
250          return $attrValue;
251      }
252   
253      /**
254      * Parse current BBCode
255      *
256      * @return void
257      */
258      protected function parseBBCode()
259      {
260          $this->parseBBCodeSuffix();
261   
262          // Test whether this is an end tag
263          if ($this->text[$this->startPos + 1] === '/')
264          {
265              // Test whether the tag is properly closed and whether this tag has an identifier.
266              // We skip end tags that carry an identifier because they're automatically added
267              // when their start tag is processed
268              if (substr($this->text, $this->pos, 1) === ']' && $this->bbcodeSuffix === '')
269              {
270                  ++$this->pos;
271                  $this->addBBCodeEndTag();
272              }
273   
274              return;
275          }
276   
277          // Parse attributes
278          $this->parseAttributes();
279   
280          // Test whether the tag is properly closed
281          if (substr($this->text, $this->pos, 1) === ']')
282          {
283              ++$this->pos;
284          }
285          else
286          {
287              // Test whether this is a self-closing tag
288              if (substr($this->text, $this->pos, 2) === '/]')
289              {
290                  $this->pos += 2;
291                  $this->addBBCodeSelfClosingTag();
292              }
293   
294              return;
295          }
296   
297          // Record the names of attributes that need the content of this tag
298          $contentAttributes = [];
299          if (isset($this->bbcodeConfig['contentAttributes']))
300          {
301              foreach ($this->bbcodeConfig['contentAttributes'] as $attrName)
302              {
303                  if (!isset($this->attributes[$attrName]))
304                  {
305                      $contentAttributes[] = $attrName;
306                  }
307              }
308          }
309   
310          // Look ahead and parse the end tag that matches this tag, if applicable
311          $requireEndTag = ($this->bbcodeSuffix || !empty($this->bbcodeConfig['forceLookahead']));
312          $endTag = ($requireEndTag || !empty($contentAttributes)) ? $this->captureEndTag() : null;
313          if (isset($endTag))
314          {
315              foreach ($contentAttributes as $attrName)
316              {
317                  $this->attributes[$attrName] = substr($this->text, $this->pos, $endTag->getPos() - $this->pos);
318              }
319          }
320          elseif ($requireEndTag)
321          {
322              return;
323          }
324   
325          // Create this start tag
326          $tag = $this->addBBCodeStartTag();
327   
328          // If an end tag was created, pair it with this start tag
329          if (isset($endTag))
330          {
331              $tag->pairWith($endTag);
332          }
333      }
334   
335      /**
336      * Parse the BBCode suffix starting at current position
337      *
338      * Used to explicitly pair specific tags together, e.g.
339      *   [code:123][code]type your code here[/code][/code:123]
340      *
341      * @return void
342      */
343      protected function parseBBCodeSuffix()
344      {
345          $this->bbcodeSuffix = '';
346          if ($this->text[$this->pos] === ':')
347          {
348              // Capture the colon and the (0 or more) digits following it
349              $spn = 1 + strspn($this->text, '0123456789', 1 + $this->pos);
350              $this->bbcodeSuffix = substr($this->text, $this->pos, $spn);
351   
352              // Move past the suffix
353              $this->pos += $spn;
354          }
355      }
356   
357      /**
358      * Parse a quoted attribute value that starts at current offset
359      *
360      * @return string
361      */
362      protected function parseQuotedAttributeValue()
363      {
364          $quote    = $this->text[$this->pos];
365          $valuePos = $this->pos + 1;
366          do
367          {
368              // Look for the next quote
369              $this->pos = strpos($this->text, $quote, $this->pos + 1);
370              if ($this->pos === false)
371              {
372                  // No matching quote. Apparently that string never ends...
373                  throw new RuntimeException;
374              }
375   
376              // Test for an odd number of backslashes before this character
377              $n = 1;
378              while ($this->text[$this->pos - $n] === '\\')
379              {
380                  ++$n;
381              }
382          }
383          while ($n % 2 === 0);
384   
385          $attrValue = substr($this->text, $valuePos, $this->pos - $valuePos);
386          if (strpos($attrValue, '\\') !== false)
387          {
388              $attrValue = strtr($attrValue, ['\\\\' => '\\', '\\"' => '"', "\\'" => "'"]);
389          }
390   
391          // Skip past the closing quote
392          ++$this->pos;
393   
394          return $attrValue;
395      }
396  }