Failed Conditions
Push — psr2 ( de2261...e9eace )
by Andreas
04:19
created

inc/parser/handler.php (34 issues)

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\Handler\Block;
4
use dokuwiki\Handler\CallWriter;
5
use dokuwiki\Handler\CallWriterInterface;
6
use dokuwiki\Handler\Lists;
7
use dokuwiki\Handler\Nest;
8
use dokuwiki\Handler\Preformatted;
9
use dokuwiki\Handler\Quote;
10
use dokuwiki\Handler\Table;
11
12
/**
13
 * Class Doku_Handler
14
 */
15
class Doku_Handler {
16
    /** @var CallWriterInterface */
17
    protected $callWriter = null;
18
19
    /** @var array The current CallWriter will write directly to this list of calls, Parser reads it */
20
    public $calls = array();
21
22
    /** @var array internal status holders for some modes */
23
    protected $status = array(
24
        'section' => false,
25
        'doublequote' => 0,
26
    );
27
28
    /** @var bool should blocks be rewritten? FIXME seems to always be true */
29
    protected $rewriteBlocks = true;
30
31
    /**
32
     * Doku_Handler constructor.
33
     */
34
    function __construct() {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
35
        $this->callWriter = new CallWriter($this);
36
    }
37
38
    /**
39
     * Add a new call by passing it to the current CallWriter
40
     *
41
     * @param string $handler handler method name (see mode handlers below)
42
     * @param mixed $args arguments for this call
43
     * @param int $pos  byte position in the original source file
44
     */
45
    protected function addCall($handler, $args, $pos) {
46
        $call = array($handler,$args, $pos);
47
        $this->callWriter->writeCall($call);
48
    }
49
50
    /**
51
     * Similar to addCall, but adds a plugin call
52
     *
53
     * @param string $plugin name of the plugin
54
     * @param mixed $args arguments for this call
55
     * @param int $state a LEXER_STATE_* constant
56
     * @param int $pos byte position in the original source file
57
     * @param string $match matched syntax
58
     */
59
    protected function addPluginCall($plugin, $args, $state, $pos, $match) {
60
        $call = array('plugin',array($plugin, $args, $state, $match), $pos);
61
        $this->callWriter->writeCall($call);
62
    }
63
64
    /**
65
     * Finishes handling
66
     *
67
     * Called from the parser. Calls finalise() on the call writer, closes open
68
     * sections, rewrites blocks and adds document_start and document_end calls.
69
     *
70
     * @triggers PARSER_HANDLER_DONE
71
     */
72
    public function finalize(){
73
        $this->callWriter->finalise();
74
75
        if ( $this->status['section'] ) {
76
            $last_call = end($this->calls);
77
            array_push($this->calls,array('section_close',array(), $last_call[2]));
78
        }
79
80
        if ( $this->rewriteBlocks ) {
81
            $B = new Block();
82
            $this->calls = $B->process($this->calls);
83
        }
84
85
        trigger_event('PARSER_HANDLER_DONE',$this);
86
87
        array_unshift($this->calls,array('document_start',array(),0));
88
        $last_call = end($this->calls);
89
        array_push($this->calls,array('document_end',array(),$last_call[2]));
90
    }
91
92
    /**
93
     * fetch the current call and advance the pointer to the next one
94
     *
95
     * @fixme seems to be unused?
96
     * @return bool|mixed
97
     */
98
    public function fetch() {
99
        $call = current($this->calls);
100
        if($call !== false) {
101
            next($this->calls); //advance the pointer
102
            return $call;
103
        }
104
        return false;
105
    }
106
107
108
    /**
109
     * Internal function for parsing highlight options.
110
     * $options is parsed for key value pairs separated by commas.
111
     * A value might also be missing in which case the value will simple
112
     * be set to true. Commas in strings are ignored, e.g. option="4,56"
113
     * will work as expected and will only create one entry.
114
     *
115
     * @param string $options space separated list of key-value pairs,
116
     *                        e.g. option1=123, option2="456"
117
     * @return array|null     Array of key-value pairs $array['key'] = 'value';
118
     *                        or null if no entries found
119
     */
120
    protected function parse_highlight_options ($options) {
121
        $result = array();
122
        preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER);
123
        foreach ($matches as $match) {
124
            $equal_sign = strpos($match [0], '=');
125
            if ($equal_sign === false) {
126
                $key = trim($match[0]);
127
                $result [$key] = 1;
128
            } else {
129
                $key = substr($match[0], 0, $equal_sign);
130
                $value = substr($match[0], $equal_sign+1);
131
                $value = trim($value, '"');
132
                if (strlen($value) > 0) {
133
                    $result [$key] = $value;
134
                } else {
135
                    $result [$key] = 1;
136
                }
137
            }
138
        }
139
140
        // Check for supported options
141
        $result = array_intersect_key(
142
            $result,
143
            array_flip(array(
144
                           'enable_line_numbers',
145
                           'start_line_numbers_at',
146
                           'highlight_lines_extra',
147
                           'enable_keyword_links')
148
            )
149
        );
150
151
        // Sanitize values
152
        if(isset($result['enable_line_numbers'])) {
153
            if($result['enable_line_numbers'] === 'false') {
154
                $result['enable_line_numbers'] = false;
155
            }
156
            $result['enable_line_numbers'] = (bool) $result['enable_line_numbers'];
157
        }
158
        if(isset($result['highlight_lines_extra'])) {
159
            $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra']));
160
            $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']);
161
            $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']);
162
        }
163
        if(isset($result['start_line_numbers_at'])) {
164
            $result['start_line_numbers_at'] = (int) $result['start_line_numbers_at'];
165
        }
166
        if(isset($result['enable_keyword_links'])) {
167
            if($result['enable_keyword_links'] === 'false') {
168
                $result['enable_keyword_links'] = false;
169
            }
170
            $result['enable_keyword_links'] = (bool) $result['enable_keyword_links'];
171
        }
172
        if (count($result) == 0) {
173
            return null;
174
        }
175
176
        return $result;
177
    }
