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 ( c5ea8e...25ce45 )
by Sebastian
01:58
created

Menu::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Spatie\Menu;
4
5
use ReflectionFunction;
6
use ReflectionParameter;
7
use Spatie\HtmlElement\HtmlElement;
8
use Spatie\Menu\Items\Link;
9
use Spatie\Menu\Traits\HtmlAttributes;
10
use Spatie\Menu\Traits\ParentAttributes;
11
12
class Menu implements Item
13
{
14
    use HtmlAttributes, ParentAttributes;
15
16
    /** @var array */
17
    protected $items = [];
18
19
    /** @var string */
20
    protected $prepend = '';
21
22
    /** @var string */
23
    protected $append = '';
24
25
    /** @var array */
26
    protected $filters = [];
27
28
    /**
29
     * @param \Spatie\Menu\Item[] ...$items
30
     */
31
    protected function __construct(Item ...$items)
32
    {
33
        $this->items = $items;
34
    }
35
36
    /**
37
     * Create a new menu, optionally prefilled with items.
38
     *
39
     * @param array $items
40
     *
41
     * @return static
42
     */
43
    public static function new(array $items = [])
44
    {
45
        return new static(...array_values($items));
46
    }
47
48
    /**
49
     * Add an item to the menu. This also applies all registered filters on the item. If a filter
50
     * returns false, the item won't be added.
51
     *
52
     * @param \Spatie\Menu\Item $item
53
     *
54
     * @return $this
55
     */
56
    public function add(Item $item)
57
    {
58
        if ($this->applyFilters($item) === false) {
59
            return $this;
60
        }
61
62
        $this->items[] = $item;
63
64
        return $this;
65
    }
66
67
    /**
68
     * Applies all the currently registered filters to an item.
69
     *
70
     * @param \Spatie\Menu\Item $item
71
     *
72
     * @return bool
73
     */
74
    protected function applyFilters(Item $item) : bool
75
    {
76
        foreach ($this->filters as $filter) {
77
            if ($this->applyFilter($filter, $item) === false) {
78
                return false;
79
            }
80
        }
81
82
        return true;
83
    }
84
85
    /**
86
     * Apply a filter to an item. Returns the result of the filter.
87
     *
88
     * @param callable $filter
89
     * @param \Spatie\Menu\Item $item
90
     *
91
     * @return mixed
92
     */
93
    protected function applyFilter(callable $filter, Item $item)
94
    {
95
        $type = $this->determineFirstParameterType($filter);
96
97
        if ($type !== null && !$item instanceof $type) {
98
            return;
99
        }
100
101
        return $filter($item);
102
    }
103
104
    /**
105
     * Map through all the items and return an array containing the result. If you typehint the
106
     * item parameter in the callable, it wil only be applied to items of that type.
107
     *
108
     * @param callable $callable
109
     *
110
     * @return array
111
     */
112
    public function map(callable $callable) : array
113
    {
114
        $type = $this->determineFirstParameterType($callable);
115
116
        $items = $this->items;
117
118
        if ($type !== null) {
119
            $items = array_filter($items, function (Item $item) use ($type) {
120
                return $item instanceof $type;
121
            });
122
        }
123
124
        return array_map($callable, $items);
125
    }
126
127
    /**
128
     * Iterate over all the items and apply a callback. If you typehint the
129
     * item parameter in the callable, it wil only be applied to items of that type.
130
     *
131
     * @param callable $callable
132
     *
133
     * @return $this
134
     */
135
    public function each(callable $callable)
136
    {
137
        $type = $this->determineFirstParameterType($callable);
138
139
        foreach ($this->items as $item) {
140
            if ($type !== null && !$item instanceof $type) {
141
                continue;
142
            }
143
144
            $callable($item);
145
        }
146
147
        return $this;
148
    }
149
150
    /**
151
     * Register a filter to the menu. When an item is added, all filters will be applied to the
152
     * item. If a filter returns false, the item won't be added. If you typehint the item
153
     * parameter in the callable, it wil only be applied to items of that type.
154
     *
155
     * @param callable $callable
156
     *
157
     * @return $this
158
     */
159
    public function registerFilter(callable $callable)
160
    {
161
        $this->filters[] = $callable;
162
163
        return $this;
164
    }
165
166
    /**
167
     * Apply a callable to all existing items, and register it as a filter so it will get applied
168
     * to all new items too. If you typehint the item parameter in the callable, it wil only be
169
     * applied to items of that type.
170
     *
171
     * @param callable $callable
172
     *
173
     * @return $this
174
     */
175
    public function applyToAll(callable $callable)
176
    {
177
        $this->each($callable);
178
        $this->registerFilter($callable);
179
180
        return $this;
181
    }
182
183
    /**
184
     * Determine the type of the first parameter of a callable.
185
     *
186
     * @param callable $callable
187
     *
188
     * @return string|null
189
     */
190
    protected function determineFirstParameterType(callable $callable)
191
    {
192
        $reflection = new ReflectionFunction($callable);
193
194
        $parameterTypes = array_map(function (ReflectionParameter $parameter) {
195
            return $parameter->getClass() ? $parameter->getClass()->name : null;
196
        }, $reflection->getParameters());
197
198
        return $parameterTypes[0] ?? null;
199
    }
200
201
    /**
202
     * Prepend a string of html to the menu on render.
203
     *
204
     * @param string $prefix
205
     *
206
     * @return $this
207
     */
208
    public function prefixLinks(string $prefix)
209
    {
210
        return $this->applyToAll(function (Link $link) use ($prefix) {
211
            $link->prefix($prefix);
212
        });
213
    }
214
215
    /**
216
     * Prepend the menu with a string of html on render.
217
     *
218
     * @param string $prepend
219
     *
220
     * @return $this
221
     */
222
    public function prepend(string $prepend)
223
    {
224
        $this->prepend = $prepend;
225
226
        return $this;
227
    }
228
229
    /**
230
     * Prepend the menu with a string of html on render if a certain condition is met.
231
     *
232
     * @param bool $condition
233
     * @param string $prepend
234
     *
235
     * @return $this
236
     */
237
    public function prependIf(bool $condition, string $prepend)
238
    {
239
        if ($condition) {
240
            $this->prepend($prepend);
241
        }
242
243
       return $this;
244
    }
245
246
    /**
247
     * Append a string of html to the menu on render.
248
     *
249
     * @param string $append
250
     *
251
     * @return $this
252
     */
253
    public function append(string $append)
254
    {
255
        $this->append = $append;
256
257
        return $this;
258
    }
259
260
    /**
261
     * Append the menu with a string of html on render if a certain condition is met.
262
     *
263
     * @param bool $condition
264
     * @param string $append
265
     *
266
     * @return $this
267
     */
268
    public function appendIf(bool $condition, string $append)
269
    {
270
        if ($condition) {
271
            $this->append($append);
272
        }
273
274
        return $this;
275
    }
276
277
    /**
278
     * Determine whether the menu is active.
279
     *
280
     * @return bool
281
     */
282
    public function isActive() : bool
283
    {
284
        foreach ($this->items as $item) {
285
            if ($item->isActive()) {
286
                return true;
287
            }
288
        }
289
290
        return false;
291
    }
292
293
    /**
294
     * Set multiple items in the menu as active based on a callable that filters through items.
295
     * If you typehint the item parameter in the callable, it wil only be applied to items of
296
     * that type.
297
     *
298
     * @param callable|string $patternOrCallable
299
     * @param string $root
300
     *
301
     * @return $this
302
     */
303
    public function setActive($patternOrCallable, string $root = '')
304
    {
305
        if (is_string($patternOrCallable)) {
306
            return $this->setActiveFromPattern($patternOrCallable, $root);
307
        }
308
309
        if (is_callable($patternOrCallable)) {
310
            return $this->setActiveFromCallable();
0 ignored issues
show
Bug introduced by
The call to setActiveFromCallable() misses a required argument $callable.

This check looks for function calls that miss required arguments.

Loading history...
311
        }
312
313
        throw new \InvalidArgumentException('`setActive` requires a pattern or a callable');
314
    }
315
316
    public function setActiveFromPattern(string $pattern, string $root = '')
0 ignored issues
show
Unused Code introduced by
The parameter $pattern 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 $root 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...
317
    {
318
        // ...
319
    }
320
321
    public function setActiveFromCallable(callable $callable)
322
    {
323
        $type = $this->determineFirstParameterType($callable);
324
325
        return $this->applyToAll(function (Item $item) use ($callable, $type) {
326
            if ($type !== null && !$item instanceof $type) {
327
                return;
328
            }
329
330
            if ($callable($item)) {
331
                $item->setActive();
332
            }
333
        });
334
    }
335
336
    /**
337
     * @return string
338
     */
339
    public function render() : string
340
    {
341
        $menu = HtmlElement::render(
342
            'ul',
343
            $this->attributes()->toArray(),
344
            $this->map(function (Item $item) {
345
                return HtmlElement::render(
346
                    $item->isActive() ? 'li.active' : 'li',
347
                    $item->getParentAttributes(),
348
                    $item->render()
349
                );
350
            })
351
        );
352
353
        return "{$this->prepend}{$menu}{$this->append}";
354
    }
355
356
    /**
357
     * @return string
358
     */
359
    public function __toString() : string
360
    {
361
        return $this->render();
362
    }
363
}
364