Completed
Push — master ( 9daef6...ea586e )
by Freek
02:06
created

HasRoles::hasRole()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
c 2
b 1
f 1
dl 0
loc 22
rs 8.6737
cc 6
eloc 11
nc 6
nop 1
1
<?php
2
3
namespace Spatie\Permission\Traits;
4
5
use Spatie\Permission\Contracts\Permission;
6
use Spatie\Permission\Contracts\Role;
7
8
trait HasRoles
9
{
10
    use HasPermissions;
11
    use RefreshesPermissionCache;
12
13
    /**
14
     * A user may have multiple roles.
15
     *
16
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
17
     */
18
    public function roles()
19
    {
20
        return $this->belongsToMany(
0 ignored issues
show
Bug introduced by
It seems like belongsToMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
21
            config('laravel-permission.models.role'),
22
            config('laravel-permission.table_names.user_has_roles')
23
        );
24
    }
25
26
    /**
27
     * A user may have multiple direct permissions.
28
     *
29
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
30
     */
31
    public function permissions()
32
    {
33
        return $this->belongsToMany(
0 ignored issues
show
Bug introduced by
It seems like belongsToMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
34
            config('laravel-permission.models.permission'),
35
            config('laravel-permission.table_names.user_has_permissions')
36
        );
37
    }
38
39
    /**
40
     * Assign the given role to the user.
41
     *
42
     * @param string|Role $role
43
     *
44
     * @return Role
45
     */
46
    public function assignRole($role)
47
    {
48
        $this->roles()->save($this->getStoredRole($role));
0 ignored issues
show
Documentation introduced by
$this->getStoredRole($role) is of type object<Spatie\Permission\Contracts\Role>, but the function expects a object<Illuminate\Database\Eloquent\Model>.

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...
49
    }
50
51
    /**
52
     * Revoke the given role from the user.
53
     *
54
     * @param string|Role $role
55
     *
56
     * @return mixed
57
     */
58
    public function removeRole($role)
59
    {
60
        $this->roles()->detach($this->getStoredRole($role));
0 ignored issues
show
Documentation introduced by
$this->getStoredRole($role) is of type object<Spatie\Permission\Contracts\Role>, but the function expects a integer|array.

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...
61
    }
62
63
    /**
64
     * Determine if the user has (one of) the given role(s).
65
     *
66
     * @param string|array|Role|\Illuminate\Support\Collection $roles
67
     *
68
     * @return bool
69
     */
70
    public function hasRole($roles)
71
    {
72
        if (is_string($roles)) {
73
            return $this->roles->contains('name', $roles);
0 ignored issues
show
Bug introduced by
The property roles 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...
74
        }
75
76
        if ($roles instanceof Role) {
77
            return $this->roles->contains('id', $roles->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Spatie\Permission\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
78
        }
79
80
        if (is_array($roles)) {
81
            foreach ($roles as $role) {
82
                if ($this->hasRole($role)) {
83
                    return true;
84
                }
85
            }
86
87
            return false;
88
        }
89
90
        return (bool) $roles->intersect($this->roles)->count();
91
    }
92
93
    /**
94
     * Determine if the user has any of the given role(s).
95
     *
96
     * @param string|array|Role|\Illuminate\Support\Collection $roles
97
     *
98
     * @return bool
99
     */
100
    public function hasAnyRole($roles)
101
    {
102
        return $this->hasRole($roles);
103
    }
104
105
    /**
106
     * Determine if the user has all of the given role(s).
107
     *
108
     * @param string|Role|\Illuminate\Support\Collection $roles
109
     *
110
     * @return bool
111
     */
112
    public function hasAllRoles($roles)
113
    {
114
        if (is_string($roles)) {
115
            return $this->roles->contains('name', $roles);
116
        }
117
118
        if ($roles instanceof Role) {
119
            return $this->roles->contains('id', $roles->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Spatie\Permission\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
120
        }
121
122
        $roles = collect()->make($roles)->map(function ($role) {
123
            return $role instanceof Role ? $role->name : $role;
0 ignored issues
show
Bug introduced by
Accessing name on the interface Spatie\Permission\Contracts\Role suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
124
        });
125
126
        return $roles->intersect($this->roles->lists('name')) == $roles;
127
    }
128
129
    /**
130
     * Determine if the user may perform the given permission.
131
     *
132
     * @param string|Permission $permission
133
     *
134
     * @return bool
135
     */
136
    public function hasPermissionTo($permission)
137
    {
138
        if (is_string($permission)) {
139
            $permission = app(Permission::class)->findByName($permission);
140
        }
141
142
        return $this->hasDirectPermission($permission) || $this->hasPermissionViaRole($permission);
143
    }
144
145
    /**
146
     * @deprecated deprecated since version 1.0.1, use hasPermissionTo instead
147
     *
148
     * Determine if the user may perform the given permission.
149
     *
150
     * @param Permission $permission
151
     *
152
     * @return bool
153
     */
154
    public function hasPermission($permission)
155
    {
156
        return $this->hasPermissionTo($permission);
157
    }
158
159
    /**
160
     * Determine if the user has, via roles, has the given permission.
161
     *
162
     * @param Permission $permission
163
     *
164
     * @return bool
165
     */
166
    protected function hasPermissionViaRole(Permission $permission)
167
    {
168
        return $this->hasRole($permission->roles);
0 ignored issues
show
Bug introduced by
Accessing roles on the interface Spatie\Permission\Contracts\Permission suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
169
    }
170
171
    /**
172
     * Determine if the user has has the given permission.
173
     *
174
     * @param string|Permission $permission
175
     *
176
     * @return bool
177
     */
178
    protected function hasDirectPermission($permission)
179
    {
180
        if (is_string($permission)) {
181
            $permission = app(Permission::class)->findByName($permission);
182
183
            if (!$permission) {
184
                return false;
185
            }
186
        }
187
188
        return $this->permissions->contains('id', $permission->id);
0 ignored issues
show
Bug introduced by
The property permissions 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...
189
    }
190
191
    /**
192
     * @param $role
193
     *
194
     * @return Role
195
     */
196
    protected function getStoredRole($role)
197
    {
198
        if (is_string($role)) {
199
            return app(Role::class)->findByName($role);
200
        }
201
202
        return $role;
203
    }
204
}
205