178
179
    /**
180
     * Simplifies handling for the formatting tags which all behave the same
181
     *
182
     * @param string $match matched syntax
183
     * @param int $state a LEXER_STATE_* constant
184
     * @param int $pos byte position in the original source file
185
     * @param string $name actual mode name
186
     */
187
    protected function nestingTag($match, $state, $pos, $name) {
188
        switch ( $state ) {
189
            case DOKU_LEXER_ENTER:
190
                $this->addCall($name.'_open', array(), $pos);
191
                break;
192
            case DOKU_LEXER_EXIT:
193
                $this->addCall($name.'_close', array(), $pos);
194
                break;
195
            case DOKU_LEXER_UNMATCHED:
196
                $this->addCall('cdata', array($match), $pos);
197
                break;
198
        }
199
    }
200
201
202
    /**
203
     * The following methods define the handlers for the different Syntax modes
204
     *
205
     * The handlers are called from dokuwiki\Lexer\Lexer\invokeParser()
206
     *
207
     * @todo it might make sense to move these into their own class or merge them with the
208
     *       ParserMode classes some time.
209
     */
210
    // region mode handlers
211
212
    /**
213
     * Special plugin handler
214
     *
215
     * This handler is called for all modes starting with 'plugin_'.
216
     * An additional parameter with the plugin name is passed. The plugin's handle()
217
     * method is called here
218
     *
219
     * @author Andreas Gohr <[email protected]>
220
     *
221
     * @param string $match matched syntax
222
     * @param int $state a LEXER_STATE_* constant
223
     * @param int $pos byte position in the original source file
224
     * @param string $pluginname name of the plugin
225
     * @return bool mode handled?
226
     */
227
    public function plugin($match, $state, $pos, $pluginname){
228
        $data = array($match);
229
        /** @var DokuWiki_Syntax_Plugin $plugin */
230
        $plugin = plugin_load('syntax',$pluginname);
231
        if($plugin != null){
232
            $data = $plugin->handle($match, $state, $pos, $this);
233
        }
234
        if ($data !== false) {
235
            $this->addPluginCall($pluginname,$data,$state,$pos,$match);
236
        }
237
        return true;
238
    }
239
240
    /**
241
     * @param string $match matched syntax
242
     * @param int $state a LEXER_STATE_* constant
243
     * @param int $pos byte position in the original source file
244
     * @return bool mode handled?
245
     */
246
    public function base($match, $state, $pos) {
247
        switch ( $state ) {
248
            case DOKU_LEXER_UNMATCHED:
249
                $this->addCall('cdata', array($match), $pos);
250
                return true;
251
            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...
252
        }
253
        return false;
254
    }
255
256
    /**
257
     * @param string $match matched syntax
258
     * @param int $state a LEXER_STATE_* constant
259
     * @param int $pos byte position in the original source file
260
     * @return bool mode handled?
261
     */
262
    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...
263
        // get level and title
264
        $title = trim($match);
265
        $level = 7 - strspn($title,'=');
266
        if($level < 1) $level = 1;
267
        $title = trim($title,'=');
268
        $title = trim($title);
269
270
        if ($this->status['section']) $this->addCall('section_close', array(), $pos);
271
272
        $this->addCall('header', array($title, $level, $pos), $pos);
