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. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Parser.php
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 }