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\PipeTables;
009
010 use s9e\TextFormatter\Plugins\ParserBase;
011
012 class Parser extends ParserBase
013 {
014 /**
015 * @var integer Position of the cursor while parsing tables or adding tags
016 */
017 protected $pos;
018
019 /**
020 * @var array Current table being parsed
021 */
022 protected $table;
023
024 /**
025 * @var \s9e\TextFormatter\Parser\Tag
026 */
027 protected $tableTag;
028
029 /**
030 * @var array[] List of tables
031 */
032 protected $tables;
033
034 /**
035 * @var string Text being parsed
036 */
037 protected $text;
038
039 /**
040 * {@inheritdoc}
041 */
042 public function parse($text, array $matches)
043 {
044 $this->text = $text;
045 if ($this->config['overwriteMarkdown'])
046 {
047 $this->overwriteMarkdown();
048 }
049 if ($this->config['overwriteEscapes'])
050 {
051 $this->overwriteEscapes();
052 }
053
054 $this->captureTables();
055 $this->processTables();
056
057 unset($this->tables);
058 unset($this->text);
059 }
060
061 /**
062 * Add current line to a table
063 *
064 * @param string $line Line of text
065 * @return void
066 */
067 protected function addLine($line)
068 {
069 $ignoreLen = 0;
070
071 if (!isset($this->table))
072 {
073 $this->table = [];
074
075 // Make the table start at the first non-space character
076 preg_match('/^ */', $line, $m);
077 $ignoreLen = strlen($m[0]);
078 $line = substr($line, $ignoreLen);
079 }
080
081 // Overwrite the outermost pipes
082 $line = preg_replace('/^( *)\\|/', '$1 ', $line);
083 $line = preg_replace('/\\|( *)$/', ' $1', $line);
084
085 $this->table['rows'][] = ['line' => $line, 'pos' => $this->pos + $ignoreLen];
086 }
087
088 /**
089 * Process current table's body
090 *
091 * @return void
092 */
093 protected function addTableBody()
094 {
095 $i = 1;
096 $cnt = count($this->table['rows']);
097 while (++$i < $cnt)
098 {
099 $this->addTableRow('TD', $this->table['rows'][$i]);
100 }
101
102 $this->createBodyTags($this->table['rows'][2]['pos'], $this->pos);
103 }
104
105 /**
106 * Add a cell's tags for current table at current position
107 *
108 * @param string $tagName Either TD or TH
109 * @param string $align Either "left", "center", "right" or ""
110 * @param string $content Cell's text content
111 * @return void
112 */
113 protected function addTableCell($tagName, $align, $content)
114 {
115 $startPos = $this->pos;
116 $endPos = $startPos + strlen($content);
117 $this->pos = $endPos;
118
119 preg_match('/^( *).*?( *)$/', $content, $m);
120 if ($m[1])
121 {
122 $ignoreLen = strlen($m[1]);
123 $this->createIgnoreTag($startPos, $ignoreLen);
124 $startPos += $ignoreLen;
125 }
126 if ($m[2])
127 {
128 $ignoreLen = strlen($m[2]);
129 $this->createIgnoreTag($endPos - $ignoreLen, $ignoreLen);
130 $endPos -= $ignoreLen;
131 }
132
133 $this->createCellTags($tagName, $startPos, $endPos, $align);
134 }
135
136 /**
137 * Process current table's head
138 *
139 * @return void
140 */
141 protected function addTableHead()
142 {
143 $this->addTableRow('TH', $this->table['rows'][0]);
144 $this->createHeadTags($this->table['rows'][0]['pos'], $this->pos);
145 }
146
147 /**
148 * Process given table row
149 *
150 * @param string $tagName Either TD or TH
151 * @param array $row
152 * @return void
153 */
154 protected function addTableRow($tagName, $row)
155 {
156 $this->pos = $row['pos'];
157 foreach (explode('|', $row['line']) as $i => $str)
158 {
159 if ($i > 0)
160 {
161 $this->createIgnoreTag($this->pos, 1);
162 ++$this->pos;
163 }
164
165 $align = (empty($this->table['cols'][$i])) ? '' : $this->table['cols'][$i];
166 $this->addTableCell($tagName, $align, $str);
167 }
168
169 $this->createRowTags($row['pos'], $this->pos);
170 }
171
172 /**
173 * Capture all pipe tables in current text
174 *
175 * @return void
176 */
177 protected function captureTables()
178 {
179 unset($this->table);
180 $this->tables = [];
181
182 $this->pos = 0;
183 foreach (explode("\n", $this->text) as $line)
184 {
185 if (strpos($line, '|') === false)
186 {
187 $this->endTable();
188 }
189 else
190 {
191 $this->addLine($line);
192 }
193 $this->pos += 1 + strlen($line);
194 }
195 $this->endTable();
196 }
197
198 /**
199 * Create a pair of TBODY tags for given text span
200 *
201 * @param integer $startPos
202 * @param integer $endPos
203 * @return void
204 */
205 protected function createBodyTags($startPos, $endPos)
206 {
207 $this->parser->addTagPair('TBODY', $startPos, 0, $endPos, 0, -103);
208 }
209
210 /**
211 * Create a pair of TD or TH tags for given text span
212 *
213 * @param string $tagName Either TD or TH
214 * @param integer $startPos
215 * @param integer $endPos
216 * @param string $align Either "left", "center", "right" or ""
217 * @return void
218 */
219 protected function createCellTags($tagName, $startPos, $endPos, $align)
220 {
221 if ($startPos === $endPos)
222 {
223 $tag = $this->parser->addSelfClosingTag($tagName, $startPos, 0, -101);
224 }
225 else
226 {
227 $tag = $this->parser->addTagPair($tagName, $startPos, 0, $endPos, 0, -101);
228 }
229 if ($align)
230 {
231 $tag->setAttribute('align', $align);
232 }
233 }
234
235 /**
236 * Create a pair of THEAD tags for given text span
237 *
238 * @param integer $startPos
239 * @param integer $endPos
240 * @return void
241 */
242 protected function createHeadTags($startPos, $endPos)
243 {
244 $this->parser->addTagPair('THEAD', $startPos, 0, $endPos, 0, -103);
245 }
246
247 /**
248 * Create an ignore tag for given text span
249 *
250 * @param integer $pos
251 * @param integer $len
252 * @return void
253 */
254 protected function createIgnoreTag($pos, $len)
255 {
256 $this->tableTag->cascadeInvalidationTo($this->parser->addIgnoreTag($pos, $len, 1000));
257 }
258
259 /**
260 * Create a pair of TR tags for given text span
261 *
262 * @param integer $startPos
263 * @param integer $endPos
264 * @return void
265 */
266 protected function createRowTags($startPos, $endPos)
267 {
268 $this->parser->addTagPair('TR', $startPos, 0, $endPos, 0, -102);
269 }
270
271 /**
272 * Create an ignore tag for given separator row
273 *
274 * @param array $row
275 * @return void
276 */
277 protected function createSeparatorTag(array $row)
278 {
279 $this->createIgnoreTag($row['pos'] - 1, 1 + strlen($row['line']));
280 }
281
282 /**
283 * Create a pair of TABLE tags for given text span
284 *
285 * @param integer $startPos
286 * @param integer $endPos
287 * @return void
288 */
289 protected function createTableTags($startPos, $endPos)
290 {
291 $this->tableTag = $this->parser->addTagPair('TABLE', $startPos, 0, $endPos, 0, -104);
292 }
293
294 /**
295 * End current buffered table
296 *
297 * @return void
298 */
299 protected function endTable()
300 {
301 if ($this->hasValidTable())
302 {
303 $this->table['cols'] = $this->parseColumnAlignments($this->table['rows'][1]['line']);
304 $this->tables[] = $this->table;
305 }
306 unset($this->table);
307 }
308
309 /**
310 * Test whether a valid table is currently buffered
311 *
312 * @return bool
313 */
314 protected function hasValidTable()
315 {
316 return (isset($this->table) && count($this->table['rows']) > 2 && $this->isValidSeparator($this->table['rows'][1]['line']));
317 }
318
319 /**
320 * Test whether given line is a valid separator
321 *
322 * @param string $line
323 * @return bool
324 */
325 protected function isValidSeparator($line)
326 {
327 return (bool) preg_match('/^ *:?-+:?(?:(?:\\+| *\\| *):?-+:?)+ *$/', $line);
328 }
329
330 /**
331 * Overwrite right angle brackets in given match
332 *
333 * @param string[] $m
334 * @return string
335 */
336 protected function overwriteBlockquoteCallback(array $m)
337 {
338 return strtr($m[0], '!>', ' ');
339 }
340
341 /**
342 * Overwrite escape sequences in current text
343 *
344 * @return void
345 */
346 protected function overwriteEscapes()
347 {
348 if (strpos($this->text, '\\|') !== false)
349 {
350 $this->text = preg_replace('/\\\\[\\\\|]/', '..', $this->text);
351 }
352 }
353
354 /**
355 * Overwrite backticks in given match
356 *
357 * @param string[] $m
358 * @return string
359 */
360 protected function overwriteInlineCodeCallback(array $m)
361 {
362 return strtr($m[0], '|', '.');
363 }
364
365 /**
366 * Overwrite Markdown-style markup in current text
367 *
368 * @return void
369 */
370 protected function overwriteMarkdown()
371 {
372 // Overwrite inline code spans
373 if (strpos($this->text, '`') !== false)
374 {
375 $this->text = preg_replace_callback('/`[^`]*`/', [$this, 'overwriteInlineCodeCallback'], $this->text);
376 }
377
378 // Overwrite blockquotes
379 if (strpos($this->text, '>') !== false)
380 {
381 $this->text = preg_replace_callback('/^(?:>!? ?)+/m', [$this, 'overwriteBlockquoteCallback'], $this->text);
382 }
383 }
384
385 /**
386 * Parse and return column alignments in given separator line
387 *
388 * @param string $line
389 * @return string[]
390 */
391 protected function parseColumnAlignments($line)
392 {
393 // Use a bitfield to represent the colons' presence and map it to the CSS value
394 $align = [
395 0b00 => '',
396 0b01 => 'right',
397 0b10 => 'left',
398 0b11 => 'center'
399 ];
400
401 $cols = [];
402 preg_match_all('/(:?)-+(:?)/', $line, $matches, PREG_SET_ORDER);
403 foreach ($matches as $m)
404 {
405 $key = (!empty($m[1]) ? 2 : 0) + (!empty($m[2]) ? 1 : 0);
406 $cols[] = $align[$key];
407 }
408
409 return $cols;
410 }
411
412 /**
413 * Process current table declaration
414 *
415 * @return void
416 */
417 protected function processCurrentTable()
418 {
419 $firstRow = $this->table['rows'][0];
420 $lastRow = end($this->table['rows']);
421 $this->createTableTags($firstRow['pos'], $lastRow['pos'] + strlen($lastRow['line']));
422
423 $this->addTableHead();
424 $this->createSeparatorTag($this->table['rows'][1]);
425 $this->addTableBody();
426 }
427
428 /**
429 * Process all the captured tables
430 *
431 * @return void
432 */
433 protected function processTables()
434 {
435 foreach ($this->tables as $table)
436 {
437 $this->table = $table;
438 $this->processCurrentTable();
439 }
440 }
441 }