273
274
        $this->addCall('section_open', array($level), $pos);
275
        $this->status['section'] = true;
276
        return true;
277
    }
278
279
    /**
280
     * @param string $match matched syntax
281
     * @param int $state a LEXER_STATE_* constant
282
     * @param int $pos byte position in the original source file
283
     * @return bool mode handled?
284
     */
285
    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...
286
        $this->addCall('notoc', array(), $pos);
287
        return true;
288
    }
289
290
    /**
291
     * @param string $match matched syntax
292
     * @param int $state a LEXER_STATE_* constant
293
     * @param int $pos byte position in the original source file
294
     * @return bool mode handled?
295
     */
296
    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...
297
        $this->addCall('nocache', array(), $pos);
298
        return true;
299
    }
300
301
    /**
302
     * @param string $match matched syntax
303
     * @param int $state a LEXER_STATE_* constant
304
     * @param int $pos byte position in the original source file
305
     * @return bool mode handled?
306
     */
307
    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...
308
        $this->addCall('linebreak', array(), $pos);
309
        return true;
310
    }
311
312
    /**
313
     * @param string $match matched syntax
314
     * @param int $state a LEXER_STATE_* constant
315
     * @param int $pos byte position in the original source file
316
     * @return bool mode handled?
317
     */
318
    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...
319
        $this->addCall('eol', array(), $pos);
320
        return true;
321
    }
322
323
    /**
324
     * @param string $match matched syntax
325
     * @param int $state a LEXER_STATE_* constant
326
     * @param int $pos byte position in the original source file
327
     * @return bool mode handled?
328
     */
329
    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...
330
        $this->addCall('hr', array(), $pos);
331
        return true;
332
    }
333
334
    /**
335
     * @param string $match matched syntax
336
     * @param int $state a LEXER_STATE_* constant
337
     * @param int $pos byte position in the original source file
338
     * @return bool mode handled?
339
     */
340
    public function strong($match, $state, $pos) {
341
        $this->nestingTag($match, $state, $pos, 'strong');
342
        return true;
343
    }
344
345
    /**
346
     * @param string $match matched syntax
347
     * @param int $state a LEXER_STATE_* constant
348
     * @param int $pos byte position in the original source file
349
     * @return bool mode handled?
350
     */
351
    public function emphasis($match, $state, $pos) {
352
        $this->nestingTag($match, $state, $pos, 'emphasis');
353
        return true;
354
    }
355
356
    /**
357
     * @param string $match matched syntax
358
     * @param int $state a LEXER_STATE_* constant
359
     * @param int $pos byte position in the original source file
360
     * @return bool mode handled?
361
     */
362
    public function underline($match, $state, $pos) {
363
        $this->nestingTag($match, $state, $pos, 'underline');
364
        return true;
365
    }
366
367
    /**
368
     * @param string $match matched syntax
369
     * @param int $state a LEXER_STATE_* constant
370
     * @param int $pos byte position in the original source file
371
     * @return bool mode handled?
372
     */
373
    public function monospace($match, $state, $pos) {
374
        $this->nestingTag($match, $state, $pos, 'monospace');
375
        return true;
376
    }
377
378
    /**
379
     * @param string $match matched syntax
380
     * @param int $state a LEXER_STATE_* constant
381
     * @param int $pos byte position in the original source file
382
     * @return bool mode handled?
383
     */
384
    public function subscript($match, $state, $pos) {
385
        $this->nestingTag($match, $state, $pos, 'subscript');
386
        return true;
387
    }
388
389
    /**
390
     * @param string $match matched syntax
391
     * @param int $state a LEXER_STATE_* constant
392
     * @param int $pos byte position in the original source file
393
     * @return bool mode handled?
394
     */
395
    public function superscript($match, $state, $pos) {
396
        $this->nestingTag($match, $state, $pos, 'superscript');
397
        return true;
398
    }
399
400
    /**
401
     * @param string $match matched syntax
402
     * @param int $state a LEXER_STATE_* constant
403
     * @param int $pos byte position in the original source file
404
     * @return bool mode handled?
405
     */
406
    public function deleted($match, $state, $pos) {
407
        $this->nestingTag($match, $state, $pos, 'deleted');
408
        return true;
409
    }
410
411
    /**
412
     * @param string $match matched syntax
413
     * @param int $state a LEXER_STATE_* constant
414
     * @param int $pos byte position in the original source file
415
     * @return bool mode handled?
416
     */
