Failed Conditions
Push — reverse-fallback-ns-linking ( 4819a4...482747 )
by Henry
03:24
created

inc/parser/handler.php (33 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use dokuwiki\Extension\Event;
4
use dokuwiki\Extension\SyntaxPlugin;
5
use dokuwiki\Parsing\Handler\Block;
6
use dokuwiki\Parsing\Handler\CallWriter;
7
use dokuwiki\Parsing\Handler\CallWriterInterface;
8
use dokuwiki\Parsing\Handler\Lists;
9
use dokuwiki\Parsing\Handler\Nest;
10
use dokuwiki\Parsing\Handler\Preformatted;
11
use dokuwiki\Parsing\Handler\Quote;
12
use dokuwiki\Parsing\Handler\Table;
13
14
/**
15
 * Class Doku_Handler
16
 */
17
class Doku_Handler {
18
    /** @var CallWriterInterface */
19
    protected $callWriter = null;
20
21
    /** @var array The current CallWriter will write directly to this list of calls, Parser reads it */
22
    public $calls = array();
23
24
    /** @var array internal status holders for some modes */
25
    protected $status = array(
26
        'section' => false,
27
        'doublequote' => 0,
28
    );
29
30
    /** @var bool should blocks be rewritten? FIXME seems to always be true */
31
    protected $rewriteBlocks = true;
32
33
    /**
34
     * Doku_Handler constructor.
35
     */
36
    public function __construct() {
37
        $this->callWriter = new CallWriter($this);
38
    }
39
40
    /**
41
     * Add a new call by passing it to the current CallWriter
42
     *
43
     * @param string $handler handler method name (see mode handlers below)
44
     * @param mixed $args arguments for this call
45
     * @param int $pos  byte position in the original source file
46
     */
47
    protected function addCall($handler, $args, $pos) {
48
        $call = array($handler,$args, $pos);
49
        $this->callWriter->writeCall($call);
50
    }
51
52
    /**
53
     * Similar to addCall, but adds a plugin call
54
     *
55
     * @param string $plugin name of the plugin
56
     * @param mixed $args arguments for this call
57
     * @param int $state a LEXER_STATE_* constant
58
     * @param int $pos byte position in the original source file
59
     * @param string $match matched syntax
60
     */
61
    protected function addPluginCall($plugin, $args, $state, $pos, $match) {
62
        $call = array('plugin',array($plugin, $args, $state, $match), $pos);
63
        $this->callWriter->writeCall($call);
64
    }
65
66
    /**
67
     * Finishes handling
68
     *
69
     * Called from the parser. Calls finalise() on the call writer, closes open
70
     * sections, rewrites blocks and adds document_start and document_end calls.
71
     *
72
     * @triggers PARSER_HANDLER_DONE
73
     */
74
    public function finalize(){
75
        $this->callWriter->finalise();
76
77
        if ( $this->status['section'] ) {
78
            $last_call = end($this->calls);
79
            array_push($this->calls,array('section_close',array(), $last_call[2]));
80
        }
81
82
        if ( $this->rewriteBlocks ) {
83
            $B = new Block();
84
            $this->calls = $B->process($this->calls);
85
        }
86
87
        Event::createAndTrigger('PARSER_HANDLER_DONE',$this);
88
89
        array_unshift($this->calls,array('document_start',array(),0));
90
        $last_call = end($this->calls);
91
        array_push($this->calls,array('document_end',array(),$last_call[2]));
92
    }
93
94
    /**
95
     * fetch the current call and advance the pointer to the next one
96
     *
97
     * @fixme seems to be unused?
98
     * @return bool|mixed
99
     */
100
    public function fetch() {
101
        $call = current($this->calls);
102
        if($call !== false) {
103
            next($this->calls); //advance the pointer
104
            return $call;
105
        }
106
        return false;
107
    }
108
109
110
    /**
111
     * Internal function for parsing highlight options.
112
     * $options is parsed for key value pairs separated by commas.
113
     * A value might also be missing in which case the value will simple
114
     * be set to true. Commas in strings are ignored, e.g. option="4,56"
115
     * will work as expected and will only create one entry.
116
     *
117
     * @param string $options space separated list of key-value pairs,
118
     *                        e.g. option1=123, option2="456"
119
     * @return array|null     Array of key-value pairs $array['key'] = 'value';
120
     *                        or null if no entries found
121
     */
122
    protected function parse_highlight_options($options) {
123
        $result = array();
124
        preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER);
125
        foreach ($matches as $match) {
126
            $equal_sign = strpos($match [0], '=');
127
            if ($equal_sign === false) {
128
                $key = trim($match[0]);
129
                $result [$key] = 1;
130
            } else {
131
                $key = substr($match[0], 0, $equal_sign);
132
                $value = substr($match[0], $equal_sign+1);
133
                $value = trim($value, '"');
134
                if (strlen($value) > 0) {
135
                    $result [$key] = $value;
136
                } else {
137
                    $result [$key] = 1;
138
                }
139
            }
140
        }
141
142
        // Check for supported options
143
        $result = array_intersect_key(
144
            $result,
145
            array_flip(array(
146
                           'enable_line_numbers',
147
                           'start_line_numbers_at',
148
                           'highlight_lines_extra',
149
                           'enable_keyword_links')
150
            )
151
        );
152
153
        // Sanitize values
154
        if(isset($result['enable_line_numbers'])) {
155
            if($result['enable_line_numbers'] === 'false') {
156
                $result['enable_line_numbers'] = false;
157
            }
158
            $result['enable_line_numbers'] = (bool) $result['enable_line_numbers'];
159
        }
160
        if(isset($result['highlight_lines_extra'])) {
161
            $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra']));
162
            $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']);
163
            $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']);
164
        }
165
        if(isset($result['start_line_numbers_at'])) {
166
            $result['start_line_numbers_at'] = (int) $result['start_line_numbers_at'];
167
        }
168
        if(isset($result['enable_keyword_links'])) {
169
            if($result['enable_keyword_links'] === 'false') {
170
                $result['enable_keyword_links'] = false;
171
            }
172
            $result['enable_keyword_links'] = (bool) $result['enable_keyword_links'];
173
        }
174
        if (count($result) == 0) {
175
            return null;
176
        }
177
178
        return $result;
179
    }
180
181
    /**
182
     * Simplifies handling for the formatting tags which all behave the same
183
     *
184
     * @param string $match matched syntax
185
     * @param int $state a LEXER_STATE_* constant
186
     * @param int $pos byte position in the original source file
187
     * @param string $name actual mode name
188
     */
189
    protected function nestingTag($match, $state, $pos, $name) {
190
        switch ( $state ) {
191
            case DOKU_LEXER_ENTER:
192
                $this->addCall($name.'_open', array(), $pos);
193
                break;
194
            case DOKU_LEXER_EXIT:
195
                $this->addCall($name.'_close', array(), $pos);
196
                break;
197
            case DOKU_LEXER_UNMATCHED:
198
                $this->addCall('cdata', array($match), $pos);
199
                break;
200
        }
201
    }
202
203
204
    /**
205
     * The following methods define the handlers for the different Syntax modes
206
     *
207
     * The handlers are called from dokuwiki\Parsing\Lexer\Lexer\invokeParser()
208
     *
209
     * @todo it might make sense to move these into their own class or merge them with the
210
     *       ParserMode classes some time.
211
     */
212
    // region mode handlers
213
214
    /**
215
     * Special plugin handler
216
     *
217
     * This handler is called for all modes starting with 'plugin_'.
218
     * An additional parameter with the plugin name is passed. The plugin's handle()
219
     * method is called here
220
     *
221
     * @author Andreas Gohr <[email protected]>
222
     *
223
     * @param string $match matched syntax
224
     * @param int $state a LEXER_STATE_* constant
225
     * @param int $pos byte position in the original source file
226
     * @param string $pluginname name of the plugin
227
     * @return bool mode handled?
228
     */
229
    public function plugin($match, $state, $pos, $pluginname){
230
        $data = array($match);
231
        /** @var SyntaxPlugin $plugin */
232
        $plugin = plugin_load('syntax',$pluginname);
233
        if($plugin != null){
234
            $data = $plugin->handle($match, $state, $pos, $this);
235
        }
236
        if ($data !== false) {
237
            $this->addPluginCall($pluginname,$data,$state,$pos,$match);
238
        }
239
        return true;
240
    }
241
242
    /**
243
     * @param string $match matched syntax
244
     * @param int $state a LEXER_STATE_* constant
245
     * @param int $pos byte position in the original source file
246
     * @return bool mode handled?
247
     */
248
    public function base($match, $state, $pos) {
249
        switch ( $state ) {
250
            case DOKU_LEXER_UNMATCHED:
251
                $this->addCall('cdata', array($match), $pos);
252
                return true;
253
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
254
        }
255
        return false;
256
    }
257
258
    /**
259
     * @param string $match matched syntax
260
     * @param int $state a LEXER_STATE_* constant
261
     * @param int $pos byte position in the original source file
262
     * @return bool mode handled?
263
     */
264
    public function header($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
265
        // get level and title
266
        $title = trim($match);
267
        $level = 7 - strspn($title,'=');
268
        if($level < 1) $level = 1;
269
        $title = trim($title,'=');
270
        $title = trim($title);
271
272
        if ($this->status['section']) $this->addCall('section_close', array(), $pos);
273
274
        $this->addCall('header', array($title, $level, $pos), $pos);
275
276
        $this->addCall('section_open', array($level), $pos);
277
        $this->status['section'] = true;
278
        return true;
279
    }
280
281
    /**
282
     * @param string $match matched syntax
283
     * @param int $state a LEXER_STATE_* constant
284
     * @param int $pos byte position in the original source file
285
     * @return bool mode handled?
286
     */
287
    public function notoc($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
288
        $this->addCall('notoc', array(), $pos);
289
        return true;
290
    }
291
292
    /**
293
     * @param string $match matched syntax
294
     * @param int $state a LEXER_STATE_* constant
295
     * @param int $pos byte position in the original source file
296
     * @return bool mode handled?
297
     */
298
    public function nocache($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
299
        $this->addCall('nocache', array(), $pos);
300
        return true;
301
    }
302
303
    /**
304
     * @param string $match matched syntax
305
     * @param int $state a LEXER_STATE_* constant
306
     * @param int $pos byte position in the original source file
307
     * @return bool mode handled?
308
     */
309
    public function linebreak($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
310
        $this->addCall('linebreak', array(), $pos);
311
        return true;
312
    }
313
314
    /**
315
     * @param string $match matched syntax
316
     * @param int $state a LEXER_STATE_* constant
317
     * @param int $pos byte position in the original source file
318
     * @return bool mode handled?
319
     */
320
    public function eol($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
321
        $this->addCall('eol', array(), $pos);
322
        return true;
323
    }
324
325
    /**
326
     * @param string $match matched syntax
327
     * @param int $state a LEXER_STATE_* constant
328
     * @param int $pos byte position in the original source file
329
     * @return bool mode handled?
330
     */
331
    public function hr($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
332
        $this->addCall('hr', array(), $pos);
333
        return true;
334
    }
335
336
    /**
337
     * @param string $match matched syntax
338
     * @param int $state a LEXER_STATE_* constant
339
     * @param int $pos byte position in the original source file
340
     * @return bool mode handled?
341
     */
342
    public function strong($match, $state, $pos) {
343
        $this->nestingTag($match, $state, $pos, 'strong');
344
        return true;
345
    }
346
347
    /**
348
     * @param string $match matched syntax
349
     * @param int $state a LEXER_STATE_* constant
350
     * @param int $pos byte position in the original source file
351
     * @return bool mode handled?
352
     */
353
    public function emphasis($match, $state, $pos) {
354
        $this->nestingTag($match, $state, $pos, 'emphasis');
355
        return true;
356
    }
357
358
    /**
359
     * @param string $match matched syntax
360
     * @param int $state a LEXER_STATE_* constant
361
     * @param int $pos byte position in the original source file
362
     * @return bool mode handled?
363
     */
364
    public function underline($match, $state, $pos) {
365
        $this->nestingTag($match, $state, $pos, 'underline');
366
        return true;
367
    }
368
369
    /**
370
     * @param string $match matched syntax
371
     * @param int $state a LEXER_STATE_* constant
372
     * @param int $pos byte position in the original source file
373
     * @return bool mode handled?
374
     */
375
    public function monospace($match, $state, $pos) {
376
        $this->nestingTag($match, $state, $pos, 'monospace');
377
        return true;
378
    }
379
380
    /**
381
     * @param string $match matched syntax
382
     * @param int $state a LEXER_STATE_* constant
383
     * @param int $pos byte position in the original source file
384
     * @return bool mode handled?
385
     */
386
    public function subscript($match, $state, $pos) {
387
        $this->nestingTag($match, $state, $pos, 'subscript');
388
        return true;
389
    }
390
391
    /**
392
     * @param string $match matched syntax
393
     * @param int $state a LEXER_STATE_* constant
394
     * @param int $pos byte position in the original source file
395
     * @return bool mode handled?
396
     */
397
    public function superscript($match, $state, $pos) {
398
        $this->nestingTag($match, $state, $pos, 'superscript');
399
        return true;
400
    }
401
402
    /**
403
     * @param string $match matched syntax
404
     * @param int $state a LEXER_STATE_* constant
405
     * @param int $pos byte position in the original source file
406
     * @return bool mode handled?
407
     */
408
    public function deleted($match, $state, $pos) {
409
        $this->nestingTag($match, $state, $pos, 'deleted');
410
        return true;
411
    }
412
413
    /**
414
     * @param string $match matched syntax
415
     * @param int $state a LEXER_STATE_* constant
416
     * @param int $pos byte position in the original source file
417
     * @return bool mode handled?
418
     */
419
    public function footnote($match, $state, $pos) {
420
        if (!isset($this->_footnote)) $this->_footnote = false;
421
422
        switch ( $state ) {
423
            case DOKU_LEXER_ENTER:
424
                // footnotes can not be nested - however due to limitations in lexer it can't be prevented
425
                // we will still enter a new footnote mode, we just do nothing
426
                if ($this->_footnote) {
427
                    $this->addCall('cdata', array($match), $pos);
428
                    break;
429
                }
430
                $this->_footnote = true;
431
432
                $this->callWriter = new Nest($this->callWriter, 'footnote_close');
433
                $this->addCall('footnote_open', array(), $pos);
434
            break;
435
            case DOKU_LEXER_EXIT:
436
                // check whether we have already exitted the footnote mode, can happen if the modes were nested
437
                if (!$this->_footnote) {
438
                    $this->addCall('cdata', array($match), $pos);
439
                    break;
440
                }
441
442
                $this->_footnote = false;
443
                $this->addCall('footnote_close', array(), $pos);
444
445
                /** @var Nest $reWriter */
446
                $reWriter = $this->callWriter;
447
                $this->callWriter = $reWriter->process();
448
            break;
449
            case DOKU_LEXER_UNMATCHED:
450
                $this->addCall('cdata', array($match), $pos);
451
            break;
452
        }
453
        return true;
454
    }
455
456
    /**
457
     * @param string $match matched syntax
458
     * @param int $state a LEXER_STATE_* constant
459
     * @param int $pos byte position in the original source file
460
     * @return bool mode handled?
461
     */
462
    public function listblock($match, $state, $pos) {
463
        switch ( $state ) {
464
            case DOKU_LEXER_ENTER:
465
                $this->callWriter = new Lists($this->callWriter);
466
                $this->addCall('list_open', array($match), $pos);
467
            break;
468
            case DOKU_LEXER_EXIT:
469
                $this->addCall('list_close', array(), $pos);
470
                /** @var Lists $reWriter */
471
                $reWriter = $this->callWriter;
472
                $this->callWriter = $reWriter->process();
473
            break;
474
            case DOKU_LEXER_MATCHED:
475
                $this->addCall('list_item', array($match), $pos);
476
            break;
477
            case DOKU_LEXER_UNMATCHED:
478
                $this->addCall('cdata', array($match), $pos);
479
            break;
480
        }
481
        return true;
482
    }
483
484
    /**
485
     * @param string $match matched syntax
486
     * @param int $state a LEXER_STATE_* constant
487
     * @param int $pos byte position in the original source file
488
     * @return bool mode handled?
489
     */
490
    public function unformatted($match, $state, $pos) {
491
        if ( $state == DOKU_LEXER_UNMATCHED ) {
492
            $this->addCall('unformatted', array($match), $pos);
493
        }
494
        return true;
495
    }
496
497
    /**
498
     * @param string $match matched syntax
499
     * @param int $state a LEXER_STATE_* constant
500
     * @param int $pos byte position in the original source file
501
     * @return bool mode handled?
502
     */
503
    public function php($match, $state, $pos) {
504
        if ( $state == DOKU_LEXER_UNMATCHED ) {
505
            $this->addCall('php', array($match), $pos);
506
        }
507
        return true;
508
    }
509
510
    /**
511
     * @param string $match matched syntax
512
     * @param int $state a LEXER_STATE_* constant
513
     * @param int $pos byte position in the original source file
514
     * @return bool mode handled?
515
     */
516
    public function phpblock($match, $state, $pos) {
517
        if ( $state == DOKU_LEXER_UNMATCHED ) {
518
            $this->addCall('phpblock', array($match), $pos);
519
        }
520
        return true;
521
    }
522
523
    /**
524
     * @param string $match matched syntax
525
     * @param int $state a LEXER_STATE_* constant
526
     * @param int $pos byte position in the original source file
527
     * @return bool mode handled?
528
     */
529
    public function html($match, $state, $pos) {
530
        if ( $state == DOKU_LEXER_UNMATCHED ) {
531
            $this->addCall('html', array($match), $pos);
532
        }
533
        return true;
534
    }
535
536
    /**
537
     * @param string $match matched syntax
538
     * @param int $state a LEXER_STATE_* constant
539
     * @param int $pos byte position in the original source file
540
     * @return bool mode handled?
541
     */
542
    public function htmlblock($match, $state, $pos) {
543
        if ( $state == DOKU_LEXER_UNMATCHED ) {
544
            $this->addCall('htmlblock', array($match), $pos);
545
        }
546
        return true;
547
    }
548
549
    /**
550
     * @param string $match matched syntax
551
     * @param int $state a LEXER_STATE_* constant
552
     * @param int $pos byte position in the original source file
553
     * @return bool mode handled?
554
     */
555
    public function preformatted($match, $state, $pos) {
556
        switch ( $state ) {
557
            case DOKU_LEXER_ENTER:
558
                $this->callWriter = new Preformatted($this->callWriter);
559
                $this->addCall('preformatted_start', array(), $pos);
560
            break;
561
            case DOKU_LEXER_EXIT:
562
                $this->addCall('preformatted_end', array(), $pos);
563
                /** @var Preformatted $reWriter */
564
                $reWriter = $this->callWriter;
565
                $this->callWriter = $reWriter->process();
566
            break;
567
            case DOKU_LEXER_MATCHED:
568
                $this->addCall('preformatted_newline', array(), $pos);
569
            break;
570
            case DOKU_LEXER_UNMATCHED:
571
                $this->addCall('preformatted_content', array($match), $pos);
572
            break;
573
        }
574
575
        return true;
576
    }
577
578
    /**
579
     * @param string $match matched syntax
580
     * @param int $state a LEXER_STATE_* constant
581
     * @param int $pos byte position in the original source file
582
     * @return bool mode handled?
583
     */
584
    public function quote($match, $state, $pos) {
585
586
        switch ( $state ) {
587
588
            case DOKU_LEXER_ENTER:
589
                $this->callWriter = new Quote($this->callWriter);
590
                $this->addCall('quote_start', array($match), $pos);
591
            break;
592
593
            case DOKU_LEXER_EXIT:
594
                $this->addCall('quote_end', array(), $pos);
595
                /** @var Lists $reWriter */
596
                $reWriter = $this->callWriter;
597
                $this->callWriter = $reWriter->process();
598
            break;
599
600
            case DOKU_LEXER_MATCHED:
601
                $this->addCall('quote_newline', array($match), $pos);
602
            break;
603
604
            case DOKU_LEXER_UNMATCHED:
605
                $this->addCall('cdata', array($match), $pos);
606
            break;
607
608
        }
609
610
        return true;
611
    }
612
613
    /**
614
     * @param string $match matched syntax
615
     * @param int $state a LEXER_STATE_* constant
616
     * @param int $pos byte position in the original source file
617
     * @return bool mode handled?
618
     */
619
    public function file($match, $state, $pos) {
620
        return $this->code($match, $state, $pos, 'file');
621
    }
622
623
    /**
624
     * @param string $match matched syntax
625
     * @param int $state a LEXER_STATE_* constant
626
     * @param int $pos byte position in the original source file
627
     * @param string $type either 'code' or 'file'
628
     * @return bool mode handled?
629
     */
630
    public function code($match, $state, $pos, $type='code') {
631
        if ( $state == DOKU_LEXER_UNMATCHED ) {
632
            $matches = explode('>',$match,2);
633
            // Cut out variable options enclosed in []
634
            preg_match('/\[.*\]/', $matches[0], $options);
635
            if (!empty($options[0])) {
636
                $matches[0] = str_replace($options[0], '', $matches[0]);
637
            }
638
            $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY);
639
            while(count($param) < 2) array_push($param, null);
640
            // We shortcut html here.
641
            if ($param[0] == 'html') $param[0] = 'html4strict';
642
            if ($param[0] == '-') $param[0] = null;
643
            array_unshift($param, $matches[1]);
644
            if (!empty($options[0])) {
645
                $param [] = $this->parse_highlight_options ($options[0]);
646
            }
647
            $this->addCall($type, $param, $pos);
648
        }
649
        return true;
650
    }
