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 Spatie\Menu; |
||
4 | |||
5 | use ArrayIterator; |
||
6 | use Countable; |
||
7 | use IteratorAggregate; |
||
8 | use Spatie\Menu\Helpers\Reflection; |
||
9 | use Spatie\Menu\Html\Attributes; |
||
10 | use Spatie\Menu\Html\Tag; |
||
11 | use Spatie\Menu\Traits\Conditions as ConditionsTrait; |
||
12 | use Spatie\Menu\Traits\HasHtmlAttributes as HasHtmlAttributesTrait; |
||
13 | use Spatie\Menu\Traits\HasParentAttributes as HasParentAttributesTrait; |
||
14 | use Spatie\Menu\Traits\HasTextAttributes as HasAttributesTrait; |
||
15 | use Traversable; |
||
16 | |||
17 | class Menu implements Item, Countable, HasHtmlAttributes, HasParentAttributes, IteratorAggregate |
||
18 | { |
||
19 | use HasHtmlAttributesTrait, HasParentAttributesTrait, ConditionsTrait, HasAttributesTrait; |
||
20 | |||
21 | /** @var array */ |
||
22 | protected $items = []; |
||
23 | |||
24 | /** @var array */ |
||
25 | protected $filters = []; |
||
26 | |||
27 | /** @var string */ |
||
28 | protected $prepend, $append = ''; |
||
29 | |||
30 | /** @var array */ |
||
31 | protected $wrap = []; |
||
32 | |||
33 | /** @var string */ |
||
34 | protected $activeClass = 'active'; |
||
35 | |||
36 | /** @var string */ |
||
37 | protected $exactActiveClass = 'exact-active'; |
||
38 | |||
39 | /** @var string */ |
||
40 | protected $wrapperTagName = 'ul'; |
||
41 | |||
42 | /** @var bool */ |
||
43 | protected $parentTagName = 'li'; |
||
44 | |||
45 | /** @var bool */ |
||
46 | protected $activeClassOnParent = true; |
||
47 | |||
48 | /** @var bool */ |
||
49 | protected $activeClassOnLink = false; |
||
50 | |||
51 | /** @var \Spatie\Menu\Html\Attributes */ |
||
52 | protected $htmlAttributes, $parentAttributes; |
||
53 | |||
54 | protected function __construct(Item ...$items) |
||
55 | { |
||
56 | $this->items = $items; |
||
57 | |||
58 | $this->htmlAttributes = new Attributes(); |
||
59 | $this->parentAttributes = new Attributes(); |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * Create a new menu, optionally prefilled with items. |
||
64 | * |
||
65 | * @param array $items |
||
66 | * |
||
67 | * @return static |
||
68 | */ |
||
69 | public static function new($items = []) |
||
70 | { |
||
71 | return new static(...array_values($items)); |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Build a new menu from an array. The callback receives a menu instance as |
||
76 | * the accumulator, the array item as the second parameter, and the item's |
||
77 | * key as the third. |
||
78 | * |
||
79 | * @param array|\Iterator $items |
||
80 | * @param callable $callback |
||
81 | * @param \Spatie\Menu\Menu|null $initial |
||
82 | * |
||
83 | * @return static |
||
84 | */ |
||
85 | public static function build($items, callable $callback, self $initial = null) |
||
86 | { |
||
87 | return ($initial ?: static::new())->fill($items, $callback); |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Fill a menu from an array. The callback receives a menu instance as |
||
92 | * the accumulator, the array item as the second parameter, and the item's |
||
93 | * key as the third. |
||
94 | * |
||
95 | * @param array|\Iterator $items |
||
96 | * @param callable $callback |
||
97 | * |
||
98 | * @return static |
||
99 | */ |
||
100 | public function fill($items, callable $callback) |
||
101 | { |
||
102 | $menu = $this; |
||
103 | |||
104 | foreach ($items as $key => $item) { |
||
105 | $menu = $callback($menu, $item, $key) ?: $menu; |
||
106 | } |
||
107 | |||
108 | return $menu; |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Add an item to the menu. This also applies all registered filters to the |
||
113 | * item. |
||
114 | * |
||
115 | * @param \Spatie\Menu\Item $item |
||
116 | * |
||
117 | * @return $this |
||
118 | */ |
||
119 | public function add(Item $item) |
||
120 | { |
||
121 | foreach ($this->filters as $filter) { |
||
122 | $this->applyFilter($filter, $item); |
||
123 | } |
||
124 | |||
125 | $this->items[] = $item; |
||
126 | |||
127 | return $this; |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * Add an item to the menu if a (non-strict) condition is met. |
||
132 | * |
||
133 | * @param bool $condition |
||
134 | * @param \Spatie\Menu\Item $item |
||
135 | * |
||
136 | * @return $this |
||
137 | */ |
||
138 | public function addIf($condition, Item $item) |
||
139 | { |
||
140 | if ($this->resolveCondition($condition)) { |
||
141 | $this->add($item); |
||
142 | } |
||
143 | |||
144 | return $this; |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Shortcut function to add a plain link to the menu. |
||
149 | * |
||
150 | * @param string $url |
||
151 | * @param string $text |
||
152 | * |
||
153 | * @return $this |
||
154 | */ |
||
155 | public function link(string $url, string $text) |
||
156 | { |
||
157 | return $this->add(Link::to($url, $text)); |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * Shortcut function to add an empty item to the menu. |
||
162 | * |
||
163 | * @return $this |
||
164 | */ |
||
165 | public function empty() |
||
166 | { |
||
167 | return $this->add(Html::empty()); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Add a link to the menu if a (non-strict) condition is met. |
||
172 | * |
||
173 | * @param bool $condition |
||
174 | * @param string $url |
||
175 | * @param string $text |
||
176 | * |
||
177 | * @return $this |
||
178 | */ |
||
179 | public function linkIf($condition, string $url, string $text) |
||
180 | { |
||
181 | if ($this->resolveCondition($condition)) { |
||
182 | $this->link($url, $text); |
||
183 | } |
||
184 | |||
185 | return $this; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Shortcut function to add raw html to the menu. |
||
190 | * |
||
191 | * @param string $html |
||
192 | * @param array $parentAttributes |
||
193 | * |
||
194 | * @return $this |
||
195 | */ |
||
196 | public function html(string $html, array $parentAttributes = []) |
||
197 | { |
||
198 | return $this->add(Html::raw($html)->setParentAttributes($parentAttributes)); |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Add a chunk of html if a (non-strict) condition is met. |
||
203 | * |
||
204 | * @param bool $condition |
||
205 | * @param string $html |
||
206 | * @param array $parentAttributes |
||
207 | * |
||
208 | * @return $this |
||
209 | */ |
||
210 | public function htmlIf($condition, string $html, array $parentAttributes = []) |
||
211 | { |
||
212 | if ($this->resolveCondition($condition)) { |
||
213 | $this->html($html, $parentAttributes); |
||
214 | } |
||
215 | |||
216 | return $this; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * @param callable|\Spatie\Menu\Menu|\Spatie\Menu\Item $header |
||
221 | * @param callable|\Spatie\Menu\Menu|null $menu |
||
222 | * |
||
223 | * @return $this |
||
224 | */ |
||
225 | public function submenu($header, $menu = null) |
||
226 | { |
||
227 | [$header, $menu] = $this->parseSubmenuArgs(func_get_args()); |
||
228 | |||
229 | $menu = $this->createSubmenuMenu($menu); |
||
0 ignored issues
–
show
|
|||
230 | $header = $this->createSubmenuHeader($header); |
||
0 ignored issues
–
show
It seems like
$header can also be of type callable ; however, Spatie\Menu\Menu::createSubmenuHeader() does only seem to accept object<Spatie\Menu\Item>|string , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
231 | |||
232 | return $this->add($menu->prependIf($header, $header)); |
||
0 ignored issues
–
show
$header is of type string , but the function expects a boolean .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
233 | } |
||
234 | |||
235 | /** |
||
236 | * @param bool $condition |
||
237 | * @param callable|\Spatie\Menu\Menu|\Spatie\Menu\Item $header |
||
238 | * @param callable|\Spatie\Menu\Menu|null $menu |
||
239 | * |
||
240 | * @return $this |
||
241 | */ |
||
242 | public function submenuIf($condition, $header, $menu = null) |
||
243 | { |
||
244 | if ($condition) { |
||
245 | $this->submenu($header, $menu); |
||
246 | } |
||
247 | |||
248 | return $this; |
||
249 | } |
||
250 | |||
251 | protected function parseSubmenuArgs($args): array |
||
252 | { |
||
253 | if (count($args) === 1) { |
||
254 | return ['', $args[0]]; |
||
255 | } |
||
256 | |||
257 | return [$args[0], $args[1]]; |
||
258 | } |
||
259 | |||
260 | /** |
||
261 | * @param \Spatie\Menu\Menu|callable $menu |
||
262 | * |
||
263 | * @return \Spatie\Menu\Menu |
||
264 | */ |
||
265 | protected function createSubmenuMenu($menu): self |
||
266 | { |
||
267 | if (is_callable($menu)) { |
||
268 | $transformer = $menu; |
||
269 | $menu = $this->blueprint(); |
||
270 | $transformer($menu); |
||
271 | } |
||
272 | |||
273 | return $menu; |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * @param \Spatie\Menu\Item|string $header |
||
278 | * |
||
279 | * @return string |
||
280 | */ |
||
281 | protected function createSubmenuHeader($header): string |
||
282 | { |
||
283 | if ($header instanceof Item) { |
||
284 | $header = $header->render(); |
||
285 | } |
||
286 | |||
287 | return $header; |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * Iterate over all the items and apply a callback. If you typehint the |
||
292 | * item parameter in the callable, it wil only be applied to items of that |
||
293 | * type. |
||
294 | * |
||
295 | * @param callable $callable |
||
296 | * |
||
297 | * @return $this |
||
298 | */ |
||
299 | public function each(callable $callable) |
||
300 | { |
||
301 | $type = Reflection::firstParameterType($callable); |
||
302 | |||
303 | foreach ($this->items as $item) { |
||
304 | if (! Reflection::itemMatchesType($item, $type)) { |
||
305 | continue; |
||
306 | } |
||
307 | |||
308 | $callable($item); |
||
309 | } |
||
310 | |||
311 | return $this; |
||
312 | } |
||
313 | |||
314 | /** |
||
315 | * Register a filter to the menu. When an item is added, all filters will be |
||
316 | * applied to the item. If you typehint the item parameter in the callable, it |
||
317 | * will only be applied to items of that type. |
||
318 | * |
||
319 | * @param callable $callable |
||
320 | * |
||
321 | * @return $this |
||
322 | */ |
||
323 | public function registerFilter(callable $callable) |
||
324 | { |
||
325 | $this->filters[] = $callable; |
||
326 | |||
327 | return $this; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Apply a filter to an item. Returns the result of the filter. |
||
332 | * |
||
333 | * @param callable $filter |
||
334 | * @param \Spatie\Menu\Item $item |
||
335 | */ |
||
336 | protected function applyFilter(callable $filter, Item $item) |
||
337 | { |
||
338 | $type = Reflection::firstParameterType($filter); |
||
339 | |||
340 | if (! Reflection::itemMatchesType($item, $type)) { |
||
341 | return; |
||
342 | } |
||
343 | |||
344 | $filter($item); |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Apply a callable to all existing items, and register it as a filter so it |
||
349 | * will get applied to all new items too. If you typehint the item parameter |
||
350 | * in the callable, it wil only be applied to items of that type. |
||
351 | * |
||
352 | * @param callable $callable |
||
353 | * |
||
354 | * @return $this |
||
355 | */ |
||
356 | public function applyToAll(callable $callable) |
||
357 | { |
||
358 | $this->each($callable); |
||
359 | $this->registerFilter($callable); |
||
360 | |||
361 | return $this; |
||
362 | } |
||
363 | |||
364 | /** |
||
365 | * Wrap the entire menu in an html element. This is another level of |
||
366 | * wrapping above the `wrapperTag`. |
||
367 | * |
||
368 | * @param string $element |
||
369 | * @param array $attributes |
||
370 | * |
||
371 | * @return $this |
||
372 | */ |
||
373 | public function wrap(string $element, $attributes = []) |
||
374 | { |
||
375 | $this->wrap = [$element, $attributes]; |
||
376 | |||
377 | return $this; |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * Determine whether the menu is active. |
||
382 | * |
||
383 | * @return bool |
||
384 | */ |
||
385 | public function isActive(): bool |
||
386 | { |
||
387 | foreach ($this->items as $item) { |
||
388 | if ($item->isActive()) { |
||
389 | return true; |
||
390 | } |
||
391 | } |
||
392 | |||
393 | return false; |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * A menu can be active but not exact-active. |
||
398 | * |
||
399 | * @return bool |
||
400 | */ |
||
401 | public function isExactActive(): bool |
||
402 | { |
||
403 | return false; |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * Set multiple items in the menu as active based on a callable that filters |
||
408 | * through items. If you typehint the item parameter in the callable, it will |
||
409 | * only be applied to items of that type. |
||
410 | * |
||
411 | * @param callable|string $urlOrCallable |
||
412 | * @param string $root |
||
413 | * |
||
414 | * @return $this |
||
415 | */ |
||
416 | public function setActive($urlOrCallable, string $root = '/') |
||
417 | { |
||
418 | if (is_string($urlOrCallable)) { |
||
419 | return $this->setActiveFromUrl($urlOrCallable, $root); |
||
420 | } |
||
421 | |||
422 | if (is_callable($urlOrCallable)) { |
||
423 | return $this->setActiveFromCallable($urlOrCallable); |
||
424 | } |
||
425 | |||
426 | throw new \InvalidArgumentException('`setActive` requires a pattern or a callable'); |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * Set the class name that will be used on exact-active items for this menu. |
||
431 | * |
||
432 | * @param string $class |
||
433 | * |
||
434 | * @return $this |
||
435 | */ |
||
436 | public function setExactActiveClass(string $class) |
||
437 | { |
||
438 | $this->exactActiveClass = $class; |
||
439 | |||
440 | return $this; |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * Set all relevant children active based on the current request's URL. |
||
445 | * |
||
446 | * /, /about, /contact => request to /about will set the about link active. |
||
447 | * |
||
448 | * /en, /en/about, /en/contact => request to /en won't set /en active if the |
||
449 | * request root is set to /en. |
||
450 | * |
||
451 | * @param string $url The current request url. |
||
452 | * @param string $root If the link's URL is an exact match with the request |
||
453 | * root, the link won't be set active. This behavior is |
||
454 | * to avoid having home links active on every request. |
||
455 | * |
||
456 | * @return $this |
||
457 | */ |
||
458 | public function setActiveFromUrl(string $url, string $root = '/') |
||
459 | { |
||
460 | $this->applyToAll(function (self $menu) use ($url, $root) { |
||
461 | $menu->setActiveFromUrl($url, $root); |
||
462 | }); |
||
463 | |||
464 | $this->applyToAll(function (Activatable $item) use ($url, $root) { |
||
465 | $item->determineActiveForUrl($url, $root); |
||
466 | }); |
||
467 | |||
468 | return $this; |
||
469 | } |
||
470 | |||
471 | /** |
||
472 | * @param callable $callable |
||
473 | * |
||
474 | * @return $this |
||
475 | */ |
||
476 | public function setActiveFromCallable(callable $callable) |
||
477 | { |
||
478 | $this->applyToAll(function (self $menu) use ($callable) { |
||
479 | $menu->setActiveFromCallable($callable); |
||
480 | }); |
||
481 | |||
482 | $type = Reflection::firstParameterType($callable); |
||
483 | |||
484 | $this->applyToAll(function (Activatable $item) use ($callable, $type) { |
||
485 | if (! Reflection::itemMatchesType($item, $type)) { |
||
0 ignored issues
–
show
$item is of type object<Spatie\Menu\Activatable> , but the function expects a object<Spatie\Menu\Item> .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
486 | return; |
||
487 | } |
||
488 | |||
489 | if ($callable($item)) { |
||
490 | $item->setActive(); |
||
491 | $item->setExactActive(); |
||
492 | } |
||
493 | }); |
||
494 | |||
495 | return $this; |
||
496 | } |
||
497 | |||
498 | /** |
||
499 | * Set the class name that will be used on active items for this menu. |
||
500 | * |
||
501 | * @param string $class |
||
502 | * |
||
503 | * @return $this |
||
504 | */ |
||
505 | public function setActiveClass(string $class) |
||
506 | { |
||
507 | $this->activeClass = $class; |
||
508 | |||
509 | return $this; |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Add a class to all items in the menu. |
||
514 | * |
||
515 | * @param string $class |
||
516 | * |
||
517 | * @return $this |
||
518 | */ |
||
519 | public function addItemClass(string $class) |
||
520 | { |
||
521 | $this->applyToAll(function (HasHtmlAttributes $link) use ($class) { |
||
522 | $link->addClass($class); |
||
523 | }); |
||
524 | |||
525 | return $this; |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * Set an attribute on all items in the menu. |
||
530 | * |
||
531 | * @param string $attribute |
||
532 | * @param string $value |
||
533 | * |
||
534 | * @return $this |
||
535 | */ |
||
536 | public function setItemAttribute(string $attribute, string $value = '') |
||
537 | { |
||
538 | $this->applyToAll(function (HasHtmlAttributes $link) use ($attribute, $value) { |
||
539 | $link->setAttribute($attribute, $value); |
||
540 | }); |
||
541 | |||
542 | return $this; |
||
543 | } |
||
544 | |||
545 | /** |
||
546 | * Add a parent class to all items in the menu. |
||
547 | * |
||
548 | * @param string $class |
||
549 | * |
||
550 | * @return $this |
||
551 | */ |
||
552 | public function addItemParentClass(string $class) |
||
553 | { |
||
554 | $this->applyToAll(function (HasParentAttributes $item) use ($class) { |
||
555 | $item->addParentClass($class); |
||
556 | }); |
||
557 | |||
558 | return $this; |
||
559 | } |
||
560 | |||
561 | /** |
||
562 | * Add a parent attribute to all items in the menu. |
||
563 | * |
||
564 | * @param string $attribute |
||
565 | * @param string $value |
||
566 | * |
||
567 | * @return $this |
||
568 | */ |
||
569 | public function setItemParentAttribute(string $attribute, string $value = '') |
||
570 | { |
||
571 | $this->applyToAll(function (HasParentAttributes $item) use ($attribute, $value) { |
||
572 | $item->setParentAttribute($attribute, $value); |
||
573 | }); |
||
574 | |||
575 | return $this; |
||
576 | } |
||
577 | |||
578 | /** |
||
579 | * Set tag for items wrapper. |
||
580 | * |
||
581 | * @param string|null $wrapperTagName |
||
582 | * @return $this |
||
583 | */ |
||
584 | public function setWrapperTag($wrapperTagName = null) |
||
585 | { |
||
586 | $this->wrapperTagName = $wrapperTagName; |
||
587 | |||
588 | return $this; |
||
589 | } |
||
590 | |||
591 | /** |
||
592 | * Set tag for items wrapper. |
||
593 | * |
||
594 | * @param string|null $wrapperTagName |
||
0 ignored issues
–
show
There is no parameter named
$wrapperTagName . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not. ![]() |
|||
595 | * @return $this |
||
596 | */ |
||
597 | public function withoutWrapperTag() |
||
598 | { |
||
599 | $this->wrapperTagName = null; |
||
600 | |||
601 | return $this; |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * Set the parent tag name. |
||
606 | * |
||
607 | * @param string|null $parentTagName |
||
608 | * @return $this |
||
609 | */ |
||
610 | public function setParentTag($parentTagName = null) |
||
611 | { |
||
612 | $this->parentTagName = $parentTagName; |
||
0 ignored issues
–
show
It seems like
$parentTagName can also be of type string . However, the property $parentTagName is declared as type boolean . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
613 | |||
614 | return $this; |
||
615 | } |
||
616 | |||
617 | /** |
||
618 | * Render items without a parent tag. |
||
619 | * |
||
620 | * @return $this |
||
621 | */ |
||
622 | public function withoutParentTag() |
||
623 | { |
||
624 | $this->parentTagName = null; |
||
625 | |||
626 | return $this; |
||
627 | } |
||
628 | |||
629 | /** |
||
630 | * Set whether active class should (also) be on link. |
||
631 | * |
||
632 | * @param $activeClassOnLink |
||
633 | * @return $this |
||
634 | */ |
||
635 | public function setActiveClassOnLink(bool $activeClassOnLink = true) |
||
636 | { |
||
637 | $this->activeClassOnLink = $activeClassOnLink; |
||
638 | |||
639 | return $this; |
||
640 | } |
||
641 | |||
642 | /** |
||
643 | * Set whether active class should (also) be on parent. |
||
644 | * |
||
645 | * @param $activeClassOnParent |
||
646 | * @return $this |
||
647 | */ |
||
648 | public function setActiveClassOnParent(bool $activeClassOnParent = true) |
||
649 | { |
||
650 | $this->activeClassOnParent = $activeClassOnParent; |
||
651 | |||
652 | return $this; |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * @param bool $condition |
||
657 | * @param callable $callable |
||
658 | * |
||
659 | * @return $this |
||
660 | */ |
||
661 | public function if(bool $condition, callable $callable) |
||
662 | { |
||
663 | return $condition ? $callable($this) : $this; |
||
664 | } |
||
665 | |||
666 | /** |
||
667 | * Create a empty blueprint of the menu (copies `filters` and `activeClass`). |
||
668 | * |
||
669 | * @return static |
||
670 | */ |
||
671 | public function blueprint() |
||
672 | { |
||
673 | $clone = new static(); |
||
674 | |||
675 | $clone->filters = $this->filters; |
||
676 | $clone->activeClass = $this->activeClass; |
||
677 | |||
678 | return $clone; |
||
679 | } |
||
680 | |||
681 | /** |
||
682 | * Render the menu. |
||
683 | * |
||
684 | * @return string |
||
685 | */ |
||
686 | public function render(): string |
||
687 | { |
||
688 | $tag = $this->wrapperTagName |
||
689 | ? new Tag($this->wrapperTagName, $this->htmlAttributes) |
||
690 | : null; |
||
691 | |||
692 | $contents = array_map([$this, 'renderItem'], $this->items); |
||
693 | |||
694 | $wrappedContents = $tag ? $tag->withContents($contents) : implode('', $contents); |
||
695 | |||
696 | $menu = $this->prepend.$wrappedContents.$this->append; |
||
697 | |||
698 | if (! empty($this->wrap)) { |
||
699 | return Tag::make($this->wrap[0], new Attributes($this->wrap[1]))->withContents($menu); |
||
700 | } |
||
701 | |||
702 | return $menu; |
||
703 | } |
||
704 | |||
705 | protected function renderItem(Item $item): string |
||
706 | { |
||
707 | $attributes = new Attributes(); |
||
708 | |||
709 | if (method_exists($item, 'beforeRender')) { |
||
710 | $item->beforeRender(); |
||
0 ignored issues
–
show
|
|||
711 | } |
||
712 | |||
713 | if (method_exists($item, 'willRender') && $item->willRender() === false) { |
||
0 ignored issues
–
show
|
|||
714 | return ''; |
||
715 | } |
||
716 | |||
717 | if ($item->isActive()) { |
||
718 | if ($this->activeClassOnParent) { |
||
719 | $attributes->addClass($this->activeClass); |
||
720 | |||
721 | if ($item->isExactActive()) { |
||
722 | $attributes->addClass($this->exactActiveClass); |
||
723 | } |
||
724 | } |
||
725 | |||
726 | if ($this->activeClassOnLink && $item instanceof HasHtmlAttributes) { |
||
727 | $item->addClass($this->activeClass); |
||
728 | |||
729 | if ($item->isExactActive()) { |
||
730 | $item->addClass($this->exactActiveClass); |
||
731 | } |
||
732 | } |
||
733 | } |
||
734 | |||
735 | if ($item instanceof HasParentAttributes) { |
||
736 | $attributes->setAttributes($item->parentAttributes()); |
||
737 | } |
||
738 | |||
739 | if (! $this->parentTagName) { |
||
740 | return $item->render(); |
||
741 | } |
||
742 | |||
743 | return Tag::make($this->parentTagName, $attributes)->withContents($item->render()); |
||
0 ignored issues
–
show
$this->parentTagName is of type boolean , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
744 | } |
||
745 | |||
746 | /** |
||
747 | * The amount of items in the menu. |
||
748 | * |
||
749 | * @return int |
||
750 | */ |
||
751 | public function count(): int |
||
752 | { |
||
753 | return count($this->items); |
||
754 | } |
||
755 | |||
756 | /** |
||
757 | * @return string |
||
758 | */ |
||
759 | public function __toString(): string |
||
760 | { |
||
761 | return $this->render(); |
||
762 | } |
||
763 | |||
764 | public function getIterator(): Traversable |
||
765 | { |
||
766 | return new ArrayIterator($this->items); |
||
767 | } |
||
768 | } |
||
769 |
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.