Failed Conditions
Push — psr2 ( 64159a )
by Andreas
07:54 queued 04:15
created

Doku_Handler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
if (!defined('DOKU_PARSER_EOL')) define('DOKU_PARSER_EOL',"\n");   // add this to make handling test cases simpler
3
4
class Doku_Handler {
5
6
    var $Renderer = null;
7
8
    var $CallWriter = null;
9
10
    var $calls = array();
11
12
    var $status = array(
13
        'section' => false,
14
        'doublequote' => 0,
15
    );
16
17
    var $rewriteBlocks = true;
18
19
    function __construct() {
0 ignored issues
show
Best Practice introduced by
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...
20
        $this->CallWriter = new Doku_Handler_CallWriter($this);
21
    }
22
23
    /**
24
     * @param string $handler
25
     * @param mixed $args
26
     * @param integer|string $pos
27
     */
28
    function _addCall($handler, $args, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
29
        $call = array($handler,$args, $pos);
30
        $this->CallWriter->writeCall($call);
31
    }
32
33
    function addPluginCall($plugin, $args, $state, $pos, $match) {
0 ignored issues
show
Best Practice introduced by
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...
34
        $call = array('plugin',array($plugin, $args, $state, $match), $pos);
35
        $this->CallWriter->writeCall($call);
36
    }
37
38
    function _finalize(){
0 ignored issues
show
Best Practice introduced by
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...
39
40
        $this->CallWriter->finalise();
41
42
        if ( $this->status['section'] ) {
43
            $last_call = end($this->calls);
44
            array_push($this->calls,array('section_close',array(), $last_call[2]));
45
        }
46
47
        if ( $this->rewriteBlocks ) {
48
            $B = new Doku_Handler_Block();
49
            $this->calls = $B->process($this->calls);
50
        }
51
52
        trigger_event('PARSER_HANDLER_DONE',$this);
53
54
        array_unshift($this->calls,array('document_start',array(),0));
55
        $last_call = end($this->calls);
56
        array_push($this->calls,array('document_end',array(),$last_call[2]));
57
    }
58
59
    /**
60
     * fetch the current call and advance the pointer to the next one
61
     *
62
     * @return bool|mixed
63
     */
64
    function fetch() {
0 ignored issues
show
Best Practice introduced by
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...
65
        $call = current($this->calls);
66
        if($call !== false) {
67
            next($this->calls); //advance the pointer
68
            return $call;
69
        }
70
        return false;
71
    }
72
73
74
    /**
75
     * Special plugin handler
76
     *
77
     * This handler is called for all modes starting with 'plugin_'.
78
     * An additional parameter with the plugin name is passed
79
     *
80
     * @author Andreas Gohr <[email protected]>
81
     *
82
     * @param string|integer $match
83
     * @param string|integer $state
84
     * @param integer $pos
85
     * @param $pluginname
86
     *
87
     * @return bool
88
     */
89
    function plugin($match, $state, $pos, $pluginname){
0 ignored issues
show
Best Practice introduced by
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...
90
        $data = array($match);
91
        /** @var DokuWiki_Syntax_Plugin $plugin */
92
        $plugin = plugin_load('syntax',$pluginname);
93
        if($plugin != null){
94
            $data = $plugin->handle($match, $state, $pos, $this);
95
        }
96
        if ($data !== false) {
97
            $this->addPluginCall($pluginname,$data,$state,$pos,$match);
98
        }
99
        return true;
100
    }
101
102
    function base($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
103
        switch ( $state ) {
104
            case DOKU_LEXER_UNMATCHED:
105
                $this->_addCall('cdata',array($match), $pos);
106
                return true;
107
            break;
0 ignored issues
show
Unused Code introduced by
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...
108
        }
109
    }
110
111
    function header($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
112
        // get level and title
113
        $title = trim($match);
114
        $level = 7 - strspn($title,'=');
115
        if($level < 1) $level = 1;
116
        $title = trim($title,'=');
117
        $title = trim($title);
118
119
        if ($this->status['section']) $this->_addCall('section_close',array(),$pos);
120
121
        $this->_addCall('header',array($title,$level,$pos), $pos);
122
123
        $this->_addCall('section_open',array($level),$pos);
124
        $this->status['section'] = true;
125
        return true;
126
    }
127
128
    function notoc($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
129
        $this->_addCall('notoc',array(),$pos);
130
        return true;
131
    }
132
133
    function nocache($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
134
        $this->_addCall('nocache',array(),$pos);
135
        return true;
136
    }
137
138
    function linebreak($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
139
        $this->_addCall('linebreak',array(),$pos);
140
        return true;
141
    }
142
143
    function eol($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
144
        $this->_addCall('eol',array(),$pos);
145
        return true;
146
    }
147
148
    function hr($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
149
        $this->_addCall('hr',array(),$pos);
150
        return true;
151
    }
152
153
    /**
154
     * @param string|integer $match
155
     * @param string|integer $state
156
     * @param integer $pos
157
     * @param string $name
158
     */
159
    function _nestingTag($match, $state, $pos, $name) {
0 ignored issues
show
Best Practice introduced by
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...
160
        switch ( $state ) {
161
            case DOKU_LEXER_ENTER:
162
                $this->_addCall($name.'_open', array(), $pos);
163
            break;
164
            case DOKU_LEXER_EXIT:
165
                $this->_addCall($name.'_close', array(), $pos);
166
            break;
167
            case DOKU_LEXER_UNMATCHED:
168
                $this->_addCall('cdata',array($match), $pos);
169
            break;
170
        }
171
    }
172
173
    function strong($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
174
        $this->_nestingTag($match, $state, $pos, 'strong');
175
        return true;
176
    }
177
178
    function emphasis($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
179
        $this->_nestingTag($match, $state, $pos, 'emphasis');
180
        return true;
181
    }
182
183
    function underline($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
184
        $this->_nestingTag($match, $state, $pos, 'underline');
185
        return true;
186
    }
187
188
    function monospace($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
189
        $this->_nestingTag($match, $state, $pos, 'monospace');
190
        return true;
191
    }
192
193
    function subscript($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
194
        $this->_nestingTag($match, $state, $pos, 'subscript');
195
        return true;
196
    }
197
198
    function superscript($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
199
        $this->_nestingTag($match, $state, $pos, 'superscript');
200
        return true;
201
    }
202
203
    function deleted($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
204
        $this->_nestingTag($match, $state, $pos, 'deleted');
205
        return true;
206
    }
207
208
209
    function footnote($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
210
//        $this->_nestingTag($match, $state, $pos, 'footnote');
211
        if (!isset($this->_footnote)) $this->_footnote = false;
0 ignored issues
show
Bug introduced by
The property _footnote does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
212
213
        switch ( $state ) {
214
            case DOKU_LEXER_ENTER:
215
                // footnotes can not be nested - however due to limitations in lexer it can't be prevented
216
                // we will still enter a new footnote mode, we just do nothing
217
                if ($this->_footnote) {
218
                    $this->_addCall('cdata',array($match), $pos);
219
                    break;
220
                }
221
222
                $this->_footnote = true;
223
224
                $ReWriter = new Doku_Handler_Nest($this->CallWriter,'footnote_close');
225
                $this->CallWriter = & $ReWriter;
226
                $this->_addCall('footnote_open', array(), $pos);
227
            break;
228
            case DOKU_LEXER_EXIT:
229
                // check whether we have already exitted the footnote mode, can happen if the modes were nested
230
                if (!$this->_footnote) {
231
                    $this->_addCall('cdata',array($match), $pos);
232
                    break;
233
                }
234
235
                $this->_footnote = false;
236
237
                $this->_addCall('footnote_close', array(), $pos);
238
                $this->CallWriter->process();
0 ignored issues
show
Bug introduced by
The method process() does not seem to exist on object<Doku_Handler_CallWriter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
239
                $ReWriter = & $this->CallWriter;
240
                $this->CallWriter = & $ReWriter->CallWriter;
0 ignored issues
show
Bug introduced by
The property CallWriter does not seem to exist in Doku_Handler_CallWriter.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
241
            break;
242
            case DOKU_LEXER_UNMATCHED:
243
                $this->_addCall('cdata', array($match), $pos);
244
            break;
245
        }
246
        return true;
247
    }
248
249
    function listblock($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
250
        switch ( $state ) {
251
            case DOKU_LEXER_ENTER:
252
                $ReWriter = new Doku_Handler_List($this->CallWriter);
253
                $this->CallWriter = & $ReWriter;
254
                $this->_addCall('list_open', array($match), $pos);
255
            break;
256
            case DOKU_LEXER_EXIT:
257
                $this->_addCall('list_close', array(), $pos);
258
                $this->CallWriter->process();
0 ignored issues
show
Bug introduced by
The method process does only exist in Doku_Handler_Nest, but not in Doku_Handler_CallWriter.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
259
                $ReWriter = & $this->CallWriter;
260
                $this->CallWriter = & $ReWriter->CallWriter;
261
            break;
262
            case DOKU_LEXER_MATCHED:
263
                $this->_addCall('list_item', array($match), $pos);
264
            break;
265
            case DOKU_LEXER_UNMATCHED:
266
                $this->_addCall('cdata', array($match), $pos);
267
            break;
268
        }
269
        return true;
270
    }
271
272
    function unformatted($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
273
        if ( $state == DOKU_LEXER_UNMATCHED ) {
274
            $this->_addCall('unformatted',array($match), $pos);
275
        }
276
        return true;
277
    }
278
279
    function php($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
280
        global $conf;
281
        if ( $state == DOKU_LEXER_UNMATCHED ) {
282
            $this->_addCall('php',array($match), $pos);
283
        }
284
        return true;
285
    }
286
287
    function phpblock($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
288
        global $conf;
289
        if ( $state == DOKU_LEXER_UNMATCHED ) {
290
            $this->_addCall('phpblock',array($match), $pos);
291
        }
292
        return true;
293
    }
294
295
    function html($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
296
        global $conf;
297
        if ( $state == DOKU_LEXER_UNMATCHED ) {
298
            $this->_addCall('html',array($match), $pos);
299
        }
300
        return true;
301
    }
302
303
    function htmlblock($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
304
        global $conf;
305
        if ( $state == DOKU_LEXER_UNMATCHED ) {
306
            $this->_addCall('htmlblock',array($match), $pos);
307
        }
308
        return true;
309
    }
310
311
    function preformatted($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
312
        switch ( $state ) {
313
            case DOKU_LEXER_ENTER:
314
                $ReWriter = new Doku_Handler_Preformatted($this->CallWriter);
315
                $this->CallWriter = $ReWriter;
316
                $this->_addCall('preformatted_start',array(), $pos);
317
            break;
318
            case DOKU_LEXER_EXIT:
319
                $this->_addCall('preformatted_end',array(), $pos);
320
                $this->CallWriter->process();
0 ignored issues
show
Bug introduced by
The method process does only exist in Doku_Handler_List and Doku_Handler_Nest, but not in Doku_Handler_CallWriter.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
321
                $ReWriter = & $this->CallWriter;
322
                $this->CallWriter = & $ReWriter->CallWriter;
323
            break;
324
            case DOKU_LEXER_MATCHED:
325
                $this->_addCall('preformatted_newline',array(), $pos);
326
            break;
327
            case DOKU_LEXER_UNMATCHED:
328
                $this->_addCall('preformatted_content',array($match), $pos);
329
            break;
330
        }
331
332
        return true;
333
    }
334
335
    function quote($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
336
337
        switch ( $state ) {
338
339
            case DOKU_LEXER_ENTER:
340
                $ReWriter = new Doku_Handler_Quote($this->CallWriter);
341
                $this->CallWriter = & $ReWriter;
342
                $this->_addCall('quote_start',array($match), $pos);
343
            break;
344
345
            case DOKU_LEXER_EXIT:
346
                $this->_addCall('quote_end',array(), $pos);
347
                $this->CallWriter->process();
0 ignored issues
show
Bug introduced by
The method process does only exist in Doku_Handler_List and Do...ku_Handler_Preformatted, but not in Doku_Handler_CallWriter.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
348
                $ReWriter = & $this->CallWriter;
349
                $this->CallWriter = & $ReWriter->CallWriter;
350
            break;
351
352
            case DOKU_LEXER_MATCHED:
353
                $this->_addCall('quote_newline',array($match), $pos);
354
            break;
355
356
            case DOKU_LEXER_UNMATCHED:
357
                $this->_addCall('cdata',array($match), $pos);
358
            break;
359
360
        }
361
362
        return true;
363
    }
364
365
    /**
366
     * Internal function for parsing highlight options.
367
     * $options is parsed for key value pairs separated by commas.
368
     * A value might also be missing in which case the value will simple
369
     * be set to true. Commas in strings are ignored, e.g. option="4,56"
370
     * will work as expected and will only create one entry.
371
     *
372
     * @param string $options space separated list of key-value pairs,
373
     *                        e.g. option1=123, option2="456"
374
     * @return array|null     Array of key-value pairs $array['key'] = 'value';
375
     *                        or null if no entries found
376
     */
377
    protected function parse_highlight_options ($options) {
378
        $result = array();
379
        preg_match_all('/(\w+(?:="[^"]*"))|(\w+(?:=[^\s]*))|(\w+[^=\s\]])(?:\s*)/', $options, $matches, PREG_SET_ORDER);
380
        foreach ($matches as $match) {
0 ignored issues
show
Bug introduced by
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
381
            $equal_sign = strpos($match [0], '=');
382
            if ($equal_sign === false) {
383
                $key = trim($match[0]);
384
                $result [$key] = 1;
385
            } else {
386
                $key = substr($match[0], 0, $equal_sign);
387
                $value = substr($match[0], $equal_sign+1);
388
                $value = trim($value, '"');
389
                if (strlen($value) > 0) {
390
                    $result [$key] = $value;
391
                } else {
392
                    $result [$key] = 1;
393
                }
394
            }
395
        }
396
397
        // Check for supported options
398
        $result = array_intersect_key(
399
            $result,
400
            array_flip(array(
401
                'enable_line_numbers',
402
                'start_line_numbers_at',
403
                'highlight_lines_extra',
404
                'enable_keyword_links')
405
            )
406
        );
407
408
        // Sanitize values
409
        if(isset($result['enable_line_numbers'])) {
410
            if($result['enable_line_numbers'] === 'false') {
411
                $result['enable_line_numbers'] = false;
412
            }
413
            $result['enable_line_numbers'] = (bool) $result['enable_line_numbers'];
414
        }
415
        if(isset($result['highlight_lines_extra'])) {
416
            $result['highlight_lines_extra'] = array_map('intval', explode(',', $result['highlight_lines_extra']));
417
            $result['highlight_lines_extra'] = array_filter($result['highlight_lines_extra']);
418
            $result['highlight_lines_extra'] = array_unique($result['highlight_lines_extra']);
419
        }
420
        if(isset($result['start_line_numbers_at'])) {
421
            $result['start_line_numbers_at'] = (int) $result['start_line_numbers_at'];
422
        }
423
        if(isset($result['enable_keyword_links'])) {
424
            if($result['enable_keyword_links'] === 'false') {
425
                $result['enable_keyword_links'] = false;
426
            }
427
            $result['enable_keyword_links'] = (bool) $result['enable_keyword_links'];
428
        }
429
        if (count($result) == 0) {
430
            return null;
431
        }
432
433
        return $result;
434
    }
435
436
    function file($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
437
        return $this->code($match, $state, $pos, 'file');
438
    }
439
440
    function code($match, $state, $pos, $type='code') {
0 ignored issues
show
Best Practice introduced by
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...
441
        if ( $state == DOKU_LEXER_UNMATCHED ) {
442
            $matches = explode('>',$match,2);
443
            // Cut out variable options enclosed in []
444
            preg_match('/\[.*\]/', $matches[0], $options);
445
            if (!empty($options[0])) {
446
                $matches[0] = str_replace($options[0], '', $matches[0]);
447
            }
448
            $param = preg_split('/\s+/', $matches[0], 2, PREG_SPLIT_NO_EMPTY);
449
            while(count($param) < 2) array_push($param, null);
450
            // We shortcut html here.
451
            if ($param[0] == 'html') $param[0] = 'html4strict';
452
            if ($param[0] == '-') $param[0] = null;
453
            array_unshift($param, $matches[1]);
454
            if (!empty($options[0])) {
455
                $param [] = $this->parse_highlight_options ($options[0]);
456
            }
457
            $this->_addCall($type, $param, $pos);
458
        }
459
        return true;
460
    }
461
462
    function acronym($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
463
        $this->_addCall('acronym',array($match), $pos);
464
        return true;
465
    }
466
467
    function smiley($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
468
        $this->_addCall('smiley',array($match), $pos);
469
        return true;
470
    }
471
472
    function wordblock($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
473
        $this->_addCall('wordblock',array($match), $pos);
474
        return true;
475
    }
476
477
    function entity($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
478
        $this->_addCall('entity',array($match), $pos);
479
        return true;
480
    }
481
482
    function multiplyentity($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
483
        preg_match_all('/\d+/',$match,$matches);
484
        $this->_addCall('multiplyentity',array($matches[0][0],$matches[0][1]), $pos);
485
        return true;
486
    }
487
488
    function singlequoteopening($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
489
        $this->_addCall('singlequoteopening',array(), $pos);
490
        return true;
491
    }
492
493
    function singlequoteclosing($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
494
        $this->_addCall('singlequoteclosing',array(), $pos);
495
        return true;
496
    }
497
498
    function apostrophe($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
499
        $this->_addCall('apostrophe',array(), $pos);
500
        return true;
501
    }
502
503
    function doublequoteopening($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Unused Code introduced by
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...
Best Practice introduced by
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...
504
        $this->_addCall('doublequoteopening',array(), $pos);
505
        $this->status['doublequote']++;
506
        return true;
507
    }
508
509
    function doublequoteclosing($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
510
        if ($this->status['doublequote'] <= 0) {
511
            $this->doublequoteopening($match, $state, $pos);
512
        } else {
513
            $this->_addCall('doublequoteclosing',array(), $pos);
514
            $this->status['doublequote'] = max(0, --$this->status['doublequote']);
515
        }
516
        return true;
517
    }
518
519
    function camelcaselink($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
520
        $this->_addCall('camelcaselink',array($match), $pos);
521
        return true;
522
    }
523
524
    /*
525
    */
526
    function internallink($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
527
        // Strip the opening and closing markup
528
        $link = preg_replace(array('/^\[\[/','/\]\]$/u'),'',$match);
529
530
        // Split title from URL
531
        $link = explode('|',$link,2);
532
        if ( !isset($link[1]) ) {
533
            $link[1] = null;
534
        } else if ( preg_match('/^\{\{[^\}]+\}\}$/',$link[1]) ) {
535
            // If the title is an image, convert it to an array containing the image details
536
            $link[1] = Doku_Handler_Parse_Media($link[1]);
537
        }
538
        $link[0] = trim($link[0]);
539
540
        //decide which kind of link it is
541
542
        if ( link_isinterwiki($link[0]) ) {
543
            // Interwiki
544
            $interwiki = explode('>',$link[0],2);
545
            $this->_addCall(
546
                'interwikilink',
547
                array($link[0],$link[1],strtolower($interwiki[0]),$interwiki[1]),
548
                $pos
549
                );
550
        }elseif ( preg_match('/^\\\\\\\\[^\\\\]+?\\\\/u',$link[0]) ) {
551
            // Windows Share
552
            $this->_addCall(
553
                'windowssharelink',
554
                array($link[0],$link[1]),
555
                $pos
556
                );
557
        }elseif ( preg_match('#^([a-z0-9\-\.+]+?)://#i',$link[0]) ) {
558
            // external link (accepts all protocols)
559
            $this->_addCall(
560
                    'externallink',
561
                    array($link[0],$link[1]),
562
                    $pos
563
                    );
564
        }elseif ( preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link[0]) ) {
565
            // E-Mail (pattern above is defined in inc/mail.php)
566
            $this->_addCall(
567
                'emaillink',
568
                array($link[0],$link[1]),
569
                $pos
570
                );
571
        }elseif ( preg_match('!^#.+!',$link[0]) ){
572
            // local link
573
            $this->_addCall(
574
                'locallink',
575
                array(substr($link[0],1),$link[1]),
576
                $pos
577
                );
578
        }else{
579
            // internal link
580
            $this->_addCall(
581
                'internallink',
582
                array($link[0],$link[1]),
583
                $pos
584
                );
585
        }
586
587
        return true;
588
    }