651
652
    /**
653
     * @param string $match matched syntax
654
     * @param int $state a LEXER_STATE_* constant
655
     * @param int $pos byte position in the original source file
656
     * @return bool mode handled?
657
     */
658
    public function acronym($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
659
        $this->addCall('acronym', array($match), $pos);
660
        return true;
661
    }
662
663
    /**
664
     * @param string $match matched syntax
665
     * @param int $state a LEXER_STATE_* constant
666
     * @param int $pos byte position in the original source file
667
     * @return bool mode handled?
668
     */
669
    public function smiley($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
670
        $this->addCall('smiley', array($match), $pos);
671
        return true;
672
    }
673
674
    /**
675
     * @param string $match matched syntax
676
     * @param int $state a LEXER_STATE_* constant
677
     * @param int $pos byte position in the original source file
678
     * @return bool mode handled?
679
     */
680
    public function wordblock($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
681
        $this->addCall('wordblock', array($match), $pos);
682
        return true;
683
    }
684
685
    /**
686
     * @param string $match matched syntax
687
     * @param int $state a LEXER_STATE_* constant
688
     * @param int $pos byte position in the original source file
689
     * @return bool mode handled?
690
     */
691
    public function entity($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
692
        $this->addCall('entity', array($match), $pos);
693
        return true;
694
    }
