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 ( 4098b2...8a3de5 )
by Sebastian
02:04
created

Menu::setActiveFromCallable()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 14
rs 9.4285
cc 3
eloc 7
nc 1
nop 1
1
<?php
2
3
namespace Spatie\Menu;
4
5
use Countable;
6
use Spatie\HtmlElement\HtmlElement;
7
use Spatie\Menu\Traits\HtmlAttributes;
8
use Spatie\Menu\Traits\ParentAttributes;
9
10
class Menu implements Countable, Item
11
{
12
    use HtmlAttributes, ParentAttributes;
13
14
    /** @var array */
15
    protected $items = [];
16
17
    /** @var string */
18
    protected $prepend = '';
19
20
    /** @var string */
21
    protected $append = '';
22
23
    /** @var array */
24
    protected $filters = [];
25
26
    /** @var string */
27
    protected $activeClass = 'active';
28
29
    /**
30
     * @param \Spatie\Menu\Item[] ...$items
31
     */
32
    protected function __construct(Item ...$items)
33
    {
34
        $this->items = $items;
35
36
        $this->bootHtmlAttributes();
37
        $this->bootParentAttributes();
38
    }
39
40
    /**
41
     * Create a new menu, optionally prefilled with items.
42
     *
43
     * @param array $items
44
     *
45
     * @return static
46
     */
47
    public static function new(array $items = [])
48
    {
49
        return new static(...array_values($items));
50
    }
51
52
    /**
53
     * Add an item to the menu. This also applies all registered filters on the item. If a filter
54
     * returns false, the item won't be added.
55
     *
56
     * @param \Spatie\Menu\Item $item
57
     *
58
     * @return $this
59
     */
60
    public function add(Item $item)
61
    {
62
        foreach ($this->filters as $filter) {
63
            $this->applyFilter($filter, $item);
64
        }
65
66
        $this->items[] = $item;
67
68
        return $this;
69
    }
70
71
    /**
72
     * Shortcut function to add a plain link to the menu.
73
     *
74
     * @param string $url
75
     * @param string $text
76
     *
77
     * @return $this
78
     */
79
    public function link(string $url, string $text)
80
    {
81
        return $this->add(Link::to($url, $text));
82
    }
83
84
    /**
85
     * Shortcut function to add raw html to the menu.
86
     *
87
     * @param string $html
88
     *
89
     * @return $this
90
     */
91
    public function html(string $html)
92
    {
93
        return $this->add(Html::raw($html));
94
    }
95
96
    /**
97
     * Apply a filter to an item. Returns the result of the filter.
98
     *
99
     * @param callable $filter
100
     * @param \Spatie\Menu\Item $item
101
     */
102
    protected function applyFilter(callable $filter, Item $item)
103
    {
104
        $type = first_parameter_type($filter);
105
106
        if (!item_matches_type($item, $type)) {
107
            return;
108
        }
109
110
        $filter($item);
111
    }
112
113
    /**
114
     * Iterate over all the items and apply a callback. If you typehint the
115
     * item parameter in the callable, it wil only be applied to items of that type.
116
     *
117
     * @param callable $callable
118
     *
119
     * @return $this
120
     */
121
    public function each(callable $callable)
122
    {
123
        $type = first_parameter_type($callable);
124
125
        foreach ($this->items as $item) {
126
            if (!item_matches_type($item, $type)) {
127
                continue;
128
            }
129
130
            $callable($item);
131
        }
132
133
        return $this;
134
    }
135
136
    /**
137
     * Register a filter to the menu. When an item is added, all filters will be applied to the
138
     * item. If you typehint the item
139
     * parameter in the callable, it wil only be applied to items of that type.
140
     *
141
     * @param callable $callable
142
     *
143
     * @return $this
144
     */
145
    public function registerFilter(callable $callable)
146
    {
147
        $this->filters[] = $callable;
148
149
        return $this;
150
    }
151
152
    /**
153
     * Apply a callable to all existing items, and register it as a filter so it will get applied
154
     * to all new items too. If you typehint the item parameter in the callable, it wil only be
155
     * applied to items of that type.
156
     *
157
     * @param callable $callable
158
     *
159
     * @return $this
160
     */
161
    public function applyToAll(callable $callable)
162
    {
163
        $this->each($callable);
164
        $this->registerFilter($callable);
165
166
        return $this;
167
    }
168
169
    /**
170
     * Prepend a string of html to the menu on render.
171
     *
172
     * @param string $prefix
173
     *
174
     * @return $this
175
     */
176
    public function prefixLinks(string $prefix)
177
    {
178
        return $this->applyToAll(function (Link $link) use ($prefix) {
179
            $link->prefix($prefix);
180
        });
181
    }
182
183
    /**
184
     * Prepend the menu with a string of html on render.
185
     *
186
     * @param string $prepend
187
     *
188
     * @return $this
189
     */
190
    public function prepend(string $prepend)
191
    {
192
        $this->prepend = $prepend;
193
194
        return $this;
195
    }
196
197
    /**
198
     * Prepend the menu with a string of html on render if a certain condition is met.
199
     *
200
     * @param bool $condition
201
     * @param string $prepend
202
     *
203
     * @return $this
204
     */
205
    public function prependIf(bool $condition, string $prepend)
206
    {
207
        if ($condition) {
208
            return $this->prepend($prepend);
209
        }
210
211
        return $this;
212
    }
213
214
    /**
215
     * Append a string of html to the menu on render.
216
     *
217
     * @param string $append
218
     *
219
     * @return $this
220
     */
221
    public function append(string $append)
222
    {
223
        $this->append = $append;
224
225
        return $this;
226
    }
227
228
    /**
229
     * Append the menu with a string of html on render if a certain condition is met.
230
     *
231
     * @param bool $condition
232
     * @param string $append
233
     *
234
     * @return static
235
     */
236
    public function appendIf(bool $condition, string $append)
237
    {
238
        if ($condition) {
239
            return $this->append($append);
240
        }
241
242
        return $this;
243
    }
244
245
    /**
246
     * Determine whether the menu is active.
247
     *
248
     * @return bool
249
     */
250
    public function isActive() : bool
251
    {
252
        foreach ($this->items as $item) {
253
            if ($item->isActive()) {
254
                return true;
255
            }
256
        }
257
258
        return false;
259
    }
260
261
    /**
262
     * Set multiple items in the menu as active based on a callable that filters through items.
263
     * If you typehint the item parameter in the callable, it wil only be applied to items of
264
     * that type.
265
     *
266
     * @param callable|string $patternOrCallable
267
     * @param string $root
268
     *
269
     * @return $this
270
     */
271
    public function setActive($patternOrCallable, string $root = '/')
272
    {
273
        if (is_string($patternOrCallable)) {
274
            return $this->setActiveFromUrl($patternOrCallable, $root);
275
        }
276
277
        if (is_callable($patternOrCallable)) {
278
            return $this->setActiveFromCallable($patternOrCallable);
279
        }
280
281
        throw new \InvalidArgumentException('`setActive` requires a pattern or a callable');
282
    }
283
284
    /**
285
     * Set all relevant children active based on the current request's URL.
286
     *
287
     * /, /about, /contact => request to /about will set the about link active.
288
     *
289
     * /en, /en/about, /en/contact => request to /en won't set /en active if the request root
290
     *                                is set to /en.
291
     *
292
     * @param string $url The current request url.
293
     * @param string $root If the link's URL is an exact match with the request root, the
294
     *                            link won't be set active. This behavior is to avoid having home
295
     *                            links active on every request.
296
     *
297
     * @return $this
298
     */
299
    public function setActiveFromUrl(string $url, string $root = '/')
300
    {
301
        $requestUrl = url_parts($url);
302
        $requestRoot = strip_trailing_slashes($root, '/');
303
304
        $this->applyToAll(function (Link $link) use ($requestUrl, $requestRoot) {
305
306
            $url = url_parts($link->getUrl());
307
308
            // If the menu item is on a different host it can't be active.
309
            if ($url['host'] !== '' && $url['host'] !== $requestUrl['host']) {
310
                return;
311
            }
312
313
            // If the request url or the link url is on the root, only set exact matches active.
314
            if (
315
                $requestUrl['path'] === $requestRoot ||
316
                $url['path'] === $requestRoot
317
            ) {
318
                if ($url['path'] === $requestUrl['path']) {
319
                    $link->setActive();
320
                }
321
322
                return;
323
            }
324
            
325
            // If the request path is empty and it isn't the root, there's most likely a
326
            // configuration error, and the item isn't active.
327
            if (empty($url['path'])) {
328
                return;
329
            }
330
331
            // The menu item is active if it's path starts with the request path.
332
            if (strpos($url['path'], $requestUrl['path']) === 0) {
333
                $link->setActive();
334
            };
335
        });
336
337
        return $this;
338
    }
339
340
    /**
341
     * @param callable $callable
342
     *
343
     * @return $this
344
     */
345
    public function setActiveFromCallable(callable $callable)
346
    {
347
        $type = first_parameter_type($callable);
348
349
        return $this->applyToAll(function (Item $item) use ($callable, $type) {
350
            if (!item_matches_type($item, $type)) {
351
                return;
352
            }
353
354
            if ($callable($item)) {
355
                $item->setActive();
356
            }
357
        });
358
    }
359
360
    /**
361
     * @param string $class
362
     *
363
     * @return $this
364
     */
365
    public function setActiveClass(string $class)
366
    {
367
        $this->activeClass = $class;
368
369
        return $this;
370
    }
371
372
    /**
373
     * @return string
374
     */
375
    public function render() : string
376
    {
377
        $contents = HtmlElement::render(
378
            'ul',
379
            $this->htmlAttributes->toArray(),
380
            array_map(function (Item $item) {
381
                return HtmlElement::render(
382
                    $item->isActive() ? "li.{$this->activeClass}" : 'li',
383
                    $item->getParentAttributes(),
384
                    $item->render()
385
                );
386
            }, $this->items)
387
        );
388
389
        return "{$this->prepend}{$contents}{$this->append}";
390
    }
391
392
    /**
393
     * @return int
394
     */
395
    public function count() : int
396
    {
397
        return count($this->items);
398
    }
399
400
    /**
401
     * @return string
402
     */
403
    public function __toString() : string
404
    {
405
        return $this->render();
406
    }
407
}
408