589
590
    function filelink($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
591
        $this->_addCall('filelink',array($match, null), $pos);
592
        return true;
593
    }
594
595
    function windowssharelink($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
596
        $this->_addCall('windowssharelink',array($match, null), $pos);
597
        return true;
598
    }
599
600
    function media($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
601
        $p = Doku_Handler_Parse_Media($match);
602
603
        $this->_addCall(
604
              $p['type'],
605
              array($p['src'], $p['title'], $p['align'], $p['width'],
606
                     $p['height'], $p['cache'], $p['linking']),
607
              $pos
608
             );
609
        return true;
610
    }
611
612
    function rss($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
613
        $link = preg_replace(array('/^\{\{rss>/','/\}\}$/'),'',$match);
614
615
        // get params
616
        list($link,$params) = explode(' ',$link,2);
617
618
        $p = array();
619
        if(preg_match('/\b(\d+)\b/',$params,$match)){
620
            $p['max'] = $match[1];
621
        }else{
622
            $p['max'] = 8;
623
        }
624
        $p['reverse'] = (preg_match('/rev/',$params));
625
        $p['author']  = (preg_match('/\b(by|author)/',$params));
626
        $p['date']    = (preg_match('/\b(date)/',$params));
627
        $p['details'] = (preg_match('/\b(desc|detail)/',$params));
628
        $p['nosort']  = (preg_match('/\b(nosort)\b/',$params));
629
630
        if (preg_match('/\b(\d+)([dhm])\b/',$params,$match)) {
631
            $period = array('d' => 86400, 'h' => 3600, 'm' => 60);
632
            $p['refresh'] = max(600,$match[1]*$period[$match[2]]);  // n * period in seconds, minimum 10 minutes
633
        } else {
634
            $p['refresh'] = 14400;   // default to 4 hours
635
        }
636
637
        $this->_addCall('rss',array($link,$p),$pos);
638
        return true;
639
    }