695
696
    /**
697
     * @param string $match matched syntax
698
     * @param int $state a LEXER_STATE_* constant
699
     * @param int $pos byte position in the original source file
700
     * @return bool mode handled?
701
     */
702
    public function multiplyentity($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
703
        preg_match_all('/\d+/',$match,$matches);
704
        $this->addCall('multiplyentity', array($matches[0][0], $matches[0][1]), $pos);
705
        return true;
706
    }
707
708
    /**
709
     * @param string $match matched syntax
710
     * @param int $state a LEXER_STATE_* constant
711
     * @param int $pos byte position in the original source file
712
     * @return bool mode handled?
713
     */
714
    public function singlequoteopening($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
715
        $this->addCall('singlequoteopening', array(), $pos);
716
        return true;
717
    }
718
719
    /**
720
     * @param string $match matched syntax
721
     * @param int $state a LEXER_STATE_* constant
722
     * @param int $pos byte position in the original source file
723
     * @return bool mode handled?
724
     */
725
    public function singlequoteclosing($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
726
        $this->addCall('singlequoteclosing', array(), $pos);
727
        return true;
728
    }
729
730
    /**
731
     * @param string $match matched syntax
732
     * @param int $state a LEXER_STATE_* constant
733
     * @param int $pos byte position in the original source file
734
     * @return bool mode handled?
735
     */
