GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 5d4268...446cb4 )
by Sebastian
11s
created

Menu::empty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Spatie\Menu;
4
5
use Countable;
6
use Spatie\Menu\Html\Tag;
7
use Spatie\Menu\Html\Attributes;
8
use Spatie\Menu\Helpers\Reflection;
9
use Spatie\Menu\Traits\Conditions as ConditionsTrait;
10
use Spatie\Menu\Traits\HasTextAttributes as HasAttributesTrait;
11
use Spatie\Menu\Traits\HasHtmlAttributes as HasHtmlAttributesTrait;
12
use Spatie\Menu\Traits\HasParentAttributes as HasParentAttributesTrait;
13
14
class Menu implements Item, Countable, HasHtmlAttributes, HasParentAttributes
15
{
16
    use HasHtmlAttributesTrait, HasParentAttributesTrait, ConditionsTrait, HasAttributesTrait;
17
18
    /** @var array */
19
    protected $items = [];
20
21
    /** @var array */
22
    protected $filters = [];
23
24
    /** @var string */
25
    protected $prepend, $append = '';
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
26
27
    /** @var array */
28
    protected $wrap = [];
29
30
    /** @var string */
31
    protected $activeClass = 'active';
32
33
    /** @var \Spatie\Menu\Html\Attributes */
34
    protected $htmlAttributes, $parentAttributes;
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
35
36
    protected function __construct(Item ...$items)
37
    {
38
        $this->items = $items;
39
40
        $this->htmlAttributes = new Attributes();
41
        $this->parentAttributes = new Attributes();
42
    }
43
44
    /**
45
     * Create a new menu, optionally prefilled with items.
46
     *
47
     * @param array $items
48
     *
49
     * @return static
50
     */
51
    public static function new($items = [])
52
    {
53
        return new static(...array_values($items));
54
    }
55
56
    /**
57
     * Build a new menu from an array. The callback receives a menu instance as
58
     * the accumulator, the array item as the second parameter, and the item's
59
     * key as the third.
60
     *
61
     * @param array|\Iterator $items
62
     * @param callable $callback
63
     * @param \Spatie\Menu\Menu|null $initial
64
     *
65
     * @return static
66
     */
67
    public static function build($items, callable $callback, Menu $initial = null)
68
    {
69
        return ($initial ?: static::new())->fill($items, $callback);
70
    }
71
72
    /**
73
     * Fill a menu from an array. The callback receives a menu instance as
74
     * the accumulator, the array item as the second parameter, and the item's
75
     * key as the third.
76
     *
77
     * @param array|\Iterator $items
78
     * @param callable $callback
79
     *
80
     * @return static
81
     */
82
    public function fill($items, callable $callback)
83
    {
84
        $menu = $this;
85
86
        foreach ($items as $key => $item) {
87
            $menu = $callback($menu, $item, $key) ?: $menu;
88
        }
89
90
        return $menu;
91
    }
92
93
    /**
94
     * Add an item to the menu. This also applies all registered filters to the
95
     * item.
96
     *
97
     * @param \Spatie\Menu\Item $item
98
     *
99
     * @return $this
100
     */
101
    public function add(Item $item)
102
    {
103
        foreach ($this->filters as $filter) {
104
            $this->applyFilter($filter, $item);
105
        }
106
107
        $this->items[] = $item;
108
109
        return $this;
110
    }
111
112
    /**
113
     * Add an item to the menu if a (non-strict) condition is met.
114
     *
115
     * @param bool              $condition
116
     * @param \Spatie\Menu\Item $item
117
     *
118
     * @return $this
119
     */
120
    public function addIf($condition, Item $item)
121
    {
122
        if ($this->resolveCondition($condition)) {
123
            $this->add($item);
124
        }
125
126
        return $this;
127
    }
128
129
    /**
130
     * Shortcut function to add a plain link to the menu.
131
     *
132
     * @param string $url
133
     * @param string $text
134
     *
135
     * @return $this
136
     */
137
    public function link(string $url, string $text)
138
    {
139
        return $this->add(Link::to($url, $text));
140
    }
141
142
    /**
143
     * Shortcut function to add an empty item to the menu.
144
     *
145
     * @return $this
146
     */
147
    public function empty()
148
    {
149
        return $this->add(Html::empty());
150
    }
151
152
    /**
153
     * Add a link to the menu if a (non-strict) condition is met.
154
     *
155
     * @param bool   $condition
156
     * @param string $url
157
     * @param string $text
158
     *
159
     * @return $this
160
     */
161
    public function linkIf($condition, string $url, string $text)
162
    {
163
        if ($this->resolveCondition($condition)) {
164
            $this->link($url, $text);
165
        }
166
167
        return $this;
168
    }
169
170
    /**
171
     * Shortcut function to add raw html to the menu.
172
     *
173
     * @param string $html
174
     * @param array  $parentAttributes
175
     *
176
     * @return $this
177
     */
178
    public function html(string $html, array $parentAttributes = [])
179
    {
180
        return $this->add(Html::raw($html)->setParentAttributes($parentAttributes));
181
    }
182
183
    /**
184
     * Add a chunk of html if a (non-strict) condition is met.
185
     *
186
     * @param bool   $condition
187
     * @param string $html
188
     * @param array  $parentAttributes
189
     *
190
     * @return $this
191
     */
192
    public function htmlIf($condition, string $html, array $parentAttributes = [])
193
    {
194
        if ($this->resolveCondition($condition)) {
195
            $this->html($html, $parentAttributes);
196
        }
197
198
        return $this;
199
    }
200
201
    /**
202
     * @param callable|\Spatie\Menu\Menu|\Spatie\Menu\Item $header
203
     * @param callable|\Spatie\Menu\Menu|null $menu
204
     *
205
     * @return $this
206
     */
207
    public function submenu($header, $menu = null)
0 ignored issues
show
Unused Code introduced by
The parameter $header is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $menu is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
208
    {
209
        list($header, $menu) = $this->parseSubmenuArgs(func_get_args());
210
211
        $menu = $this->createSubmenuMenu($menu);
212
        $header = $this->createSubmenuHeader($header);
213
214
        return $this->add($menu->prependIf($header, $header));
0 ignored issues
show
Documentation introduced by
$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);
Loading history...
215
    }