640
641
    function externallink($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
642
        $url   = $match;
643
        $title = null;
644
645
        // add protocol on simple short URLs
646
        if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')){
647
            $title = $url;
648
            $url   = 'ftp://'.$url;
649
        }
650
        if(substr($url,0,3) == 'www' && (substr($url,0,7) != 'http://')){
651
            $title = $url;
652
            $url = 'http://'.$url;
653
        }
654
655
        $this->_addCall('externallink',array($url, $title), $pos);
656
        return true;
657
    }
658
659
    function emaillink($match, $state, $pos) {
0 ignored issues
show
Unused Code introduced by
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...
Best Practice introduced by
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...
660
        $email = preg_replace(array('/^</','/>$/'),'',$match);
661
        $this->_addCall('emaillink',array($email, null), $pos);
662
        return true;
663
    }
664
665
    function table($match, $state, $pos) {
0 ignored issues
show
Best Practice introduced by
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...
666
        switch ( $state ) {
667
668
            case DOKU_LEXER_ENTER:
669
670
                $ReWriter = new Doku_Handler_Table($this->CallWriter);
671
                $this->CallWriter = & $ReWriter;
672
673
                $this->_addCall('table_start', array($pos + 1), $pos);
674
                if ( trim($match) == '^' ) {
675
                    $this->_addCall('tableheader', array(), $pos);
676
                } else {
677
                    $this->_addCall('tablecell', array(), $pos);
678
                }
679
            break;
680
681
            case DOKU_LEXER_EXIT:
682
                $this->_addCall('table_end', array($pos), $pos);
683
                $this->CallWriter->process();
0 ignored issues
show
Bug introduced by
The method process does only exist in Doku_Handler_List and Do... and Doku_Handler_Quote, but not in Doku_Handler_CallWriter.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
684
                $ReWriter = & $this->CallWriter;
685
                $this->CallWriter = & $ReWriter->CallWriter;
686
            break;
687
688
            case DOKU_LEXER_UNMATCHED:
689
                if ( trim($match) != '' ) {
690
                    $this->_addCall('cdata',array($match), $pos);
691
                }
692
            break;
693
694
            case DOKU_LEXER_MATCHED:
695
                if ( $match == ' ' ){
696
                    $this->_addCall('cdata', array($match), $pos);
697
                } else if ( preg_match('/:::/',$match) ) {
698
                    $this->_addCall('rowspan', array($match), $pos);
699
                } else if ( preg_match('/\t+/',$match) ) {
700
                    $this->_addCall('table_align', array($match), $pos);
701
                } else if ( preg_match('/ {2,}/',$match) ) {
702
                    $this->_addCall('table_align', array($match), $pos);
703
                } else if ( $match == "\n|" ) {
704
                    $this->_addCall('table_row', array(), $pos);
705
                    $this->_addCall('tablecell', array(), $pos);
706
                } else if ( $match == "\n^" ) {
707
                    $this->_addCall('table_row', array(), $pos);
708
                    $this->_addCall('tableheader', array(), $pos);
709
                } else if ( $match == '|' ) {
710
                    $this->_addCall('tablecell', array(), $pos);
711
                } else if ( $match == '^' ) {
712
                    $this->_addCall('tableheader', array(), $pos);
713
                }
714
            break;
715
        }
716
        return true;
717
    }