736
    public function apostrophe($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
737
        $this->addCall('apostrophe', array(), $pos);
738
        return true;
739
    }
740
741
    /**
742
     * @param string $match matched syntax
743
     * @param int $state a LEXER_STATE_* constant
744
     * @param int $pos byte position in the original source file
745
     * @return bool mode handled?
746
     */
747
    public function doublequoteopening($match, $state, $pos) {
0 ignored issues
show
The parameter $match is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
748
        $this->addCall('doublequoteopening', array(), $pos);
749
        $this->status['doublequote']++;
750
        return true;
751
    }
752
753
    /**
754
     * @param string $match matched syntax
755
     * @param int $state a LEXER_STATE_* constant
756
     * @param int $pos byte position in the original source file
757
     * @return bool mode handled?
758
     */
759
    public function doublequoteclosing($match, $state, $pos) {
760
        if ($this->status['doublequote'] <= 0) {
761
            $this->doublequoteopening($match, $state, $pos);
762
        } else {
763
            $this->addCall('doublequoteclosing', array(), $pos);
764
            $this->status['doublequote'] = max(0, --$this->status['doublequote']);
765
        }
766
        return true;
767
    }
768
769
    /**
770
     * @param string $match matched syntax
771
     * @param int $state a LEXER_STATE_* constant
772
     * @param int $pos byte position in the original source file
773
     * @return bool mode handled?
774
     */