216
217
    /**
218
     * @param bool $condition
219
     * @param callable|\Spatie\Menu\Menu|\Spatie\Menu\Item $header
220
     * @param callable|\Spatie\Menu\Menu|null $menu
221
     *
222
     * @return $this
223
     */
224
    public function submenuIf($condition, $header, $menu = null)
225
    {
226
        if ($condition) {
227
            $this->submenu($header, $menu);
228
        }
229
230
        return $this;
231
    }
232
233
    protected function parseSubmenuArgs($args): array
234
    {
235
        if (count($args) === 1) {
236
            return ['', $args[0]];
237
        }
238
239
        return [$args[0], $args[1]];
240
    }
241
242
    /**
243
     * @param \Spatie\Menu\Menu|callable $menu
244
     *
245
     * @return \Spatie\Menu\Menu
246
     */
247
    protected function createSubmenuMenu($menu): Menu
248
    {
249
        if (is_callable($menu)) {
250
            $transformer = $menu;
251
            $menu = $this->blueprint();
252
            $transformer($menu);
253
        }
254
255
        return $menu;
256
    }
257
258
    /**
259
     * @param \Spatie\Menu\Item|string $header
260
     *
261
     * @return string
262
     */
263
    protected function createSubmenuHeader($header): string
264
    {
265
        if ($header instanceof Item) {
266
            $header = $header->render();
267
        }
268
269
        return $header;
270
    }
271
272
    /**
273
     * Iterate over all the items and apply a callback. If you typehint the
274
     * item parameter in the callable, it wil only be applied to items of that
275
     * type.
276
     *
277
     * @param callable $callable
278
     *
279
     * @return $this
280
     */
281
    public function each(callable $callable)
282
    {
283
        $type = Reflection::firstParameterType($callable);
284
285
        foreach ($this->items as $item) {
286
            if (! Reflection::itemMatchesType($item, $type)) {
287
                continue;
288
            }
289
290
            $callable($item);
291
        }
292
293
        return $this;
294
    }