718
}
719
720
//------------------------------------------------------------------------
721
function Doku_Handler_Parse_Media($match) {
722
723
    // Strip the opening and closing markup
724
    $link = preg_replace(array('/^\{\{/','/\}\}$/u'),'',$match);
725
726
    // Split title from URL
727
    $link = explode('|',$link,2);
728
729
    // Check alignment
730
    $ralign = (bool)preg_match('/^ /',$link[0]);
731
    $lalign = (bool)preg_match('/ $/',$link[0]);
732
733
    // Logic = what's that ;)...
734
    if ( $lalign & $ralign ) {
735
        $align = 'center';
736
    } else if ( $ralign ) {
737
        $align = 'right';
738
    } else if ( $lalign ) {
739
        $align = 'left';
740
    } else {
741
        $align = null;
742
    }
743
744
    // The title...
745
    if ( !isset($link[1]) ) {
746
        $link[1] = null;
747
    }
748
749
    //remove aligning spaces
750
    $link[0] = trim($link[0]);
751
752
    //split into src and parameters (using the very last questionmark)
753
    $pos = strrpos($link[0], '?');
754
    if($pos !== false){
755
        $src   = substr($link[0],0,$pos);
756
        $param = substr($link[0],$pos+1);
757
    }else{
758
        $src   = $link[0];
759
        $param = '';
760
    }
761
762
    //parse width and height
763
    if(preg_match('#(\d+)(x(\d+))?#i',$param,$size)){
764
        !empty($size[1]) ? $w = $size[1] : $w = null;
765
        !empty($size[3]) ? $h = $size[3] : $h = null;
766
    } else {
767
        $w = null;
768
        $h = null;
769
    }
770
771
    //get linking command
772
    if(preg_match('/nolink/i',$param)){
773
        $linking = 'nolink';
774
    }else if(preg_match('/direct/i',$param)){
775
        $linking = 'direct';
776
    }else if(preg_match('/linkonly/i',$param)){
777
        $linking = 'linkonly';
778
    }else{
779
        $linking = 'details';
780
    }
781
782
    //get caching command
783
    if (preg_match('/(nocache|recache)/i',$param,$cachemode)){
784
        $cache = $cachemode[1];
785
    }else{
786
        $cache = 'cache';
787
    }
788
789
    // Check whether this is a local or remote image or interwiki
790
    if (media_isexternal($src) || link_isinterwiki($src)){
791
        $call = 'externalmedia';
792
    } else {
793
        $call = 'internalmedia';
794
    }
795
796
    $params = array(
797
        'type'=>$call,
798
        'src'=>$src,
799
        'title'=>$link[1],
800
        'align'=>$align,
801
        'width'=>$w,
802
        'height'=>$h,
803
        'cache'=>$cache,
804
        'linking'=>$linking,
805
    );
806
807
    return $params;
808
}
809
810
//------------------------------------------------------------------------
811
interface Doku_Handler_CallWriter_Interface {
812
    public function writeCall($call);
813
    public function writeCalls($calls);
814
    public function finalise();
815
}
816
817
class Doku_Handler_CallWriter implements Doku_Handler_CallWriter_Interface {
818
819
    var $Handler;
820
821
    /**
822
     * @param Doku_Handler $Handler
823
     */
824
    function __construct(Doku_Handler $Handler) {
0 ignored issues
show
Best Practice introduced by
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...
825
        $this->Handler = $Handler;
826
    }
827
828
    function writeCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
829
        $this->Handler->calls[] = $call;
830
    }
831
832
    function writeCalls($calls) {
0 ignored issues
show
Best Practice introduced by
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...
833
        $this->Handler->calls = array_merge($this->Handler->calls, $calls);
834
    }
835
836
    // function is required, but since this call writer is first/highest in
837
    // the chain it is not required to do anything
838
    function finalise() {
0 ignored issues
show
Best Practice introduced by
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...
839
        unset($this->Handler);
840
    }
841
}
842
843
//------------------------------------------------------------------------
844
/**
845
 * Generic call writer class to handle nesting of rendering instructions
846
 * within a render instruction. Also see nest() method of renderer base class
847
 *
848
 * @author    Chris Smith <[email protected]>
849
 */
850
class Doku_Handler_Nest implements Doku_Handler_CallWriter_Interface {
851
852
    var $CallWriter;
853
    var $calls = array();
854
855
    var $closingInstruction;
856
857
    /**
858
     * constructor
859
     *
860
     * @param  Doku_Handler_CallWriter $CallWriter     the renderers current call writer
861
     * @param  string     $close          closing instruction name, this is required to properly terminate the
862
     *                                    syntax mode if the document ends without a closing pattern
863
     */
864
    function __construct(Doku_Handler_CallWriter_Interface $CallWriter, $close="nest_close") {
0 ignored issues
show
Best Practice introduced by
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...
865
        $this->CallWriter = $CallWriter;
866
867
        $this->closingInstruction = $close;
868
    }
869
870
    function writeCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
871
        $this->calls[] = $call;
872
    }
873
874
    function writeCalls($calls) {
0 ignored issues
show
Best Practice introduced by
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...
875
        $this->calls = array_merge($this->calls, $calls);
876
    }
877
878
    function finalise() {
0 ignored issues
show
Best Practice introduced by
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...
879
        $last_call = end($this->calls);
880
        $this->writeCall(array($this->closingInstruction,array(), $last_call[2]));
881
882
        $this->process();
883
        $this->CallWriter->finalise();
884
        unset($this->CallWriter);
885
    }
886
887
    function process() {
0 ignored issues
show
Best Practice introduced by
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...
888
        // merge consecutive cdata
889
        $unmerged_calls = $this->calls;
890
        $this->calls = array();
891
892
        foreach ($unmerged_calls as $call) $this->addCall($call);
893
894
        $first_call = reset($this->calls);
895
        $this->CallWriter->writeCall(array("nest", array($this->calls), $first_call[2]));
896
    }
897
898
    function addCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
899
        $key = count($this->calls);
900
        if ($key and ($call[0] == 'cdata') and ($this->calls[$key-1][0] == 'cdata')) {
901
            $this->calls[$key-1][1][0] .= $call[1][0];
902
        } else if ($call[0] == 'eol') {
903
            // do nothing (eol shouldn't be allowed, to counter preformatted fix in #1652 & #1699)
904
        } else {
905
            $this->calls[] = $call;
906
        }
907
    }
908
}
909
910
class Doku_Handler_List implements Doku_Handler_CallWriter_Interface {
911
912
    var $CallWriter;
913
914
    var $calls = array();
915
    var $listCalls = array();
916
    var $listStack = array();
917
918
    const NODE = 1;
919
920
    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
0 ignored issues
show
Best Practice introduced by
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...
921
        $this->CallWriter = $CallWriter;
922
    }
923
924
    function writeCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
925
        $this->calls[] = $call;
926
    }
927
928
    // Probably not needed but just in case...
929
    function writeCalls($calls) {
0 ignored issues
show
Best Practice introduced by
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...
930
        $this->calls = array_merge($this->calls, $calls);
931
#        $this->CallWriter->writeCalls($this->calls);
932
    }
933
934
    function finalise() {
0 ignored issues
show
Best Practice introduced by
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...
935
        $last_call = end($this->calls);
936
        $this->writeCall(array('list_close',array(), $last_call[2]));
937
938
        $this->process();
939
        $this->CallWriter->finalise();
940
        unset($this->CallWriter);
941
    }
942
943
    //------------------------------------------------------------------------