417
    public function footnote($match, $state, $pos) {
418
        if (!isset($this->_footnote)) $this->_footnote = false;
419
420
        switch ( $state ) {
421
            case DOKU_LEXER_ENTER:
422
                // footnotes can not be nested - however due to limitations in lexer it can't be prevented
423
                // we will still enter a new footnote mode, we just do nothing
424
                if ($this->_footnote) {
425
                    $this->addCall('cdata', array($match), $pos);
426
                    break;
427
                }
428
                $this->_footnote = true;
429
430
                $this->callWriter = new Nest($this->callWriter, 'footnote_close');
431
                $this->addCall('footnote_open', array(), $pos);
432
            break;
433
            case DOKU_LEXER_EXIT:
434
                // check whether we have already exitted the footnote mode, can happen if the modes were nested
435
                if (!$this->_footnote) {
436
                    $this->addCall('cdata', array($match), $pos);
437
                    break;
438
                }
439
440
                $this->_footnote = false;
441
                $this->addCall('footnote_close', array(), $pos);
442
443
                /** @var Nest $reWriter */
444
                $reWriter = $this->callWriter;
445
                $this->callWriter = $reWriter->process();
446
            break;
447
            case DOKU_LEXER_UNMATCHED:
448
                $this->addCall('cdata', array($match), $pos);
449
            break;
450
        }
451
        return true;
452
    }
453
454
    /**
455
     * @param string $match matched syntax
456
     * @param int $state a LEXER_STATE_* constant
457
     * @param int $pos byte position in the original source file
458
     * @return bool mode handled?
459
     */
460
    public function listblock($match, $state, $pos) {
461
        switch ( $state ) {
462
            case DOKU_LEXER_ENTER:
463
                $this->callWriter = new Lists($this->callWriter);
464
                $this->addCall('list_open', array($match), $pos);
465
            break;
466
            case DOKU_LEXER_EXIT:
467
                $this->addCall('list_close', array(), $pos);
468
                /** @var Lists $reWriter */
469
                $reWriter = $this->callWriter;
470
                $this->callWriter = $reWriter->process();
471
            break;
472
            case DOKU_LEXER_MATCHED:
473
                $this->addCall('list_item', array($match), $pos);
474
            break;
475
            case DOKU_LEXER_UNMATCHED:
476
                $this->addCall('cdata', array($match), $pos);
477
            break;
478
        }
479
        return true;
480
    }
481
482
    /**
483
     * @param string $match matched syntax
484
     * @param int $state a LEXER_STATE_* constant
485
     * @param int $pos byte position in the original source file
486
     * @return bool mode handled?
487
     */
488
    public function unformatted($match, $state, $pos) {
489
        if ( $state == DOKU_LEXER_UNMATCHED ) {
490
            $this->addCall('unformatted', array($match), $pos);
491
        }
492
        return true;
493
    }
494
495
    /**
496
     * @param string $match matched syntax
497
     * @param int $state a LEXER_STATE_* constant
498
     * @param int $pos byte position in the original source file
499
     * @return bool mode handled?
500
     */
501
    public function php($match, $state, $pos) {
502
        if ( $state == DOKU_LEXER_UNMATCHED ) {
503
            $this->addCall('php', array($match), $pos);
504
        }
505
        return true;
506
    }
507
508
    /**
509
     * @param string $match matched syntax
510
     * @param int $state a LEXER_STATE_* constant
511
     * @param int $pos byte position in the original source file
512
     * @return bool mode handled?
513
     */
514
    public function phpblock($match, $state, $pos) {
515
        if ( $state == DOKU_LEXER_UNMATCHED ) {
516
            $this->addCall('phpblock', array($match), $pos);
517
        }
518
        return true;
519
    }
520
521
    /**
522
     * @param string $match matched syntax
523
     * @param int $state a LEXER_STATE_* constant
524
     * @param int $pos byte position in the original source file
525
     * @return bool mode handled?
526
     */
527
    public function html($match, $state, $pos) {
528
        if ( $state == DOKU_LEXER_UNMATCHED ) {
529
            $this->addCall('html', array($match), $pos);
530
        }
531
        return true;
532
    }
533
534
    /**
535
     * @param string $match matched syntax
536
     * @param int $state a LEXER_STATE_* constant
537
     * @param int $pos byte position in the original source file
538
     * @return bool mode handled?
539
     */
540
    public function htmlblock($match, $state, $pos) {
541
        if ( $state == DOKU_LEXER_UNMATCHED ) {
542
            $this->addCall('htmlblock', array($match), $pos);
543
        }
544
        return true;
545
    }
546
547
    /**
548
     * @param string $match matched syntax
549
     * @param int $state a LEXER_STATE_* constant
550
     * @param int $pos byte position in the original source file
551
     * @return bool mode handled?
552
     */