775
    public function camelcaselink($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
776
        $this->addCall('camelcaselink', array($match), $pos);
777
        return true;
778
    }
779
780
    /**
781
     * @param string $match matched syntax
782
     * @param int $state a LEXER_STATE_* constant
783
     * @param int $pos byte position in the original source file
784
     * @return bool mode handled?
785
     */
786
    public function internallink($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
787
        // Strip the opening and closing markup
788
        $link = preg_replace(array('/^\[\[/','/\]\]$/u'),'',$match);
789
790
        // Split title from URL
791
        $link = explode('|',$link,2);
792
        if ( !isset($link[1]) ) {
793
            $link[1] = null;
794
        } else if ( preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
795
            // If the title is an image, convert it to an array containing the image details
796
            $link[1] = Doku_Handler_Parse_Media($link[1]);
797
        }
798
        $link[0] = trim($link[0]);
799
800
        //decide which kind of link it is
801
802
        if ( link_isinterwiki($link[0]) ) {
803
            // Interwiki
804
            $interwiki = explode('>',$link[0],2);
805
            $this->addCall(
806
                'interwikilink',
807
                array($link[0],$link[1],strtolower($interwiki[0]),$interwiki[1]),
808
                $pos
809
                );
810
        }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) {
811
            // Windows Share
812
            $this->addCall(
813
                'windowssharelink',
814
                array($link[0],$link[1]),
815
                $pos
816
                );
817
        }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) {
818
            // external link (accepts all protocols)
819
            $this->addCall(
820
                    'externallink',
821
                    array($link[0],$link[1]),
822
                    $pos
823
                    );
824
        }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) {
825
            // E-Mail (pattern above is defined in inc/mail.php)
826
            $this->addCall(
827
                'emaillink',
828
                array($link[0],$link[1]),
829
                $pos
830
                );
831
        }elseif ( preg_match('!^#.+!',$link[0]) ){
832
            // local link
833
            $this->addCall(
834
                'locallink',
835
                array(substr($link[0],1),$link[1]),
836
                $pos
837
                );
838
        }else{
839
            // internal link
840
            $this->addCall(
841
                'internallink',
842
                array($link[0],$link[1]),
843
                $pos
844
                );
845
        }