944
    function process() {
0 ignored issues
show
Best Practice introduced by
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...
945
946
        foreach ( $this->calls as $call ) {
947
            switch ($call[0]) {
948
                case 'list_item':
949
                    $this->listOpen($call);
950
                break;
951
                case 'list_open':
952
                    $this->listStart($call);
953
                break;
954
                case 'list_close':
955
                    $this->listEnd($call);
956
                break;
957
                default:
958
                    $this->listContent($call);
959
                break;
960
            }
961
        }
962
963
        $this->CallWriter->writeCalls($this->listCalls);
964
    }
965
966
    //------------------------------------------------------------------------
967
    function listStart($call) {
0 ignored issues
show
Best Practice introduced by
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...
968
        $depth = $this->interpretSyntax($call[1][0], $listType);
969
970
        $this->initialDepth = $depth;
0 ignored issues
show
Bug introduced by
The property initialDepth does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
971
        //                   array(list type, current depth, index of current listitem_open)
972
        $this->listStack[] = array($listType, $depth, 1);
973
974
        $this->listCalls[] = array('list'.$listType.'_open',array(),$call[2]);
975
        $this->listCalls[] = array('listitem_open',array(1),$call[2]);
976
        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
977
    }
978
979
    //------------------------------------------------------------------------
980
    function listEnd($call) {
0 ignored issues
show
Best Practice introduced by
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...
981
        $closeContent = true;
982
983
        while ( $list = array_pop($this->listStack) ) {
984
            if ( $closeContent ) {
985
                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
986
                $closeContent = false;
987
            }
988
            $this->listCalls[] = array('listitem_close',array(),$call[2]);
989
            $this->listCalls[] = array('list'.$list[0].'_close', array(), $call[2]);
990
        }
991
    }
992
993
    //------------------------------------------------------------------------
994
    function listOpen($call) {
0 ignored issues
show
Best Practice introduced by
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...
995
        $depth = $this->interpretSyntax($call[1][0], $listType);
996
        $end = end($this->listStack);
997
        $key = key($this->listStack);
998
999
        // Not allowed to be shallower than initialDepth
1000
        if ( $depth < $this->initialDepth ) {
1001
            $depth = $this->initialDepth;
1002
        }
1003
1004
        //------------------------------------------------------------------------
1005
        if ( $depth == $end[1] ) {
1006
1007
            // Just another item in the list...
1008
            if ( $listType == $end[0] ) {
1009
                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
1010
                $this->listCalls[] = array('listitem_close',array(),$call[2]);
1011
                $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
1012
                $this->listCalls[] = array('listcontent_open',array(),$call[2]);
1013
1014
                // new list item, update list stack's index into current listitem_open
1015
                $this->listStack[$key][2] = count($this->listCalls) - 2;
1016
1017
            // Switched list type...
1018
            } else {
1019
1020
                $this->listCalls[] = array('listcontent_close',array(),$call[2]);
1021
                $this->listCalls[] = array('listitem_close',array(),$call[2]);
1022
                $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
1023
                $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
1024
                $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
1025
                $this->listCalls[] = array('listcontent_open',array(),$call[2]);
1026
1027
                array_pop($this->listStack);
1028
                $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
1029
            }
1030
1031
        //------------------------------------------------------------------------
1032
        // Getting deeper...
1033
        } else if ( $depth > $end[1] ) {
1034
1035
            $this->listCalls[] = array('listcontent_close',array(),$call[2]);
1036
            $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
1037
            $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
1038
            $this->listCalls[] = array('listcontent_open',array(),$call[2]);
1039
1040
            // set the node/leaf state of this item's parent listitem_open to NODE
1041
            $this->listCalls[$this->listStack[$key][2]][1][1] = self::NODE;
1042
1043
            $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
1044
1045
        //------------------------------------------------------------------------
1046
        // Getting shallower ( $depth < $end[1] )
1047
        } else {
1048
            $this->listCalls[] = array('listcontent_close',array(),$call[2]);
1049
            $this->listCalls[] = array('listitem_close',array(),$call[2]);
1050
            $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
1051
1052
            // Throw away the end - done
1053
            array_pop($this->listStack);
1054
1055
            while (1) {
1056
                $end = end($this->listStack);
1057
                $key = key($this->listStack);
1058
1059
                if ( $end[1] <= $depth ) {
1060
1061
                    // Normalize depths
1062
                    $depth = $end[1];
1063
1064
                    $this->listCalls[] = array('listitem_close',array(),$call[2]);
1065
1066
                    if ( $end[0] == $listType ) {
1067
                        $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]);
1068
                        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
1069
1070
                        // new list item, update list stack's index into current listitem_open
1071
                        $this->listStack[$key][2] = count($this->listCalls) - 2;
1072
1073
                    } else {
1074
                        // Switching list type...
1075
                        $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]);
1076
                        $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]);
1077
                        $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]);
1078
                        $this->listCalls[] = array('listcontent_open',array(),$call[2]);
1079
1080
                        array_pop($this->listStack);
1081
                        $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2);
1082
                    }
1083
1084
                    break;
1085
1086
                // Haven't dropped down far enough yet.... ( $end[1] > $depth )
1087
                } else {
1088
1089
                    $this->listCalls[] = array('listitem_close',array(),$call[2]);
1090
                    $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]);
1091
1092
                    array_pop($this->listStack);
1093
1094
                }
1095
1096
            }
1097
1098
        }
1099
    }
1100
1101
    //------------------------------------------------------------------------
1102
    function listContent($call) {
0 ignored issues
show
Best Practice introduced by
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...
1103
        $this->listCalls[] = $call;
1104
    }
1105
1106
    //------------------------------------------------------------------------
1107
    function interpretSyntax($match, & $type) {
0 ignored issues
show
Best Practice introduced by
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...
1108
        if ( substr($match,-1) == '*' ) {
1109
            $type = 'u';
1110
        } else {
1111
            $type = 'o';
1112
        }
1113
        // Is the +1 needed? It used to be count(explode(...))
1114
        // but I don't think the number is seen outside this handler
1115
        return substr_count(str_replace("\t",'  ',$match), '  ') + 1;
1116
    }
1117
}
1118
1119
//------------------------------------------------------------------------
1120
class Doku_Handler_Preformatted implements Doku_Handler_CallWriter_Interface {
1121
1122
    var $CallWriter;
1123
1124
    var $calls = array();
1125
    var $pos;
1126
    var $text ='';
1127
1128
1129
1130
    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
0 ignored issues
show
Best Practice introduced by
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...
1131
        $this->CallWriter = $CallWriter;
1132
    }
1133
1134
    function writeCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
1135
        $this->calls[] = $call;
1136
    }
1137
1138
    // Probably not needed but just in case...
1139
    function writeCalls($calls) {
0 ignored issues
show
Best Practice introduced by
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...
1140
        $this->calls = array_merge($this->calls, $calls);
1141
#        $this->CallWriter->writeCalls($this->calls);
1142
    }
1143
1144
    function finalise() {
0 ignored issues
show
Best Practice introduced by
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...
1145
        $last_call = end($this->calls);
1146
        $this->writeCall(array('preformatted_end',array(), $last_call[2]));
1147
1148
        $this->process();
1149
        $this->CallWriter->finalise();
1150
        unset($this->CallWriter);
1151
    }
1152
1153
    function process() {
0 ignored issues
show
Best Practice introduced by
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...
1154
        foreach ( $this->calls as $call ) {
1155
            switch ($call[0]) {
1156
                case 'preformatted_start':
1157
                    $this->pos = $call[2];
1158
                break;
1159
                case 'preformatted_newline':
1160
                    $this->text .= "\n";
1161
                break;
1162
                case 'preformatted_content':
1163
                    $this->text .= $call[1][0];
1164
                break;
1165
                case 'preformatted_end':
1166
                    if (trim($this->text)) {
1167
                        $this->CallWriter->writeCall(array('preformatted',array($this->text),$this->pos));
1168
                    }
1169
                    // see FS#1699 & FS#1652, add 'eol' instructions to ensure proper triggering of following p_open
1170
                    $this->CallWriter->writeCall(array('eol',array(),$this->pos));
1171
                    $this->CallWriter->writeCall(array('eol',array(),$this->pos));
1172
                break;
1173
            }
1174
        }
1175
    }