553
    public function preformatted($match, $state, $pos) {
554
        switch ( $state ) {
555
            case DOKU_LEXER_ENTER:
556
                $this->callWriter = new Preformatted($this->callWriter);
557
                $this->addCall('preformatted_start', array(), $pos);
558
            break;
559
            case DOKU_LEXER_EXIT:
560
                $this->addCall('preformatted_end', array(), $pos);
561
                /** @var Preformatted $reWriter */
562
                $reWriter = $this->callWriter;
563
                $this->callWriter = $reWriter->process();
564
            break;
565
            case DOKU_LEXER_MATCHED:
566
                $this->addCall('preformatted_newline', array(), $pos);
567
            break;
568
            case DOKU_LEXER_UNMATCHED:
569
                $this->addCall('preformatted_content', array($match), $pos);
570
            break;
571
        }
572
573
        return true;
574
    }
575
576
    /**
577
     * @param string $match matched syntax
578
     * @param int $state a LEXER_STATE_* constant
579
     * @param int $pos byte position in the original source file
580
     * @return bool mode handled?
581
     */
582
    public function quote($match, $state, $pos) {
583
584
        switch ( $state ) {
585
586
            case DOKU_LEXER_ENTER:
587
                $this->callWriter = new Quote($this->callWriter);
588
                $this->addCall('quote_start', array($match), $pos);
589
            break;
590
591
            case DOKU_LEXER_EXIT:
592
                $this->addCall('quote_end', array(), $pos);
593
                /** @var Lists $reWriter */
594
                $reWriter = $this->callWriter;
595
                $this->callWriter = $reWriter->process();
596
            break;
597
598
            case DOKU_LEXER_MATCHED:
599
                $this->addCall('quote_newline', array($match), $pos);
600
            break;
601
602
            case DOKU_LEXER_UNMATCHED:
603
                $this->addCall('cdata', array($match), $pos);
604
            break;
605
606
        }
607
608
        return true;
609
    }
610
611
    /**
612
     * @param string $match matched syntax
613
     * @param int $state a LEXER_STATE_* constant
614
     * @param int $pos byte position in the original source file
615
     * @return bool mode handled?
616
     */
617
    public function file($match, $state, $pos) {
618
        return $this->code($match, $state, $pos, 'file');
619
    }
620
621
    /**
622
     * @param string $match matched syntax
623
     * @param int $state a LEXER_STATE_* constant
624
     * @param int $pos byte position in the original source file
625
     * @param string $type either 'code' or 'file'
626
     * @return bool mode handled?
627
     */
628
    public function code($match, $state, $pos, $type='code') {
629
        if ( $state == DOKU_LEXER_UNMATCHED ) {
630
            $matches = explode('>',$match,2);
631
            // Cut out variable options enclosed in []
632
            preg_match('/\[.*\]/', $matches[0], $options);
633
            if (!empty($options[0])) {
634
                $matches[0] = str_replace($options[0], '', $matches[0]);
635
            }
636
            $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY);
637
            while(count($param) < 2) array_push($param, null);
638
            // We shortcut html here.
639
            if ($param[0] == 'html') $param[0] = 'html4strict';
640
            if ($param[0] == '-') $param[0] = null;
641
            array_unshift($param, $matches[1]);
642
            if (!empty($options[0])) {
643
                $param [] = $this->parse_highlight_options ($options[0]);
644
            }
645
            $this->addCall($type, $param, $pos);
646
        }
647
        return true;
648
    }
649
650
    /**
651
     * @param string $match matched syntax
652
     * @param int $state a LEXER_STATE_* constant
653
     * @param int $pos byte position in the original source file
654
     * @return bool mode handled?
655
     */
656
    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...
657
        $this->addCall('acronym', array($match), $pos);
658
        return true;
659
    }
660
661
    /**
662
     * @param string $match matched syntax
663
     * @param int $state a LEXER_STATE_* constant
664
     * @param int $pos byte position in the original source file
665
     * @return bool mode handled?
666
     */
667
    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...
668
        $this->addCall('smiley', array($match), $pos);
669
        return true;
670
    }
671
672
    /**
673
     * @param string $match matched syntax
674
     * @param int $state a LEXER_STATE_* constant
675
     * @param int $pos byte position in the original source file
676
     * @return bool mode handled?
677
     */
678
    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...
679
        $this->addCall('wordblock', array($match), $pos);
680
        return true;
681
    }
682
683
    /**
684
     * @param string $match matched syntax
685
     * @param int $state a LEXER_STATE_* constant
686
     * @param int $pos byte position in the original source file
687
     * @return bool mode handled?
688
     */
689
    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...
690
        $this->addCall('entity', array($match), $pos);
691
        return true;
692
    }
