Failed Conditions
Push — psr2-config ( c6639e )
by Andreas
03:31
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\Parsing\Handler\Block;
4
use dokuwiki\Parsing\Handler\CallWriter;
5
use dokuwiki\Parsing\Handler\CallWriterInterface;
6
use dokuwiki\Parsing\Handler\Lists;
7
use dokuwiki\Parsing\Handler\Nest;
8
use dokuwiki\Parsing\Handler\Preformatted;
9
use dokuwiki\Parsing\Handler\Quote;
10
use dokuwiki\Parsing\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
    public function __construct() {
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\Parsing\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