1176
1177
}
1178
1179
//------------------------------------------------------------------------
1180
class Doku_Handler_Quote implements Doku_Handler_CallWriter_Interface {
1181
1182
    var $CallWriter;
1183
1184
    var $calls = array();
1185
1186
    var $quoteCalls = array();
1187
1188
    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
0 ignored issues
show
Best Practice introduced by
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...
1189
        $this->CallWriter = $CallWriter;
1190
    }
1191
1192
    function writeCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
1193
        $this->calls[] = $call;
1194
    }
1195
1196
    // Probably not needed but just in case...
1197
    function writeCalls($calls) {
0 ignored issues
show
Best Practice introduced by
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...
1198
        $this->calls = array_merge($this->calls, $calls);
1199
    }
1200
1201
    function finalise() {
0 ignored issues
show
Best Practice introduced by
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...
1202
        $last_call = end($this->calls);
1203
        $this->writeCall(array('quote_end',array(), $last_call[2]));
1204
1205
        $this->process();
1206
        $this->CallWriter->finalise();
1207
        unset($this->CallWriter);
1208
    }
1209
1210
    function process() {
0 ignored issues
show
Best Practice introduced by
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...
1211
1212
        $quoteDepth = 1;
1213
1214
        foreach ( $this->calls as $call ) {
1215
            switch ($call[0]) {
1216
1217
                case 'quote_start':
1218
1219
                    $this->quoteCalls[] = array('quote_open',array(),$call[2]);
1220
1221
                case 'quote_newline':
1222
1223
                    $quoteLength = $this->getDepth($call[1][0]);
1224
1225
                    if ( $quoteLength > $quoteDepth ) {
1226
                        $quoteDiff = $quoteLength - $quoteDepth;
1227
                        for ( $i = 1; $i <= $quoteDiff; $i++ ) {
1228
                            $this->quoteCalls[] = array('quote_open',array(),$call[2]);
1229
                        }
1230
                    } else if ( $quoteLength < $quoteDepth ) {
1231
                        $quoteDiff = $quoteDepth - $quoteLength;
1232
                        for ( $i = 1; $i <= $quoteDiff; $i++ ) {
1233
                            $this->quoteCalls[] = array('quote_close',array(),$call[2]);
1234
                        }
1235
                    } else {
1236
                        if ($call[0] != 'quote_start') $this->quoteCalls[] = array('linebreak',array(),$call[2]);
1237
                    }
1238
1239
                    $quoteDepth = $quoteLength;
1240
1241
                break;
1242
1243
                case 'quote_end':
1244
1245
                    if ( $quoteDepth > 1 ) {
1246
                        $quoteDiff = $quoteDepth - 1;
1247
                        for ( $i = 1; $i <= $quoteDiff; $i++ ) {
1248
                            $this->quoteCalls[] = array('quote_close',array(),$call[2]);
1249
                        }
1250
                    }
1251
1252
                    $this->quoteCalls[] = array('quote_close',array(),$call[2]);
1253
1254
                    $this->CallWriter->writeCalls($this->quoteCalls);
1255
                break;
1256
1257
                default:
1258
                    $this->quoteCalls[] = $call;
1259
                break;
1260
            }
1261
        }
1262
    }
1263
1264
    function getDepth($marker) {
0 ignored issues
show
Best Practice introduced by
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...
1265
        preg_match('/>{1,}/', $marker, $matches);
1266
        $quoteLength = strlen($matches[0]);
1267
        return $quoteLength;
1268
    }
1269
}
1270
1271
//------------------------------------------------------------------------
1272
class Doku_Handler_Table implements Doku_Handler_CallWriter_Interface {
1273
1274
    var $CallWriter;
1275
1276
    var $calls = array();
1277
    var $tableCalls = array();
1278
    var $maxCols = 0;
1279
    var $maxRows = 1;
1280
    var $currentCols = 0;
1281
    var $firstCell = false;
1282
    var $lastCellType = 'tablecell';
1283
    var $inTableHead = true;
1284
    var $currentRow = array('tableheader' => 0, 'tablecell' => 0);
1285
    var $countTableHeadRows = 0;
1286
1287
    function __construct(Doku_Handler_CallWriter_Interface $CallWriter) {
0 ignored issues
show
Best Practice introduced by
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...
1288
        $this->CallWriter = $CallWriter;
1289
    }
1290
1291
    function writeCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
1292
        $this->calls[] = $call;
1293
    }
1294
1295
    // Probably not needed but just in case...
1296
    function writeCalls($calls) {
0 ignored issues
show
Best Practice introduced by
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...
1297
        $this->calls = array_merge($this->calls, $calls);
1298
    }
1299
1300
    function finalise() {
0 ignored issues
show
Best Practice introduced by
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...
1301
        $last_call = end($this->calls);
1302
        $this->writeCall(array('table_end',array(), $last_call[2]));
1303
1304
        $this->process();
1305
        $this->CallWriter->finalise();
1306
        unset($this->CallWriter);
1307
    }
1308
1309
    //------------------------------------------------------------------------
1310
    function process() {
0 ignored issues
show
Best Practice introduced by
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...
1311
        foreach ( $this->calls as $call ) {
1312
            switch ( $call[0] ) {
1313
                case 'table_start':
1314
                    $this->tableStart($call);
1315
                break;
1316
                case 'table_row':
1317
                    $this->tableRowClose($call);
1318
                    $this->tableRowOpen(array('tablerow_open',$call[1],$call[2]));
1319
                break;
1320
                case 'tableheader':
1321
                case 'tablecell':
1322
                    $this->tableCell($call);
1323
                break;
1324
                case 'table_end':
1325
                    $this->tableRowClose($call);
1326
                    $this->tableEnd($call);
1327
                break;
1328
                default:
1329
                    $this->tableDefault($call);
1330
                break;
1331
            }
1332
        }
1333
        $this->CallWriter->writeCalls($this->tableCalls);
1334
    }
1335
1336
    function tableStart($call) {
0 ignored issues
show
Best Practice introduced by
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...
1337
        $this->tableCalls[] = array('table_open',$call[1],$call[2]);
1338
        $this->tableCalls[] = array('tablerow_open',array(),$call[2]);
1339
        $this->firstCell = true;
1340
    }
1341
1342
    function tableEnd($call) {
0 ignored issues
show
Best Practice introduced by
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...
1343
        $this->tableCalls[] = array('table_close',$call[1],$call[2]);
1344
        $this->finalizeTable();
1345
    }
1346
1347
    function tableRowOpen($call) {
0 ignored issues
show
Best Practice introduced by
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...
1348
        $this->tableCalls[] = $call;
1349
        $this->currentCols = 0;
1350
        $this->firstCell = true;
1351
        $this->lastCellType = 'tablecell';
1352
        $this->maxRows++;
1353
        if ($this->inTableHead) {
1354
            $this->currentRow = array('tablecell' => 0, 'tableheader' => 0);
1355
        }
1356
    }
1357
1358
    function tableRowClose($call) {
0 ignored issues
show
Best Practice introduced by
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...
1359
        if ($this->inTableHead && ($this->inTableHead = $this->isTableHeadRow())) {
1360
            $this->countTableHeadRows++;
1361
        }
1362
        // Strip off final cell opening and anything after it
1363
        while ( $discard = array_pop($this->tableCalls ) ) {
1364
1365
            if ( $discard[0] == 'tablecell_open' || $discard[0] == 'tableheader_open') {
1366
                break;
1367
            }
1368
            if (!empty($this->currentRow[$discard[0]])) {
1369
                $this->currentRow[$discard[0]]--;
1370
            }
1371
        }
1372
        $this->tableCalls[] = array('tablerow_close', array(), $call[2]);
1373
1374
        if ( $this->currentCols > $this->maxCols ) {
1375
            $this->maxCols = $this->currentCols;
1376
        }
1377
    }
1378
1379
    function isTableHeadRow() {
0 ignored issues
show
Best Practice introduced by
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...
1380
        $td = $this->currentRow['tablecell'];
1381
        $th = $this->currentRow['tableheader'];
1382
1383
        if (!$th || $td > 2) return false;
1384
        if (2*$td > $th) return false;
1385
1386
        return true;
1387
    }