846
847
        return true;
848
    }
849
850
    /**
851
     * @param string $match matched syntax
852
     * @param int $state a LEXER_STATE_* constant
853
     * @param int $pos byte position in the original source file
854
     * @return bool mode handled?
855
     */
856
    public function filelink($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
857
        $this->addCall('filelink', array($match, null), $pos);
858
        return true;
859
    }
860
861
    /**
862
     * @param string $match matched syntax
863
     * @param int $state a LEXER_STATE_* constant
864
     * @param int $pos byte position in the original source file
865
     * @return bool mode handled?
866
     */
867
    public function windowssharelink($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
868
        $this->addCall('windowssharelink', array($match, null), $pos);
869
        return true;
870
    }
871
872
    /**
873
     * @param string $match matched syntax
874
     * @param int $state a LEXER_STATE_* constant
875
     * @param int $pos byte position in the original source file
876
     * @return bool mode handled?
877
     */
878
    public function media($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
879
        $p = Doku_Handler_Parse_Media($match);
880
881
        $this->addCall(
882
              $p['type'],
883
              array($p['src'], $p['title'], $p['align'], $p['width'],
884
                     $p['height'], $p['cache'], $p['linking']),
885
              $pos
886
             );
887
        return true;
888
    }
889
890
    /**
891
     * @param string $match matched syntax
892
     * @param int $state a LEXER_STATE_* constant
893
     * @param int $pos byte position in the original source file
894
     * @return bool mode handled?
895
     */