295
296
    /**
297
     * Register a filter to the menu. When an item is added, all filters will be
298
     * applied to the item. If you typehint the item parameter in the callable, it
299
     * will only be applied to items of that type.
300
     *
301
     * @param callable $callable
302
     *
303
     * @return $this
304
     */
305
    public function registerFilter(callable $callable)
306
    {
307
        $this->filters[] = $callable;
308
309
        return $this;
310
    }
311
312
    /**
313
     * Apply a filter to an item. Returns the result of the filter.
314
     *
315
     * @param callable          $filter
316
     * @param \Spatie\Menu\Item $item
317
     */
318
    protected function applyFilter(callable $filter, Item $item)
319
    {
320
        $type = Reflection::firstParameterType($filter);
321
322
        if (! Reflection::itemMatchesType($item, $type)) {
323
            return;
324
        }
325
326
        $filter($item);
327
    }
328
329
    /**
330
     * Apply a callable to all existing items, and register it as a filter so it
331
     * will get applied to all new items too. If you typehint the item parameter
332
     * in the callable, it wil only be applied to items of that type.
333
     *
334
     * @param callable $callable
335
     *
336
     * @return $this
337
     */
338
    public function applyToAll(callable $callable)
339
    {
340
        $this->each($callable);
341
        $this->registerFilter($callable);
342
343
        return $this;
344
    }
345
346
    /**
347
     * Wrap the menu in an html element.
348
     *
349
     * @param string $element
350
     * @param array $attributes
351
     *
352
     * @return $this
353
     */
354
    public function wrap(string $element, $attributes = [])
355
    {
356
        $this->wrap = [$element, $attributes];
357
358
        return $this;
359
    }
360
361
    /**
362
     * Determine whether the menu is active.
363
     *
364
     * @return bool
365
     */
366
    public function isActive(): bool
367
    {
368
        foreach ($this->items as $item) {
369
            if ($item->isActive()) {
370
                return true;
371
            }
372
        }
373
374
        return false;
375
    }
376
377
    /**
378
     * Set multiple items in the menu as active based on a callable that filters
379
     * through items. If you typehint the item parameter in the callable, it will
380
     * only be applied to items of that type.
381
     *
382
     * @param callable|string $urlOrCallable
383
     * @param string          $root
384
     *
385
     * @return $this
386
     */
387
    public function setActive($urlOrCallable, string $root = '/')
388
    {
389
        if (is_string($urlOrCallable)) {
390
            return $this->setActiveFromUrl($urlOrCallable, $root);
391
        }
392
393
        if (is_callable($urlOrCallable)) {
394
            return $this->setActiveFromCallable($urlOrCallable);
395
        }
396
397
        throw new \InvalidArgumentException('`setActive` requires a pattern or a callable');
398
    }
399
400
    /**
401
     * Set all relevant children active based on the current request's URL.
402
     *
403
     * /, /about, /contact => request to /about will set the about link active.
404
     *
405
     * /en, /en/about, /en/contact => request to /en won't set /en active if the
406
     *                                request root is set to /en.
407
     *
408
     * @param string $url  The current request url.
409
     * @param string $root If the link's URL is an exact match with the request
410
     *                     root, the link won't be set active. This behavior is
411
     *                     to avoid having home links active on every request.
412
     *
413
     * @return $this
414
     */
415
    public function setActiveFromUrl(string $url, string $root = '/')
416
    {
417
        $this->applyToAll(function (Menu $menu) use ($url, $root) {
418
            $menu->setActiveFromUrl($url, $root);
419
        });
420
421
        $this->applyToAll(function (Activatable $item) use ($url, $root) {
422
            $item->determineActiveForUrl($url, $root);
423
        });
424
425
        return $this;
426
    }
427
428
    /**
429
     * @param callable $callable
430
     *
431
     * @return $this
432
     */
433
    public function setActiveFromCallable(callable $callable)
434
    {
435
        $this->applyToAll(function (Menu $menu) use ($callable) {
436
            $menu->setActiveFromCallable($callable);
437
        });
438
439
        $type = Reflection::firstParameterType($callable);
440
441
        $this->applyToAll(function (Activatable $item) use ($callable, $type) {
442
            if (! Reflection::itemMatchesType($item, $type)) {
0 ignored issues
show
Documentation introduced by
$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);
Loading history...
443
                return;
444
            }
445
446
            if ($callable($item)) {
447
                $item->setActive();
448
            }
449
        });