1388
1389
    function tableCell($call) {
0 ignored issues
show
Best Practice introduced by
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...
1390
        if ($this->inTableHead) {
1391
            $this->currentRow[$call[0]]++;
1392
        }
1393
        if ( !$this->firstCell ) {
1394
1395
            // Increase the span
1396
            $lastCall = end($this->tableCalls);
1397
1398
            // A cell call which follows an open cell means an empty cell so span
1399
            if ( $lastCall[0] == 'tablecell_open' || $lastCall[0] == 'tableheader_open' ) {
1400
                 $this->tableCalls[] = array('colspan',array(),$call[2]);
1401
1402
            }
1403
1404
            $this->tableCalls[] = array($this->lastCellType.'_close',array(),$call[2]);
1405
            $this->tableCalls[] = array($call[0].'_open',array(1,null,1),$call[2]);
1406
            $this->lastCellType = $call[0];
1407
1408
        } else {
1409
1410
            $this->tableCalls[] = array($call[0].'_open',array(1,null,1),$call[2]);
1411
            $this->lastCellType = $call[0];
1412
            $this->firstCell = false;
1413
1414
        }
1415
1416
        $this->currentCols++;
1417
    }
1418
1419
    function tableDefault($call) {
0 ignored issues
show
Best Practice introduced by
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...
1420
        $this->tableCalls[] = $call;
1421
    }
1422
1423
    function finalizeTable() {
0 ignored issues
show
Best Practice introduced by
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...
1424
1425
        // Add the max cols and rows to the table opening
1426
        if ( $this->tableCalls[0][0] == 'table_open' ) {
1427
            // Adjust to num cols not num col delimeters
1428
            $this->tableCalls[0][1][] = $this->maxCols - 1;
1429
            $this->tableCalls[0][1][] = $this->maxRows;
1430
            $this->tableCalls[0][1][] = array_shift($this->tableCalls[0][1]);
1431
        } else {
1432
            trigger_error('First element in table call list is not table_open');
1433
        }
1434
1435
        $lastRow = 0;
1436
        $lastCell = 0;
1437
        $cellKey = array();
1438
        $toDelete = array();
1439
1440
        // if still in tableheader, then there can be no table header
1441
        // as all rows can't be within <THEAD>
1442
        if ($this->inTableHead) {
1443
            $this->inTableHead = false;
1444
            $this->countTableHeadRows = 0;
1445
        }
1446
1447
        // Look for the colspan elements and increment the colspan on the
1448
        // previous non-empty opening cell. Once done, delete all the cells
1449
        // that contain colspans
1450
        for ($key = 0 ; $key < count($this->tableCalls) ; ++$key) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1451
            $call = $this->tableCalls[$key];
1452
1453
            switch ($call[0]) {
1454
                case 'table_open' :
1455
                    if($this->countTableHeadRows) {
1456
                        array_splice($this->tableCalls, $key+1, 0, array(
1457
                              array('tablethead_open', array(), $call[2]))
1458
                        );
1459
                    }
1460
                    break;
1461
1462
                case 'tablerow_open':
1463
1464
                    $lastRow++;
1465
                    $lastCell = 0;
1466
                    break;
1467
1468
                case 'tablecell_open':
1469
                case 'tableheader_open':
1470
1471
                    $lastCell++;
1472
                    $cellKey[$lastRow][$lastCell] = $key;
1473
                    break;
1474
1475
                case 'table_align':
1476
1477
                    $prev = in_array($this->tableCalls[$key-1][0], array('tablecell_open', 'tableheader_open'));
1478
                    $next = in_array($this->tableCalls[$key+1][0], array('tablecell_close', 'tableheader_close'));
1479
                    // If the cell is empty, align left
1480
                    if ($prev && $next) {
1481
                        $this->tableCalls[$key-1][1][1] = 'left';
1482
1483
                    // If the previous element was a cell open, align right
1484
                    } elseif ($prev) {
1485
                        $this->tableCalls[$key-1][1][1] = 'right';
1486
1487
                    // If the next element is the close of an element, align either center or left
1488
                    } elseif ( $next) {
1489
                        if ( $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] == 'right' ) {
1490
                            $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'center';
1491
                        } else {
1492
                            $this->tableCalls[$cellKey[$lastRow][$lastCell]][1][1] = 'left';
1493
                        }
1494
1495
                    }
1496
1497
                    // Now convert the whitespace back to cdata
1498
                    $this->tableCalls[$key][0] = 'cdata';
1499
                    break;
1500
1501
                case 'colspan':
1502
1503
                    $this->tableCalls[$key-1][1][0] = false;
1504
1505
                    for($i = $key-2; $i >= $cellKey[$lastRow][1]; $i--) {
1506
1507
                        if (
1508
                            $this->tableCalls[$i][0] == 'tablecell_open' ||
1509
                            $this->tableCalls[$i][0] == 'tableheader_open'
1510
                        ) {
1511
1512
                            if ( false !== $this->tableCalls[$i][1][0] ) {
1513
                                $this->tableCalls[$i][1][0]++;
1514
                                break;
1515
                            }
1516
1517
                        }
1518
                    }
1519
1520
                    $toDelete[] = $key-1;
1521
                    $toDelete[] = $key;
1522
                    $toDelete[] = $key+1;
1523
                    break;
1524
1525
                case 'rowspan':
1526
1527
                    if ( $this->tableCalls[$key-1][0] == 'cdata' ) {
1528
                        // ignore rowspan if previous call was cdata (text mixed with :::)
1529
                        // we don't have to check next call as that wont match regex
1530
                        $this->tableCalls[$key][0] = 'cdata';
1531
1532
                    } else {
1533
1534
                        $spanning_cell = null;
1535
1536
                        // can't cross thead/tbody boundary
1537
                        if (!$this->countTableHeadRows || ($lastRow-1 != $this->countTableHeadRows)) {
1538
                            for($i = $lastRow-1; $i > 0; $i--) {
1539
1540
                                if (
1541
                                    $this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tablecell_open' ||
1542
                                    $this->tableCalls[$cellKey[$i][$lastCell]][0] == 'tableheader_open'
1543
                                ) {
1544
1545
                                    if ($this->tableCalls[$cellKey[$i][$lastCell]][1][2] >= $lastRow - $i) {
1546
                                        $spanning_cell = $i;
1547
                                        break;
1548
                                    }
1549
1550
                                }
1551
                            }
1552
                        }
1553
                        if (is_null($spanning_cell)) {
1554
                            // No spanning cell found, so convert this cell to
1555
                            // an empty one to avoid broken tables
1556
                            $this->tableCalls[$key][0] = 'cdata';
1557
                            $this->tableCalls[$key][1][0] = '';
1558
                            continue;
1559
                        }
1560
                        $this->tableCalls[$cellKey[$spanning_cell][$lastCell]][1][2]++;
1561
1562
                        $this->tableCalls[$key-1][1][2] = false;
1563
1564
                        $toDelete[] = $key-1;
1565
                        $toDelete[] = $key;
1566
                        $toDelete[] = $key+1;
1567
                    }
1568
                    break;
1569
1570
                case 'tablerow_close':
1571
1572
                    // Fix broken tables by adding missing cells
1573
                    $moreCalls = array();
1574
                    while (++$lastCell < $this->maxCols) {
1575
                        $moreCalls[] = array('tablecell_open', array(1, null, 1), $call[2]);
1576
                        $moreCalls[] = array('cdata', array(''), $call[2]);
1577
                        $moreCalls[] = array('tablecell_close', array(), $call[2]);
1578
                    }
1579
                    $moreCallsLength = count($moreCalls);
1580
                    if($moreCallsLength) {
1581
                        array_splice($this->tableCalls, $key, 0, $moreCalls);
1582
                        $key += $moreCallsLength;
1583
                    }
1584
1585
                    if($this->countTableHeadRows == $lastRow) {
1586
                        array_splice($this->tableCalls, $key+1, 0, array(
1587
                              array('tablethead_close', array(), $call[2])));
1588
                    }
1589
                    break;
1590
1591
            }
1592
        }
1593
1594
        // condense cdata
1595
        $cnt = count($this->tableCalls);
1596
        for( $key = 0; $key < $cnt; $key++){
1597
            if($this->tableCalls[$key][0] == 'cdata'){
1598
                $ckey = $key;
1599
                $key++;
1600
                while($this->tableCalls[$key][0] == 'cdata'){
1601
                    $this->tableCalls[$ckey][1][0] .= $this->tableCalls[$key][1][0];
1602
                    $toDelete[] = $key;
1603
                    $key++;
1604
                }
1605
                continue;
1606
            }
1607
        }
1608
1609
        foreach ( $toDelete as $delete ) {
1610
            unset($this->tableCalls[$delete]);
1611
        }