693
694
    /**
695
     * @param string $match matched syntax
696
     * @param int $state a LEXER_STATE_* constant
697
     * @param int $pos byte position in the original source file
698
     * @return bool mode handled?
699
     */
700
    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...
701
        preg_match_all('/\d+/',$match,$matches);
702
        $this->addCall('multiplyentity', array($matches[0][0], $matches[0][1]), $pos);
703
        return true;
704
    }
705
706
    /**
707
     * @param string $match matched syntax
708
     * @param int $state a LEXER_STATE_* constant
709
     * @param int $pos byte position in the original source file
710
     * @return bool mode handled?
711
     */
712
    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...
713
        $this->addCall('singlequoteopening', array(), $pos);
714
        return true;
715
    }
716
717
    /**
718
     * @param string $match matched syntax
719
     * @param int $state a LEXER_STATE_* constant
720
     * @param int $pos byte position in the original source file
721
     * @return bool mode handled?
722
     */
723
    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...
724
        $this->addCall('singlequoteclosing', array(), $pos);
725
        return true;
726
    }
727
728
    /**
729
     * @param string $match matched syntax
730
     * @param int $state a LEXER_STATE_* constant
731
     * @param int $pos byte position in the original source file
732
     * @return bool mode handled?
733
     */
734
    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...
735
        $this->addCall('apostrophe', array(), $pos);
736
        return true;
737
    }
738
739
    /**
740
     * @param string $match matched syntax
741
     * @param int $state a LEXER_STATE_* constant
742
     * @param int $pos byte position in the original source file
743
     * @return bool mode handled?
744
     */
745
    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...
746
        $this->addCall('doublequoteopening', array(), $pos);
747
        $this->status['doublequote']++;
748
        return true;
749
    }
750
751
    /**
752
     * @param string $match matched syntax
753
     * @param int $state a LEXER_STATE_* constant
754
     * @param int $pos byte position in the original source file
755
     * @return bool mode handled?
756
     */
757
    public function doublequoteclosing($match, $state, $pos) {
758
        if ($this->status['doublequote'] <= 0) {
759
            $this->doublequoteopening($match, $state, $pos);
760
        } else {
761
            $this->addCall('doublequoteclosing', array(), $pos);
762
            $this->status['doublequote'] = max(0, --$this->status['doublequote']);
763
        }
764
        return true;
765
    }
766
767
    /**
768
     * @param string $match matched syntax
769
     * @param int $state a LEXER_STATE_* constant
770
     * @param int $pos byte position in the original source file
771
     * @return bool mode handled?
772
     */
773
    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...
774
        $this->addCall('camelcaselink', array($match), $pos);
775
        return true;
776
    }
777
778
    /**
779
     * @param string $match matched syntax
780
     * @param int $state a LEXER_STATE_* constant
781
     * @param int $pos byte position in the original source file
782
     * @return bool mode handled?
783
     */
784
    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...
785
        // Strip the opening and closing markup
786
        $link = preg_replace(array('/^\[\[/','/\]\]$/u'),'',$match);
787
788
        // Split title from URL
789
        $link = explode('|',$link,2);
790
        if ( !isset($link[1]) ) {
791
            $link[1] = null;
792
        } else if ( preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
793
            // If the title is an image, convert it to an array containing the image details
794
            $link[1] = Doku_Handler_Parse_Media($link[1]);
795
        }
796
        $link[0] = trim($link[0]);
797
798
        //decide which kind of link it is
799
800
        if ( link_isinterwiki($link[0]) ) {
801
            // Interwiki
802
            $interwiki = explode('>',$link[0],2);
803
            $this->addCall(
804
                'interwikilink',
805
                array($link[0],$link[1],strtolower($interwiki[0]),$interwiki[1]),
806
                $pos
807
                );
808
        }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) {
809
            // Windows Share
810
            $this->addCall(
811
                'windowssharelink',
812
                array($link[0],$link[1]),
813
                $pos
814
                );
815
        }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) {
816
            // external link (accepts all protocols)
817
            $this->addCall(
818
                    'externallink',
819
                    array($link[0],$link[1]),
820
                    $pos
821
                    );
822
        }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) {
823
            // E-Mail (pattern above is defined in inc/mail.php)
824
            $this->addCall(
825
                'emaillink',
826
                array($link[0],$link[1]),
827
                $pos
828
                );
829
        }elseif ( preg_match('!^#.+!',$link[0]) ){
830
            // local link
831
            $this->addCall(
832
                'locallink',
833
                array(substr($link[0],1),$link[1]),
834
                $pos
835
                );
836
        }else{
837
            // internal link
838
            $this->addCall(
839
                'internallink',
840
                array($link[0],$link[1]),
841
                $pos
842
                );
843
        }