450
451
        return $this;
452
    }
453
454
    /**
455
     * Set the class name that will be used on active items for this menu.
456
     *
457
     * @param string $class
458
     *
459
     * @return $this
460
     */
461
    public function setActiveClass(string $class)
462
    {
463
        $this->activeClass = $class;
464
465
        return $this;
466
    }
467
468
    /**
469
     * Add a class to all items in the menu.
470
     *
471
     * @param string $class
472
     *
473
     * @return $this
474
     */
475
    public function addItemClass(string $class)
476
    {
477
        $this->applyToAll(function (HasHtmlAttributes $link) use ($class) {
478
            $link->addClass($class);
479
        });
480
481
        return $this;
482
    }
483
484
    /**
485
     * Set an attribute on all items in the menu.
486
     *
487
     * @param string $attribute
488
     * @param string $value
489
     *
490
     * @return $this
491
     */
492
    public function setItemAttribute(string $attribute, string $value = '')
493
    {
494
        $this->applyToAll(function (HasHtmlAttributes $link) use ($attribute, $value) {
495
            $link->setAttribute($attribute, $value);
496
        });
497
498
        return $this;
499
    }
500
501
    /**
502
     * Add a parent class to all items in the menu.
503
     *
504
     * @param string $class
505
     *
506
     * @return $this
507
     */
508
    public function addItemParentClass(string $class)
509
    {
510
        $this->applyToAll(function (HasParentAttributes $item) use ($class) {
511
            $item->addParentClass($class);
512
        });
513
514
        return $this;
515
    }
516
517
    /**
518
     * Add a parent attribute to all items in the menu.
519
     *
520
     * @param string $attribute
521
     * @param string $value
522
     *
523
     * @return $this
524
     */
525
    public function setItemParentAttribute(string $attribute, string $value = '')
526
    {
527
        $this->applyToAll(function (HasParentAttributes $item) use ($attribute, $value) {
528
            $item->setParentAttribute($attribute, $value);
529
        });
530
531
        return $this;
532
    }
533
534
    /**
535
     * @param bool $condition
536
     * @param callable $callable
537
     *
538
     * @return $this
539
     */
540
    public function if(bool $condition, callable $callable)
0 ignored issues
show
Coding Style introduced by
Possible parse error: non-abstract method defined as abstract
Loading history...
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
541
    {
542
        return $condition ? $callable($this) : $this;
543
    }
544
545
    /**
546
     * Create a empty blueprint of the menu (copies `filters` and `activeClass`).
547
     *
548
     * @return static
549
     */
550
    public function blueprint()
551
    {
552
        $clone = new static();
553
554
        $clone->filters = $this->filters;
555
        $clone->activeClass = $this->activeClass;
556
557
        return $clone;
558
    }
559
560
    /**
561
     * Render the menu.
562
     *
563
     * @return string
564
     */
565
    public function render(): string
566
    {
567
        $tag = new Tag('ul', $this->htmlAttributes);
568
569
        $contents = array_map([$this, 'renderItem'], $this->items);
570
571
        $menu = $this->prepend.$tag->withContents($contents).$this->append;
572
573
        if (! empty($this->wrap)) {
574
            return Tag::make($this->wrap[0], new Attributes($this->wrap[1]))->withContents($menu);
575
        }
576
577
        return $menu;
578
    }
579
580
    protected function renderItem(Item $item): string
581
    {
582
        $attributes = new Attributes();
583
584
        if ($item->isActive()) {
585
            $attributes->addClass($this->activeClass);
586
        }
587
588
        if ($item instanceof HasParentAttributes) {
589
            $attributes->setAttributes($item->parentAttributes());
590
        }
591
592
        return Tag::make('li', $attributes)->withContents($item->render());
593
    }
594
595
    /**
596
     * The amount of items in the menu.
597
     *
598
     * @return int
599
     */
600
    public function count(): int
601
    {
602
        return count($this->items);
603
    }
604
605
    /**
606
     * @return string
607
     */
608
    public function __toString(): string
609
    {
610
        return $this->render();
611
    }
612
}
613