Completed
Push — develop ( 194bfe...54607a )
by Abdelrahman
01:11
created

MenuItem   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 380
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 1

Importance

Changes 0
Metric Value
wmc 43
lcom 4
cbo 1
dl 0
loc 380
rs 8.3157
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A fill() 0 6 1
A add() 0 7 1
A dropdown() 0 6 1
A route() 0 4 1
A url() 0 4 1
A header() 0 6 1
A divider() 0 4 1
A getChilds() 0 4 1
A getUrl() 0 4 3
A getAttributes() 0 4 1
A isDivider() 0 4 1
A isHeader() 0 4 1
A hasChilds() 0 4 1
A hasActiveOnChild() 0 8 3
B hasActiveStateFromChilds() 0 12 7
A inactive() 0 12 3
B isActive() 0 20 5
A hasActiveStateFromRoute() 0 4 1
A hasActiveStateFromUrl() 0 4 1
A hideWhen() 0 6 1
A can() 0 8 1
A user() 0 8 1
A guest() 0 8 1
A hidden() 0 4 2
A __get() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like MenuItem often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MenuItem, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Rinvex\Menus\Models;
6
7
use Illuminate\Support\Facades\Route;
8
use Collective\Html\HtmlFacade as HTML;
9
use Illuminate\Support\Facades\Request;
10
11
class MenuItem
12
{
13
    /**
14
     * The properties array.
15
     *
16
     * @var array
17
     */
18
    public $properties = [];
19
20
    /**
21
     * The childs collection.
22
     *
23
     * @var \Illuminate\Support\Collection
24
     */
25
    protected $childs;
26
27
    /**
28
     * The hide callback.
29
     *
30
     * @var callable
31
     */
32
    protected $hideWhen;
33
34
    /**
35
     * Constructor.
36
     *
37
     * @param array $properties
38
     */
39
    public function __construct($properties = [])
40
    {
41
        $this->fill($properties);
42
        $this->childs = collect();
43
    }
44
45
    /**
46
     * Fill the properties.
47
     *
48
     * @param array $properties
49
     *
50
     * @return static
51
     */
52
    public function fill($properties)
53
    {
54
        $this->properties = array_merge($this->properties, $properties);
55
56
        return $this;
57
    }
58
59
    /**
60
     * Add new child item.
61
     *
62
     * @param array $properties
63
     *
64
     * @return static
65
     */
66
    protected function add(array $properties = [])
67
    {
68
        $properties['attributes']['id'] = $properties['attributes']['id'] ?? md5(json_encode($properties));
69
        $this->childs->push($item = new static($properties));
70
71
        return $item;
72
    }
73
74
    /**
75
     * Create new menu with dropdown.
76
     *
77
     * @param callable $callback
78
     * @param string   $title
79
     * @param int      $order
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be null|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
80
     * @param string   $icon
0 ignored issues
show
Documentation introduced by
Should the type for parameter $icon not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
81
     * @param array    $attributes
82
     *
83
     * @return static
84
     */
85
    public function dropdown(callable $callback, string $title, int $order = null, string $icon = null, array $attributes = [])
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 127 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
86
    {
87
        call_user_func($callback, $item = $this->add(compact('title', 'order', 'icon', 'attributes')));
88
89
        return $item;
90
    }
91
92
    /**
93
     * Register new menu item using registered route.
94
     *
95
     * @param string $route
0 ignored issues
show
Documentation introduced by
Should the type for parameter $route not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
96
     * @param string $title
97
     * @param int    $order
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be null|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
98
     * @param string $icon
0 ignored issues
show
Documentation introduced by
Should the type for parameter $icon not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
99
     * @param array  $attributes
100
     *
101
     * @return static
102
     */
103
    public function route(array $route, string $title, int $order = null, string $icon = null, array $attributes = [])
104
    {
105
        return $this->add(compact('route', 'title', 'order', 'icon', 'attributes'));
106
    }
107
108
    /**
109
     * Register new menu item using url.
110
     *
111
     * @param string $url
112
     * @param string $title
113
     * @param int    $order
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be null|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
114
     * @param string $icon
0 ignored issues
show
Documentation introduced by
Should the type for parameter $icon not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
115
     * @param array  $attributes
116
     *
117
     * @return static
118
     */
119
    public function url(string $url, string $title, int $order = null, string $icon = null, array $attributes = [])
120
    {
121
        return $this->add(compact('url', 'title', 'order', 'icon', 'attributes'));
122
    }
123
124
    /**
125
     * Add new header item.
126
     *
127
     * @param string $title
128
     * @param int    $order
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be null|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
129
     * @param string $icon
0 ignored issues
show
Documentation introduced by
Should the type for parameter $icon not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
130
     * @param array  $attributes
131
     *
132
     * @return static
133
     */
134
    public function header(string $title, int $order = null, string $icon = null, array $attributes = [])
135
    {
136
        $type = 'header';
137
138
        return $this->add(compact('type', 'url', 'title', 'order', 'icon', 'attributes'));
139
    }
140
141
    /**
142
     * Add new divider item.
143
     *
144
     * @param int   $order
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be null|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
145
     * @param array $attributes
146
     *
147
     * @return static
148
     */
149
    public function divider(int $order = null, array $attributes = [])
150
    {
151
        return $this->add(['type' => 'divider', 'order' => $order, 'attributes' => $attributes]);
152
    }
153
154
    /**
155
     * Get childs.
156
     *
157
     * @return \Illuminate\Support\Collection
158
     */
159
    public function getChilds()
160
    {
161
        return $this->childs->sortBy('properties.order');
162
    }
163
164
    /**
165
     * Get url.
166
     *
167
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be \Illuminate\Contracts\Routing\UrlGenerator|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
168
     */
169
    public function getUrl()
170
    {
171
        return $this->route ? route($this->route[0], $this->route[1] ?? []) : ($this->url ? url($this->url) : '');
0 ignored issues
show
Documentation introduced by
The property route does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property url does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
172
    }
173
174
    /**
175
     * Get HTML attribute data.
176
     *
177
     * @return mixed
178
     */
179
    public function getAttributes()
180
    {
181
        return HTML::attributes(array_except($this->attributes ?? [], ['active']));
0 ignored issues
show
Documentation introduced by
The property attributes does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
182
    }
183
184
    /**
185
     * Check if the current item is divider.
186
     *
187
     * @return bool
188
     */
189
    public function isDivider(): bool
190
    {
191
        return $this->type === 'divider';
0 ignored issues
show
Documentation introduced by
The property type does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
192
    }
193
194
    /**
195
     * Check if the current item is header.
196
     *
197
     * @return bool
198
     */
199
    public function isHeader(): bool
200
    {
201
        return $this->type === 'header';
0 ignored issues
show
Documentation introduced by
The property type does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
202
    }
203
204
    /**
205
     * Check is the current item has sub menu .
206
     *
207
     * @return bool
208
     */
209
    public function hasChilds()
210
    {
211
        return $this->childs->isNotEmpty();
212
    }
213
214
    /**
215
     * Check the active state for current menu.
216
     *
217
     * @return bool
218
     */
219
    public function hasActiveOnChild()
220
    {
221
        if ($this->inactive()) {
222
            return false;
223
        }
224
225
        return $this->hasChilds() ? $this->hasActiveStateFromChilds() : false;
226
    }
227
228
    /**
229
     * Check if the item has active state from childs.
230
     *
231
     * @return bool
232
     */
233
    public function hasActiveStateFromChilds(): bool
234
    {
235
        return $this->getChilds()->contains(function (MenuItem $child) {
236
            if ($child->inactive()) {
237
                return false;
238
            }
239
240
            return ($child->hasChilds() && $child->hasActiveStateFromChilds())
241
                   || ($child->route && $child->hasActiveStateFromRoute())
0 ignored issues
show
Documentation introduced by
The property route does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
242
                   || $child->isActive() || $child->hasActiveStateFromUrl();
243
        }) ?? false;
244
    }
245
246
    /**
247
     * Get inactive state.
248
     *
249
     * @return bool
250
     */
251
    public function inactive(): bool
252
    {
253
        if (is_bool($inactive = $this->inactive)) {
0 ignored issues
show
Bug introduced by
The property inactive does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
254
            return $inactive;
255
        }
256
257
        if (is_callable($inactive)) {
258
            return (bool) call_user_func($inactive);
259
        }
260
261
        return false;
262
    }
263
264
    /**
265
     * Get active state for current item.
266
     *
267
     * @return mixed
268
     */
269
    public function isActive()
270
    {
271
        if ($this->inactive()) {
272
            return false;
273
        }
274
275
        if (is_bool($active = $this->active)) {
0 ignored issues
show
Bug introduced by
The property active does not seem to exist. Did you mean inactive?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
276
            return $active;
277
        }
278
279
        if (is_callable($active)) {
280
            return call_user_func($active);
281
        }
282
283
        if ($this->route) {
0 ignored issues
show
Documentation introduced by
The property route does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
284
            return $this->hasActiveStateFromRoute();
285
        }
286
287
        return $this->hasActiveStateFromUrl();
288
    }
289
290
    /**
291
     * Get active status using route.
292
     *
293
     * @return bool
294
     */
295
    protected function hasActiveStateFromRoute(): bool
296
    {
297
        return Route::is($this->route);
0 ignored issues
show
Documentation introduced by
The property route does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
298
    }
299
300
    /**
301
     * Get active status using request url.
302
     *
303
     * @return bool
304
     */
305
    protected function hasActiveStateFromUrl(): bool
306
    {
307
        return Request::is($this->url);
0 ignored issues
show
Documentation introduced by
The property url does not exist on object<Rinvex\Menus\Models\MenuItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
308
    }
309
310
    /**
311
     * Set hide callback for current menu item.
312
     *
313
     * @param callable $callback
314
     *
315
     * @return $this
316
     */
317
    public function hideWhen(callable $callback)
318
    {
319
        $this->hideWhen = $callback;
320
321
        return $this;
322
    }
323
324
    /**
325
     * Set authorization callback for current menu item.
326
     *
327
     * @param string $ability
328
     * @param mixed  $params
329
     *
330
     * @return $this
331
     */
332
    public function can(string $ability, $params = null)
333
    {
334
        $this->hideWhen = function () use ($ability, $params) {
335
            return ! auth()->user()->can($ability, $params);
0 ignored issues
show
Bug introduced by
The method user does only exist in Illuminate\Contracts\Auth\Guard, but not in Illuminate\Contracts\Auth\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
336
        };
337
338
        return $this;
339
    }
340
341
    /**
342
     * Set authentication callback for current menu item.
343
     *
344
     * @return $this
345
     */
346
    public function user()
347
    {
348
        $this->hideWhen = function () {
349
            return ! auth()->user();
0 ignored issues
show
Bug introduced by
The method user does only exist in Illuminate\Contracts\Auth\Guard, but not in Illuminate\Contracts\Auth\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
350
        };
351
352
        return $this;
353
    }
354
355
    /**
356
     * Set authentication callback for current menu item.
357
     *
358
     * @return $this
359
     */
360
    public function guest()
361
    {
362
        $this->hideWhen = function () {
363
            return auth()->user();
0 ignored issues
show
Bug introduced by
The method user does only exist in Illuminate\Contracts\Auth\Guard, but not in Illuminate\Contracts\Auth\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
364
        };
365
366
        return $this;
367
    }
368
369
    /**
370
     * Check if the menu item is hidden.
371
     *
372
     * @return bool
373
     */
374
    public function hidden()
375
    {
376
        return $this->hideWhen ? (bool) call_user_func($this->hideWhen) : false;
377
    }
378
379
    /**
380
     * Get property.
381
     *
382
     * @param string $key
383
     *
384
     * @return mixed
385
     */
386
    public function __get($key)
387
    {
388
        return data_get($this->properties, $key);
389
    }
390
}
391