896
    public function rss($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
897
        $link = preg_replace(array('/^\{\{rss>/','/\}\}$/'),'',$match);
898
899
        // get params
900
        list($link,$params) = explode(' ',$link,2);
901
902
        $p = array();
903
        if(preg_match('/\b(\d+)\b/',$params,$match)){
904
            $p['max'] = $match[1];
905
        }else{
906
            $p['max'] = 8;
907
        }
908
        $p['reverse'] = (preg_match('/rev/',$params));
909
        $p['author']  = (preg_match('/\b(by|author)/',$params));
910
        $p['date']    = (preg_match('/\b(date)/',$params));
911
        $p['details'] = (preg_match('/\b(desc|detail)/',$params));
912
        $p['nosort']  = (preg_match('/\b(nosort)\b/',$params));
913
914
        if (preg_match('/\b(\d+)([dhm])\b/',$params,$match)) {
915
            $period = array('d' => 86400, 'h' => 3600, 'm' => 60);
916
            $p['refresh'] = max(600,$match[1]*$period[$match[2]]);  // n * period in seconds, minimum 10 minutes
917
        } else {
918
            $p['refresh'] = 14400;   // default to 4 hours
919
        }
920
921
        $this->addCall('rss', array($link, $p), $pos);
922
        return true;
923
    }
924
925
    /**
926
     * @param string $match matched syntax
927
     * @param int $state a LEXER_STATE_* constant
928
     * @param int $pos byte position in the original source file
929
     * @return bool mode handled?
930
     */
931
    public function externallink($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
932
        $url   = $match;
933
        $title = null;
934
935
        // add protocol on simple short URLs
936
        if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')){
937
            $title = $url;
938
            $url   = 'ftp://'.$url;
939
        }
940
        if(substr($url,0,3) == 'www' && (substr($url,0,7) != 'http://')){
941
            $title = $url;
942
            $url = 'http://'.$url;
943
        }
944
945
        $this->addCall('externallink', array($url, $title), $pos);
946
        return true;
947
    }
948
949
    /**
950
     * @param string $match matched syntax
951
     * @param int $state a LEXER_STATE_* constant
952
     * @param int $pos byte position in the original source file
953
     * @return bool mode handled?
954
     */
955
    public function emaillink($match, $state, $pos) {
0 ignored issues
show
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
956
        $email = preg_replace(array('/^</','/>$/'),'',$match);
957
        $this->addCall('emaillink', array($email, null), $pos);
958
        return true;
959
    }
960
961
    /**
962
     * @param string $match matched syntax
963
     * @param int $state a LEXER_STATE_* constant
964
     * @param int $pos byte position in the original source file
965
     * @return bool mode handled?
966
     */