1612
        $this->tableCalls = array_values($this->tableCalls);
1613
    }
1614
}
1615
1616
1617
/**
1618
 * Handler for paragraphs
1619
 *
1620
 * @author Harry Fuecks <[email protected]>
1621
 */
1622
class Doku_Handler_Block {
1623
    var $calls = array();
1624
    var $skipEol = false;
1625
    var $inParagraph = false;
1626
1627
    // Blocks these should not be inside paragraphs
1628
    var $blockOpen = array(
1629
            'header',
1630
            'listu_open','listo_open','listitem_open','listcontent_open',
1631
            'table_open','tablerow_open','tablecell_open','tableheader_open','tablethead_open',
1632
            'quote_open',
1633
            'code','file','hr','preformatted','rss',
1634
            'htmlblock','phpblock',
1635
            'footnote_open',
1636
        );
1637
1638
    var $blockClose = array(
1639
            'header',
1640
            'listu_close','listo_close','listitem_close','listcontent_close',
1641
            'table_close','tablerow_close','tablecell_close','tableheader_close','tablethead_close',
1642
            'quote_close',
1643
            'code','file','hr','preformatted','rss',
1644
            'htmlblock','phpblock',
1645
            'footnote_close',
1646
        );
1647
1648
    // Stacks can contain paragraphs
1649
    var $stackOpen = array(
1650
        'section_open',
1651
        );
1652
1653
    var $stackClose = array(
1654
        'section_close',
1655
        );
1656
1657
1658
    /**
1659
     * Constructor. Adds loaded syntax plugins to the block and stack
1660
     * arrays
1661
     *
1662
     * @author Andreas Gohr <[email protected]>
1663
     */
1664
    function __construct(){
0 ignored issues
show
Best Practice introduced by
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...
1665
        global $DOKU_PLUGINS;
1666
        //check if syntax plugins were loaded
1667
        if(empty($DOKU_PLUGINS['syntax'])) return;
1668
        foreach($DOKU_PLUGINS['syntax'] as $n => $p){
1669
            $ptype = $p->getPType();
1670
            if($ptype == 'block'){
1671
                $this->blockOpen[]  = 'plugin_'.$n;
1672
                $this->blockClose[] = 'plugin_'.$n;
1673
            }elseif($ptype == 'stack'){
1674
                $this->stackOpen[]  = 'plugin_'.$n;
1675
                $this->stackClose[] = 'plugin_'.$n;
1676
            }
1677
        }
1678
    }
1679
1680
    function openParagraph($pos){
0 ignored issues
show
Best Practice introduced by
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...
1681
        if ($this->inParagraph) return;
1682
        $this->calls[] = array('p_open',array(), $pos);
1683
        $this->inParagraph = true;
1684
        $this->skipEol = true;
1685
    }
1686
1687
    /**
1688
     * Close a paragraph if needed
1689
     *
1690
     * This function makes sure there are no empty paragraphs on the stack
1691
     *
1692
     * @author Andreas Gohr <[email protected]>
1693
     *
1694
     * @param string|integer $pos
1695
     */
1696
    function closeParagraph($pos){
0 ignored issues
show
Best Practice introduced by
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...
1697
        if (!$this->inParagraph) return;
1698
        // look back if there was any content - we don't want empty paragraphs
1699
        $content = '';
1700
        $ccount = count($this->calls);
1701
        for($i=$ccount-1; $i>=0; $i--){
1702
            if($this->calls[$i][0] == 'p_open'){
1703
                break;
1704
            }elseif($this->calls[$i][0] == 'cdata'){
1705
                $content .= $this->calls[$i][1][0];
1706
            }else{
1707
                $content = 'found markup';
1708
                break;
1709
            }
1710
        }
1711
1712
        if(trim($content)==''){
1713
            //remove the whole paragraph
1714
            //array_splice($this->calls,$i); // <- this is much slower than the loop below
1715
            for($x=$ccount; $x>$i; $x--) array_pop($this->calls);
1716
        }else{
1717
            // remove ending linebreaks in the paragraph
1718
            $i=count($this->calls)-1;
1719
            if ($this->calls[$i][0] == 'cdata') $this->calls[$i][1][0] = rtrim($this->calls[$i][1][0],DOKU_PARSER_EOL);
1720
            $this->calls[] = array('p_close',array(), $pos);
1721
        }
1722
1723
        $this->inParagraph = false;
1724
        $this->skipEol = true;
1725
    }
1726
1727
    function addCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
1728
        $key = count($this->calls);
1729
        if ($key and ($call[0] == 'cdata') and ($this->calls[$key-1][0] == 'cdata')) {
1730
            $this->calls[$key-1][1][0] .= $call[1][0];
1731
        } else {
1732
            $this->calls[] = $call;
1733
        }
1734
    }
1735
1736
    // simple version of addCall, without checking cdata
1737
    function storeCall($call) {
0 ignored issues
show
Best Practice introduced by
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...
1738
        $this->calls[] = $call;
1739
    }
1740
1741
    /**
1742
     * Processes the whole instruction stack to open and close paragraphs
1743
     *
1744
     * @author Harry Fuecks <[email protected]>
1745
     * @author Andreas Gohr <[email protected]>
1746
     *
1747
     * @param array $calls
1748
     *
1749
     * @return array
1750
     */
1751
    function process($calls) {
0 ignored issues
show
Best Practice introduced by
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...
1752
        // open first paragraph
1753
        $this->openParagraph(0);
1754
        foreach ( $calls as $key => $call ) {
1755
            $cname = $call[0];
1756
            if ($cname == 'plugin') {
1757
                $cname='plugin_'.$call[1][0];
1758
                $plugin = true;
1759
                $plugin_open = (($call[1][2] == DOKU_LEXER_ENTER) || ($call[1][2] == DOKU_LEXER_SPECIAL));
1760
                $plugin_close = (($call[1][2] == DOKU_LEXER_EXIT) || ($call[1][2] == DOKU_LEXER_SPECIAL));
1761
            } else {
1762
                $plugin = false;
1763
            }
1764
            /* stack */
1765
            if ( in_array($cname,$this->stackClose ) && (!$plugin || $plugin_close)) {
0 ignored issues
show
Bug introduced by
The variable $plugin_close does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1766
                $this->closeParagraph($call[2]);
1767
                $this->storeCall($call);
1768
                $this->openParagraph($call[2]);
1769
                continue;
1770
            }
1771
            if ( in_array($cname,$this->stackOpen ) && (!$plugin || $plugin_open) ) {
0 ignored issues
show
Bug introduced by
The variable $plugin_open does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1772
                $this->closeParagraph($call[2]);
1773
                $this->storeCall($call);
1774
                $this->openParagraph($call[2]);
1775
                continue;
1776
            }
1777
            /* block */
1778
            // If it's a substition it opens and closes at the same call.
1779
            // To make sure next paragraph is correctly started, let close go first.
1780
            if ( in_array($cname, $this->blockClose) && (!$plugin || $plugin_close)) {
1781
                $this->closeParagraph($call[2]);
1782
                $this->storeCall($call);
1783
                $this->openParagraph($call[2]);
1784
                continue;
1785
            }
1786
            if ( in_array($cname, $this->blockOpen) && (!$plugin || $plugin_open)) {
1787
                $this->closeParagraph($call[2]);
1788
                $this->storeCall($call);
1789
                continue;
1790
            }
1791
            /* eol */
1792
            if ( $cname == 'eol' ) {
1793
                // Check this isn't an eol instruction to skip...
1794
                if ( !$this->skipEol ) {
1795
                    // Next is EOL => double eol => mark as paragraph
1796
                    if ( isset($calls[$key+1]) && $calls[$key+1][0] == 'eol' ) {
1797
                        $this->closeParagraph($call[2]);
1798
                        $this->openParagraph($call[2]);
1799
                    } else {
1800
                        //if this is just a single eol make a space from it
1801
                        $this->addCall(array('cdata',array(DOKU_PARSER_EOL), $call[2]));
1802
                    }
1803
                }
1804
                continue;
1805
            }
1806
            /* normal */
1807
            $this->addCall($call);
1808
            $this->skipEol = false;
1809
        }
1810
        // close last paragraph
1811
        $call = end($this->calls);
1812
        $this->closeParagraph($call[2]);
1813
        return $this->calls;
1814
    }
1815
}
1816
1817
//Setup VIM: ex: et ts=4 :
1818