844
845
        return true;
846
    }
847
848
    /**
849
     * @param string $match matched syntax
850
     * @param int $state a LEXER_STATE_* constant
851
     * @param int $pos byte position in the original source file
852
     * @return bool mode handled?
853
     */
854
    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...
855
        $this->addCall('filelink', array($match, null), $pos);
856
        return true;
857
    }
858
859
    /**
860
     * @param string $match matched syntax
861
     * @param int $state a LEXER_STATE_* constant
862
     * @param int $pos byte position in the original source file
863
     * @return bool mode handled?
864
     */
865
    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...
866
        $this->addCall('windowssharelink', array($match, null), $pos);
867
        return true;
868
    }
869
870
    /**
871
     * @param string $match matched syntax
872
     * @param int $state a LEXER_STATE_* constant
873
     * @param int $pos byte position in the original source file
874
     * @return bool mode handled?
875
     */
876
    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...
877
        $p = Doku_Handler_Parse_Media($match);
878
879
        $this->addCall(
880
              $p['type'],
881
              array($p['src'], $p['title'], $p['align'], $p['width'],
882
                     $p['height'], $p['cache'], $p['linking']),
883
              $pos
884
             );
885
        return true;
886
    }
887
888
    /**
889
     * @param string $match matched syntax
890
     * @param int $state a LEXER_STATE_* constant
891
     * @param int $pos byte position in the original source file
892
     * @return bool mode handled?
893
     */
894
    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...
895
        $link = preg_replace(array('/^\{\{rss>/','/\}\}$/'),'',$match);
896
897
        // get params
898
        list($link,$params) = explode(' ',$link,2);
899
900
        $p = array();
901
        if(preg_match('/\b(\d+)\b/',$params,$match)){
902
            $p['max'] = $match[1];
903
        }else{
904
            $p['max'] = 8;
905
        }
906
        $p['reverse'] = (preg_match('/rev/',$params));
907
        $p['author']  = (preg_match('/\b(by|author)/',$params));
908
        $p['date']    = (preg_match('/\b(date)/',$params));
909
        $p['details'] = (preg_match('/\b(desc|detail)/',$params));
910
        $p['nosort']  = (preg_match('/\b(nosort)\b/',$params));
911
912
        if (preg_match('/\b(\d+)([dhm])\b/',$params,$match)) {
913
            $period = array('d' => 86400, 'h' => 3600, 'm' => 60);
914
            $p['refresh'] = max(600,$match[1]*$period[$match[2]]);  // n * period in seconds, minimum 10 minutes
915
        } else {
916
            $p['refresh'] = 14400;   // default to 4 hours
917
        }
918
919
        $this->addCall('rss', array($link, $p), $pos);
920
        return true;
921
    }
922
923
    /**
924
     * @param string $match matched syntax
925
     * @param int $state a LEXER_STATE_* constant
926
     * @param int $pos byte position in the original source file
927
     * @return bool mode handled?
928
     */
929
    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...
930
        $url   = $match;
931
        $title = null;
932
933
        // add protocol on simple short URLs
934
        if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')){
935
            $title = $url;
936
            $url   = 'ftp://'.$url;
937
        }
938
        if(substr($url,0,3) == 'www' && (substr($url,0,7) != 'http://')){
939
            $title = $url;
940
            $url = 'http://'.$url;
941
        }
942
943
        $this->addCall('externallink', array($url, $title), $pos);
944
        return true;
945
    }
946
947
    /**
948
     * @param string $match matched syntax
949
     * @param int $state a LEXER_STATE_* constant
950
     * @param int $pos byte position in the original source file
951
     * @return bool mode handled?
952
     */
953
    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...
954
        $email = preg_replace(array('/^</','/>$/'),'',$match);
955
        $this->addCall('emaillink', array($email, null), $pos);
956
        return true;
957
    }
958
959
    /**
960
     * @param string $match matched syntax
961
     * @param int $state a LEXER_STATE_* constant
962
     * @param int $pos byte position in the original source file
963
     * @return bool mode handled?
964
     */
