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 | namespace Rudolf\Component\Html; |
||
4 | |||
5 | use Rudolf\Component\Helpers\Navigation\MenuItem; |
||
6 | use Rudolf\Component\Helpers\Navigation\MenuItemCollection; |
||
7 | |||
8 | class Navigation |
||
9 | { |
||
10 | /** |
||
11 | * @var int |
||
12 | */ |
||
13 | private $rootID = 0; |
||
14 | |||
15 | /** |
||
16 | * @var string |
||
17 | */ |
||
18 | private $type; |
||
19 | |||
20 | /** |
||
21 | * @var MenuItemCollection |
||
22 | */ |
||
23 | private $menuItemsCollection; |
||
24 | |||
25 | /** |
||
26 | * @var array |
||
27 | */ |
||
28 | private $currents = []; |
||
29 | |||
30 | /** |
||
31 | * @var array |
||
32 | */ |
||
33 | private $classes = []; |
||
34 | |||
35 | /** |
||
36 | * @var int |
||
37 | */ |
||
38 | private $nesting = 0; |
||
39 | |||
40 | /** |
||
41 | * @var array |
||
42 | */ |
||
43 | private $before = []; |
||
44 | |||
45 | /** |
||
46 | * @var array |
||
47 | */ |
||
48 | private $after = []; |
||
49 | |||
50 | /** |
||
51 | * @var array |
||
52 | */ |
||
53 | private $config = []; |
||
54 | |||
55 | /** |
||
56 | * Set items. |
||
57 | * |
||
58 | * @param MenuItemCollection $items |
||
59 | */ |
||
60 | public function setItems(MenuItemCollection $items) |
||
61 | { |
||
62 | $this->menuItemsCollection = $items; |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * Set active elements slugs, use to mark current items. |
||
67 | * |
||
68 | * @param array|string $currents |
||
69 | */ |
||
70 | public function setCurrent($currents) |
||
71 | { |
||
72 | if (!is_array($currents)) { |
||
73 | $address = explode('/', trim($currents, '/')); |
||
74 | |||
75 | $currents = []; |
||
76 | $temp = ''; |
||
77 | foreach ($address as $key => $value) { |
||
78 | $currents[] = ltrim($temp = $temp.'/'.$value, '/'); |
||
79 | } |
||
80 | } |
||
81 | |||
82 | $this->currents = $currents; |
||
83 | } |
||
84 | |||
85 | /** |
||
86 | * Menu creator. |
||
87 | * @link http://pastebin.com/GAFvSew4 |
||
88 | * @author J. Bruni - original author |
||
89 | * @return string|bool |
||
90 | */ |
||
91 | public function create() |
||
92 | { |
||
93 | $root_id = $this->getRootID(); |
||
94 | $items = $this->sortItems($this->getItems()); |
||
95 | $currents = $this->getCurrents(); |
||
96 | $classes = $this->getClasses(); |
||
97 | $before = $this->getBefore(); |
||
98 | $after = $this->getAfter(); |
||
99 | $nesting = $this->getNesting(); |
||
100 | $config = $this->getConfig(); |
||
101 | |||
102 | if (empty($items)) { |
||
103 | return false; |
||
104 | } |
||
105 | |||
106 | View Code Duplication | foreach ($items as $item) { |
|
0 ignored issues
–
show
|
|||
107 | if (null !== $item->getParentId()) { |
||
108 | $children[$item->getParentId()][] = $item; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$children was never initialized. Although not strictly required by PHP, it is generally a good practice to add $children = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop. ![]() |
|||
109 | } |
||
110 | } |
||
111 | |||
112 | // loop will be false if the root has no children (i.e., an empty menu!) |
||
113 | $loop = !empty($children[$root_id]); |
||
114 | |||
115 | // initializing $parent as the root |
||
116 | $parent = $root_id; |
||
117 | $parent_stack = []; |
||
118 | |||
119 | $html = []; |
||
120 | |||
121 | $html[] = $before['root_ul']; |
||
122 | |||
123 | // HTML wrapper for the menu (open) |
||
124 | $html[] = sprintf( |
||
125 | '%1$s'.'<ul'.'%2$s'.'>', |
||
126 | # %1$s tab if text before |
||
127 | !empty($before['root_ul']) ? str_repeat("\t", $nesting) : '', |
||
128 | |||
129 | # %2$s root ul class |
||
130 | $this->isAttribute('class', $classes['root_ul']) |
||
131 | ); |
||
132 | |||
133 | $html[] = !empty($before['first_root_li']) ? str_repeat("\t", $nesting + 1).$before['first_root_li'] : ''; |
||
134 | |||
135 | // loop |
||
136 | while ($loop && (($item = $this->each($children[$parent])) || ($parent > $root_id))) { |
||
0 ignored issues
–
show
The variable
$children 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
![]() |
|||
137 | View Code Duplication | if (is_object($item['value'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
138 | /** |
||
139 | * @var MenuItem $obj |
||
140 | */ |
||
141 | $obj = $item['value']; |
||
142 | $item = [ |
||
143 | 'id' => $obj->getId(), |
||
144 | 'parent_id' => $obj->getParentId(), |
||
145 | 'title' => $obj->getTitle(), |
||
146 | 'slug' => $obj->getSlug(), |
||
147 | 'caption' => $obj->getCaption(), |
||
148 | 'ico' => $obj->getIco(), |
||
149 | ]; |
||
150 | } |
||
151 | |||
152 | // HTML for menu item containing children (close) |
||
153 | if ($item === false) { |
||
154 | $parent = array_pop($parent_stack); |
||
155 | $html[] = str_repeat("\t", (count($parent_stack) + 1) * 2 + $nesting).'</ul>'; |
||
156 | $html[] = str_repeat("\t", (count($parent_stack) + 1) * 2 - 1 + $nesting).'</li>'; |
||
157 | } // HTML for menu item containing children (open) |
||
158 | elseif (!empty($children[$item['id']])) { |
||
159 | $tab = str_repeat("\t", (count($parent_stack) + 1) * 2 - 1 + $nesting); |
||
160 | |||
161 | /* |
||
162 | * <li> with <ul> |
||
163 | */ |
||
164 | $html[] = sprintf( |
||
165 | '%1$s'.'<li'.'%2$s'.'>%3$s<a%4$s' |
||
166 | .'%5$s'.' href="'.'%6$s'.'">%7$s%8$s'.'%9$s'.'%10$s</a>%11$s', |
||
167 | # %1$s tabulation |
||
168 | $tab, |
||
169 | |||
170 | # %2$s li class (active) |
||
171 | $this->isAttribute( |
||
172 | 'class', |
||
173 | [ |
||
174 | $classes['li'], |
||
175 | $classes['li_with_ul'], |
||
176 | $this->isActive($item['slug'], $currents) ? $classes['li_active'] : '', |
||
177 | ] |
||
178 | ), |
||
179 | |||
180 | # %3$s text before li a |
||
181 | $before['li_with_ul_a'], |
||
182 | |||
183 | # %4$s a class |
||
184 | $this->isAttribute( |
||
185 | 'class', |
||
186 | [ |
||
187 | $classes['a'], |
||
188 | $this->isActive($item['slug'], $currents) ? $classes['a_active'] : '', |
||
189 | ] |
||
190 | ), |
||
191 | |||
192 | # %5$s a title="" |
||
193 | $this->isAttribute('title', $item['caption']), |
||
194 | |||
195 | # %6$s a href="" |
||
196 | $item['slug'], |
||
197 | |||
198 | # %7$s ico |
||
199 | $this->addContainerWithIcoIf( |
||
200 | $item['ico'], |
||
201 | $config['li_a_ico-container'], |
||
202 | $config['li_a_ico-class_base'], |
||
203 | $config['icon_attribute'] |
||
204 | ), |
||
205 | |||
206 | # %8$s before text in li a |
||
207 | $before['li_with_ul_a_text'], |
||
208 | |||
209 | # %9$s text inside item |
||
210 | $this->addContainerWithSelectorIf($item['title'], $config['li_a_text-container']), |
||
211 | |||
212 | # %10$s after text in li a |
||
213 | $after['li_with_ul_a_text'], |
||
214 | |||
215 | # %11$s text after li a |
||
216 | $after['li_with_ul_a'] |
||
217 | ); |
||
218 | |||
219 | /* |
||
220 | * sub <ul> in <li> |
||
221 | */ |
||
222 | $html[] = sprintf( |
||
223 | '%1$s'.'<ul'.'%2$s'.'>', |
||
224 | # %1$s tabulation |
||
225 | $tab."\t", |
||
226 | |||
227 | # %2$s sub ul class |
||
228 | $this->isAttribute('class', $classes['sub_ul']) |
||
229 | ); |
||
230 | |||
231 | $parent_stack[] = $item['parent_id']; |
||
232 | $parent = $item['id']; |
||
233 | } // HTML for menu item with no children (aka "leaf") |
||
234 | else { |
||
235 | $html[] = sprintf( |
||
236 | '%1$s'.'<li'.'%2$s'.'>%3$s<a'.'%4$s' |
||
237 | .'%5$s'.' href="'.'%6$s'.'">%7$s%8$s'.'%9$s'.'%10$s</a>%11$s', |
||
238 | # %1$s tabulation |
||
239 | str_repeat("\t", (count($parent_stack) + 1) * 2 - 1 + $nesting), |
||
240 | |||
241 | # %2$s li class (active) |
||
242 | $this->isAttribute( |
||
243 | 'class', |
||
244 | [ |
||
245 | $classes['li'], |
||
246 | $classes['li_without_ul'], |
||
247 | $this->isActive($item['slug'], $currents) ? $classes['li_active'] : '', |
||
248 | ] |
||
249 | ), |
||
250 | |||
251 | # %3$s text before li a |
||
252 | $before['li_a'], |
||
253 | |||
254 | # %4$s a class="" |
||
255 | $this->isAttribute( |
||
256 | 'class', |
||
257 | [ |
||
258 | $classes['a'], |
||
259 | $this->isActive($item['slug'], $currents) ? $classes['a_active'] : '', |
||
260 | ] |
||
261 | ), |
||
262 | |||
263 | # %5$s a title="" |
||
264 | $this->isAttribute('title', $item['caption']), |
||
265 | |||
266 | # %6$s a href="" |
||
267 | $item['slug'], |
||
268 | |||
269 | # %7$s ico |
||
270 | $this->addContainerWithIcoIf( |
||
271 | $item['ico'], |
||
272 | $config['li_a_ico-container'], |
||
273 | $config['li_a_ico-class_base'], |
||
274 | $config['icon_attribute'] |
||
275 | ), |
||
276 | |||
277 | # %8$s before text in li a |
||
278 | $before['li_a_text'], |
||
279 | |||
280 | # %9$s text inside item |
||
281 | $this->addContainerWithSelectorIf($item['title'], $config['li_a_text-container']), |
||
282 | |||
283 | # %10$s after text in li a |
||
284 | $after['li_a_text'], |
||
285 | |||
286 | # %11$s text after li a |
||
287 | $after['li_a'] |
||
288 | ); |
||
289 | } |
||
290 | } |
||
291 | |||
292 | $html[] = !empty($after['last_root_li']) ? str_repeat("\t", $nesting + 1).$after['last_root_li'] : ''; |
||
293 | |||
294 | // HTML wrapper for the menu (close) |
||
295 | $html[] = str_repeat("\t", $nesting).'</ul>'; |
||
296 | |||
297 | $html[] = $after['root_ul']; |
||
298 | |||
299 | return implode("\n", array_filter($html))."\n"; |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * @return int |
||
304 | */ |
||
305 | public function getRootID() |
||
306 | { |
||
307 | return $this->rootID; |
||
308 | } |
||
309 | |||
310 | /** |
||
311 | * Set root ID. |
||
312 | * |
||
313 | * @param int $id ID of element to start create tree. Set 0 to create full tree |
||
314 | */ |
||
315 | public function setRootID($id) |
||
316 | { |
||
317 | $this->rootID = (int)$id; |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * @param array $items |
||
322 | * |
||
323 | * @return MenuItem[] |
||
324 | */ |
||
325 | protected function sortItems(array $items) |
||
326 | { |
||
327 | usort( |
||
328 | $items, |
||
329 | function ($a, $b) { |
||
330 | /** @var MenuItem $a */ |
||
331 | /** @var MenuItem $b */ |
||
332 | return $a->getPosition() > $b->getPosition(); |
||
333 | } |
||
334 | ); |
||
335 | |||
336 | return $items; |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * @return MenuItem[] |
||
341 | */ |
||
342 | public function getItems() |
||
343 | { |
||
344 | return $this->menuItemsCollection->getByType($this->getType()); |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * @return string |
||
349 | */ |
||
350 | public function getType() |
||
351 | { |
||
352 | return $this->type; |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * Menu type defined in menu_types table. |
||
357 | * |
||
358 | * @param string $type |
||
359 | */ |
||
360 | public function setType($type) |
||
361 | { |
||
362 | $this->type = $type; |
||
363 | } |
||
364 | |||
365 | /** |
||
366 | * @return array |
||
367 | */ |
||
368 | public function getCurrents() |
||
369 | { |
||
370 | $currents = $this->currents; |
||
371 | |||
372 | // add actual app dir to currents slug |
||
373 | foreach ($currents as $key => $value) { |
||
374 | $currents[$key] = DIR.'/'.$value; |
||
375 | } |
||
376 | |||
377 | return $currents; |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * @return array |
||
382 | */ |
||
383 | public function getClasses() |
||
384 | { |
||
385 | return array_merge( |
||
386 | [ |
||
387 | 'root_ul' => '', |
||
388 | 'li' => '', |
||
389 | 'a' => '', |
||
390 | 'li_active' => '', |
||
391 | 'a_active' => '', |
||
392 | 'li_with_ul' => '', |
||
393 | 'li_without_ul' => '', |
||
394 | 'sub_ul' => '', |
||
395 | ], |
||
396 | $this->classes |
||
397 | ); |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * Set classes to use in menu. |
||
402 | * |
||
403 | * @param array $classes |
||
404 | * 'root_ul' (string) Main <ul> |
||
405 | * `li` (string) Each <li> |
||
406 | * `a` (string) Each <a> |
||
407 | * 'li_active' (string) |
||
408 | * 'a_active' (string) |
||
409 | * 'li_with_ul' (string) <li> with <ul> |
||
410 | * 'li_without_ul' (string) <li> without <ul> |
||
411 | * 'sub_ul' (string) <ul> inside <li> |
||
412 | */ |
||
413 | public function setClasses(array $classes) |
||
414 | { |
||
415 | $this->classes = $classes; |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * @return array |
||
420 | */ |
||
421 | View Code Duplication | public function getBefore() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
422 | { |
||
423 | return array_merge( |
||
424 | [ |
||
425 | 'root_ul' => '', |
||
426 | 'first_root_li' => '', |
||
427 | 'li_a' => '', |
||
428 | 'li_a_text' => '', |
||
429 | 'li_with_ul_a' => '', |
||
430 | 'li_with_ul_a_text' => '', |
||
431 | ], |
||
432 | $this->before |
||
433 | ); |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Put string before elements. |
||
438 | * |
||
439 | * @param array $before |
||
440 | * 'root_ul' (string) Main <ul> |
||
441 | * 'first_root_li' (string) First <li> in main <ul> |
||
442 | * 'li_a' (string) In <li> before <a> |
||
443 | * 'li_a_text' (string) In <li><a> before text inside |
||
444 | * 'li_with_ul_a' (string) In <li> with <ul> before <a> |
||
445 | * 'li_with_ul_a_text' (string) In <li><a> with <ul> before text inside |
||
446 | */ |
||
447 | public function setBefore(array $before) |
||
448 | { |
||
449 | $this->before = $before; |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * @return array |
||
454 | */ |
||
455 | View Code Duplication | public function getAfter() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
456 | { |
||
457 | return array_merge( |
||
458 | [ |
||
459 | 'root_ul' => '', |
||
460 | 'last_root_li' => '', |
||
461 | 'li_a' => '', |
||
462 | 'li_a_text' => '', |
||
463 | 'li_with_ul_a' => '', |
||
464 | 'li_with_ul_a_text' => '', |
||
465 | ], |
||
466 | $this->after |
||
467 | ); |
||
468 | } |
||
469 | |||
470 | /** |
||
471 | * Put string after elements. |
||
472 | * |
||
473 | * @param array $after Texts after |
||
474 | * 'root_ul' (string) Main <ul> |
||
475 | * 'last_root_li' (string) Last <li> in main <ul> |
||
476 | * 'li_a' (string) In <li> after <a> |
||
477 | * 'li_a_text' (string) In <li><a> before text inside |
||
478 | * 'li_with_ul_a' (string) In <li> with <ul> after <a> |
||
479 | * 'li_with_ul_a_text' (string) In <li><a> with <ul> after text inside |
||
480 | */ |
||
481 | public function setAfter(array $after) |
||
482 | { |
||
483 | $this->after = $after; |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * @return mixed |
||
488 | */ |
||
489 | public function getNesting() |
||
490 | { |
||
491 | return $this->nesting; |
||
492 | } |
||
493 | |||
494 | /** |
||
495 | * Set generated menu code nesting. |
||
496 | * |
||
497 | * @param int $nesting |
||
498 | */ |
||
499 | public function setNesting($nesting) |
||
500 | { |
||
501 | $this->nesting = $nesting; |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * @return array |
||
506 | */ |
||
507 | public function getConfig() |
||
508 | { |
||
509 | return array_merge( |
||
510 | [ |
||
511 | 'li_a_text-container' => '', |
||
512 | 'li_a_ico-container' => '', |
||
513 | 'li_a_ico-class_base' => '', |
||
514 | 'icon_attribute' => 'class' |
||
515 | ], |
||
516 | $this->config |
||
517 | ); |
||
518 | } |
||
519 | |||
520 | /** |
||
521 | * Set config. |
||
522 | * |
||
523 | * @param array $config |
||
524 | * 'li_a_text-container' (string) Selector container for text in <li><a> |
||
525 | * 'li_a_ico-container' (string) Selector container for ico in <li><a> |
||
526 | * 'li_a_ico-class_base' (string) Base class of icon container |
||
527 | */ |
||
528 | public function setConfig(array $config) |
||
529 | { |
||
530 | $this->config = $config; |
||
531 | } |
||
532 | |||
533 | /** |
||
534 | * Put value is not empty. |
||
535 | * |
||
536 | * @param string $attribute |
||
537 | * @param string|array $value |
||
538 | * |
||
539 | * @return string |
||
540 | */ |
||
541 | View Code Duplication | private function isAttribute($attribute, $value) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
542 | { |
||
543 | if (is_array($value)) { |
||
544 | array_filter($value); |
||
545 | $value = trim(implode(' ', $value)); |
||
546 | |||
547 | return !empty($value) ? ' '.$attribute.'="'.$value.'"' : ''; |
||
548 | } |
||
549 | |||
550 | return (isset($value) and !empty($value)) ? ' '.$attribute.'="'.trim($value).'"' : ''; |
||
551 | } |
||
552 | |||
553 | protected function each(&$arr) |
||
554 | { |
||
555 | $key = key($arr); |
||
556 | $result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)]; |
||
557 | next($arr); |
||
558 | |||
559 | return $result; |
||
560 | } |
||
561 | |||
562 | /** |
||
563 | * Check is item active. |
||
564 | * |
||
565 | * @param string $slug Current slug |
||
566 | * @param array $array Active slugs |
||
567 | * |
||
568 | * @return bool |
||
569 | */ |
||
570 | private function isActive($slug, $array) |
||
571 | { |
||
572 | return in_array($slug, $array); |
||
573 | } |
||
574 | |||
575 | private function addContainerWithIcoIf($ico, $selector, $classBase, $attribute) |
||
576 | { |
||
577 | if (empty($ico) || empty($selector)) { |
||
578 | return false; |
||
579 | } |
||
580 | |||
581 | return '<'.$selector.' '.$attribute.'="'.implode(' ', [$classBase, $ico]).'"></'.$selector.'> '; |
||
582 | } |
||
583 | |||
584 | private function addContainerWithSelectorIf($inside, $selector) |
||
585 | { |
||
586 | if (empty($selector)) { |
||
587 | return $inside; |
||
588 | } |
||
589 | |||
590 | return '<'.$selector.'>'.$inside.'</'.$selector.'>'; |
||
591 | } |
||
592 | } |
||
593 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.