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