965
    public function table($match, $state, $pos) {
966
        switch ( $state ) {
967
968
            case DOKU_LEXER_ENTER:
969
970
                $this->callWriter = new Table($this->callWriter);
971
972
                $this->addCall('table_start', array($pos + 1), $pos);
973
                if ( trim($match) == '^' ) {
974
                    $this->addCall('tableheader', array(), $pos);
975
                } else {
976
                    $this->addCall('tablecell', array(), $pos);
977
                }
978
            break;
979
980
            case DOKU_LEXER_EXIT:
981
                $this->addCall('table_end', array($pos), $pos);
982
                /** @var Table $reWriter */
983
                $reWriter = $this->callWriter;
984
                $this->callWriter = $reWriter->process();
985
            break;
986
987
            case DOKU_LEXER_UNMATCHED:
988
                if ( trim($match) != '' ) {
989
                    $this->addCall('cdata', array($match), $pos);
990
                }
991
            break;
992
993
            case DOKU_LEXER_MATCHED:
994
                if ( $match == ' ' ){
995
                    $this->addCall('cdata', array($match), $pos);
996
                } else if ( preg_match('/:::/',$match) ) {
997
                    $this->addCall('rowspan', array($match), $pos);
998
                } else if ( preg_match('/\t+/',$match) ) {
999
                    $this->addCall('table_align', array($match), $pos);
1000
                } else if ( preg_match('/ {2,}/',$match) ) {
1001
                    $this->addCall('table_align', array($match), $pos);
1002
                } else if ( $match == "\n|" ) {
1003
                    $this->addCall('table_row', array(), $pos);
1004
                    $this->addCall('tablecell', array(), $pos);
1005
                } else if ( $match == "\n^" ) {
1006
                    $this->addCall('table_row', array(), $pos);
1007
                    $this->addCall('tableheader', array(), $pos);
1008
                } else if ( $match == '|' ) {
1009
                    $this->addCall('tablecell', array(), $pos);
1010
                } else if ( $match == '^' ) {
1011
                    $this->addCall('tableheader', array(), $pos);
1012
                }
1013
            break;
1014
        }
1015
        return true;
1016
    }
1017
1018
    // endregion modes
1019
}
1020
1021
//------------------------------------------------------------------------
1022
function Doku_Handler_Parse_Media($match) {
1023
1024
    // Strip the opening and closing markup
1025
    $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match);
1026
1027
    // Split title from URL
1028
    $link = explode('|',$link,2);
1029
1030
    // Check alignment
1031
    $ralign = (bool)preg_match('/^ /',$link[0]);
1032
    $lalign = (bool)preg_match('/ $/',$link[0]);
1033
1034
    // Logic = what's that ;)...
1035
    if ( $lalign & $ralign ) {
1036
        $align = 'center';
1037
    } else if ( $ralign ) {
1038
        $align = 'right';
1039
    } else if ( $lalign ) {
1040
        $align = 'left';
1041
    } else {
1042
        $align = null;
1043
    }
1044
1045
    // The title...
1046
    if ( !isset($link[1]) ) {
1047
        $link[1] = null;
1048
    }
1049
1050
    //remove aligning spaces
1051
    $link[0] = trim($link[0]);
1052
1053
    //split into src and parameters (using the very last questionmark)
1054
    $pos = strrpos($link[0], '?');
1055
    if($pos !== false){
1056
        $src   = substr($link[0],0,$pos);
1057
        $param = substr($link[0],$pos+1);
1058
    }else{
1059
        $src   = $link[0];
1060
        $param = '';
1061
    }
1062
1063
    //parse width and height
1064
    if(preg_match('#(\d+)(x(\d+))?#i',$param,$size)){
1065
        !empty($size[1]) ? $w = $size[1] : $w = null;
1066
        !empty($size[3]) ? $h = $size[3] : $h = null;
1067
    } else {
1068
        $w = null;
1069
        $h = null;
1070
    }
1071
1072
    //get linking command
1073
    if(preg_match('/nolink/i',$param)){
1074
        $linking = 'nolink';
1075
    }else if(preg_match('/direct/i',$param)){
1076
        $linking = 'direct';
1077
    }else if(preg_match('/linkonly/i',$param)){
1078
        $linking = 'linkonly';
1079
    }else{
1080
        $linking = 'details';
1081
    }
1082
1083
    //get caching command
1084
    if (preg_match('/(nocache|recache)/i',$param,$cachemode)){
1085
        $cache = $cachemode[1];
1086
    }else{
1087
        $cache = 'cache';
1088
    }
1089
1090
    // Check whether this is a local or remote image or interwiki
1091
    if (media_isexternal($src) || link_isinterwiki($src)){
1092
        $call = 'externalmedia';
1093
    } else {
1094
        $call = 'internalmedia';
1095
    }
1096
1097
    $params = array(
1098
        'type'=>$call,
1099
        'src'=>$src,
1100
        'title'=>$link[1],
1101
        'align'=>$align,
1102
        'width'=>$w,
1103
        'height'=>$h,
1104
        'cache'=>$cache,
1105
        'linking'=>$linking,
1106
    );
1107
1108
    return $params;
1109
}
1110
1111