967
    public function table($match, $state, $pos) {
968
        switch ( $state ) {
969
970
            case DOKU_LEXER_ENTER:
971
972
                $this->callWriter = new Table($this->callWriter);
973
974
                $this->addCall('table_start', array($pos + 1), $pos);
975
                if ( trim($match) == '^' ) {
976
                    $this->addCall('tableheader', array(), $pos);
977
                } else {
978
                    $this->addCall('tablecell', array(), $pos);
979
                }
980
            break;
981
982
            case DOKU_LEXER_EXIT:
983
                $this->addCall('table_end', array($pos), $pos);
984
                /** @var Table $reWriter */
985
                $reWriter = $this->callWriter;
986
                $this->callWriter = $reWriter->process();
987
            break;
988
989
            case DOKU_LEXER_UNMATCHED:
990
                if ( trim($match) != '' ) {
991
                    $this->addCall('cdata', array($match), $pos);
992
                }
993
            break;
994
995
            case DOKU_LEXER_MATCHED:
996
                if ( $match == ' ' ){
997
                    $this->addCall('cdata', array($match), $pos);
998
                } else if ( preg_match('/:::/',$match) ) {
999
                    $this->addCall('rowspan', array($match), $pos);
1000
                } else if ( preg_match('/\t+/',$match) ) {
1001
                    $this->addCall('table_align', array($match), $pos);
1002
                } else if ( preg_match('/ {2,}/',$match) ) {
1003
                    $this->addCall('table_align', array($match), $pos);
1004
                } else if ( $match == "\n|" ) {
1005
                    $this->addCall('table_row', array(), $pos);
1006
                    $this->addCall('tablecell', array(), $pos);
1007
                } else if ( $match == "\n^" ) {
1008
                    $this->addCall('table_row', array(), $pos);
1009
                    $this->addCall('tableheader', array(), $pos);
1010
                } else if ( $match == '|' ) {
1011
                    $this->addCall('tablecell', array(), $pos);
1012
                } else if ( $match == '^' ) {
1013
                    $this->addCall('tableheader', array(), $pos);
1014
                }
1015
            break;
1016
        }
1017
        return true;
1018
    }
1019
1020
    // endregion modes
1021
}
1022
1023
//------------------------------------------------------------------------
1024
function Doku_Handler_Parse_Media($match) {
1025
1026
    // Strip the opening and closing markup
1027
    $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match);
1028
1029
    // Split title from URL
1030
    $link = explode('|',$link,2);
1031
1032
    // Check alignment
1033
    $ralign = (bool)preg_match('/^ /',$link[0]);
1034
    $lalign = (bool)preg_match('/ $/',$link[0]);
1035
1036
    // Logic = what's that ;)...
1037
    if ( $lalign & $ralign ) {
1038
        $align = 'center';
1039
    } else if ( $ralign ) {
1040
        $align = 'right';
1041
    } else if ( $lalign ) {
1042
        $align = 'left';
1043
    } else {
1044
        $align = null;
1045
    }
1046
1047
    // The title...
1048
    if ( !isset($link[1]) ) {
1049
        $link[1] = null;
1050
    }
1051
1052
    //remove aligning spaces
1053
    $link[0] = trim($link[0]);
1054
1055
    //split into src and parameters (using the very last questionmark)
1056
    $pos = strrpos($link[0], '?');
1057
    if($pos !== false){
1058
        $src   = substr($link[0],0,$pos);
1059
        $param = substr($link[0],$pos+1);
1060
    }else{
1061
        $src   = $link[0];
1062
        $param = '';
1063
    }
1064
1065
    //parse width and height
1066
    if(preg_match('#(\d+)(x(\d+))?#i',$param,$size)){
1067
        !empty($size[1]) ? $w = $size[1] : $w = null;
1068
        !empty($size[3]) ? $h = $size[3] : $h = null;
1069
    } else {
1070
        $w = null;
1071
        $h = null;
1072
    }
1073
1074
    //get linking command
1075
    if(preg_match('/nolink/i',$param)){
1076
        $linking = 'nolink';
1077
    }else if(preg_match('/direct/i',$param)){
1078
        $linking = 'direct';
1079
    }else if(preg_match('/linkonly/i',$param)){
1080
        $linking = 'linkonly';
1081
    }else{
1082
        $linking = 'details';
1083
    }
1084
1085
    //get caching command
1086
    if (preg_match('/(nocache|recache)/i',$param,$cachemode)){
1087
        $cache = $cachemode[1];
1088
    }else{
1089
        $cache = 'cache';
1090
    }
1091
1092
    // Check whether this is a local or remote image or interwiki
1093
    if (media_isexternal($src) || link_isinterwiki($src)){
1094
        $call = 'externalmedia';
1095
    } else {
1096
        $call = 'internalmedia';
1097
    }
1098
1099
    $params = array(
1100
        'type'=>$call,
1101
        'src'=>$src,
1102
        'title'=>$link[1],
1103
        'align'=>$align,
1104
        'width'=>$w,
1105
        'height'=>$h,
1106
        'cache'=>$cache,
1107
        'linking'=>$linking,
1108
    );
1109
1110
    return $params;
1111
}
1112
1113