Completed
Push — fix-travis ( 5cbed5...15fb6b )
by Fèvre
02:46
created

HasRoleAndPermission::isPretendEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Xetaravel\Models;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Eloquent\Model;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Xetaravel\Models\Model.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
9
use Illuminate\Support\Str;
10
use InvalidArgumentException;
11
use Ultraware\Roles\Models\Permission;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Xetaravel\Models\Permission.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
12
use Ultraware\Roles\Models\Role;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Xetaravel\Models\Role.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
13
14
trait HasRoleAndPermission
15
{
16
    /**
17
     * Property for caching roles.
18
     *
19
     * @var Collection|null
20
     */
21
    protected $roles;
22
23
    /**
24
     * Property for caching permissions.
25
     *
26
     * @var Collection|null
27
     */
28
    protected $permissions;
29
30
    /**
31
     * User belongs to many roles.
32
     *
33
     * @return BelongsToMany
34
     */
35
    public function roles()
36
    {
37
        return $this->belongsToMany(config('roles.models.role'))->withTimestamps();
0 ignored issues
show
Documentation Bug introduced by
The method belongsToMany does not exist on object<Xetaravel\Models\HasRoleAndPermission>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
38
    }
39
40
    /**
41
     * Get all roles as collection.
42
     *
43
     * @return Collection
44
     */
45
    public function getRoles()
46
    {
47
        return (!$this->roles) ? $this->roles = $this->roles()->get() : $this->roles;
48
    }
49
50
    /**
51
     * Check if the user has a role or roles.
52
     *
53
     * @param int|string|array $role
54
     * @param bool $all
55
     * @return bool
56
     */
57
    public function hasRole($role, $all = false)
58
    {
59
        if ($this->isPretendEnabled()) {
60
            return $this->pretend('hasRole');
61
        }
62
63
        if (!$all) {
64
            return $this->hasOneRole($role);
65
        }
66
67
        return $this->hasAllRoles($role);
68
    }
69
70
    /**
71
     * Check if the user has at least one of the given roles.
72
     *
73
     * @param int|string|array $role
74
     * @return bool
75
     */
76
    public function hasOneRole($role)
77
    {
78
        foreach ($this->getArrayFrom($role) as $role) {
79
            if ($this->checkRole($role)) {
80
                return true;
81
            }
82
        }
83
84
        return false;
85
    }
86
87
    /**
88
     * Check if the user has all roles.
89
     *
90
     * @param int|string|array $role
91
     * @return bool
92
     */
93
    public function hasAllRoles($role)
94
    {
95
        foreach ($this->getArrayFrom($role) as $role) {
96
            if (!$this->checkRole($role)) {
97
                return false;
98
            }
99
        }
100
101
        return true;
102
    }
103
104
    /**
105
     * Check if the user has role.
106
     *
107
     * @param int|string $role
108
     * @return bool
109
     */
110
    public function checkRole($role)
111
    {
112
        return $this->getRoles()->contains(function ($value) use ($role) {
113
            return $role == $value->id || Str::is($role, $value->slug);
114
        });
115
    }
116
117
    /**
118
     * Attach role to a user.
119
     *
120
     * @param int|Role $role
121
     * @return null|bool
122
     */
123
    public function attachRole($role)
124
    {
125
        return (!$this->getRoles()->contains($role)) ? $this->roles()->attach($role) : true;
126
    }
127
128
    /**
129
     * Detach role from a user.
130
     *
131
     * @param int|Role $role
132
     * @return int
133
     */
134
    public function detachRole($role)
135
    {
136
        $this->roles = null;
137
138
        return $this->roles()->detach($role);
139
    }
140
141
    /**
142
     * Detach all roles from a user.
143
     *
144
     * @return int
145
     */
146
    public function detachAllRoles()
147
    {
148
        $this->roles = null;
149
150
        return $this->roles()->detach();
151
    }
152
153
    /**
154
     * Sync roles for a user.
155
     *
156
     * @param array|\Ultraware\Roles\Models\Role[]|\Illuminate\Database\Eloquent\Collection $roles
157
     * @return array
158
     */
159
    public function syncRoles($roles)
160
    {
161
        $this->roles = null;
162
163
        return $this->roles()->sync($roles);
164
    }
165
166
    /**
167
     * Get role level of a user.
168
     *
169
     * @return int
170
     */
171
    public function level()
172
    {
173
        return ($role = $this->getRoles()->sortByDesc('level')->first()) ? $role->level : 0;
174
    }
175
176
    /**
177
     * Get all permissions from roles.
178
     *
179
     * @return Builder
180
     */
181
    public function rolePermissions()
182
    {
183
        $permissionModel = app(config('roles.models.permission'));
184
185
        if (!$permissionModel instanceof Model) {
186
            throw new InvalidArgumentException(
187
                '[roles.models.permission] must be an instance of \Illuminate\Database\Eloquent\Model'
188
            );
189
        }
190
191
        return $permissionModel
192
            ::select([
193
                'permissions.*',
194
                'permission_role.created_at as pivot_created_at',
195
                'permission_role.updated_at as pivot_updated_at'
196
            ])
197
            ->join('permission_role', 'permission_role.permission_id', '=', 'permissions.id')
198
            ->join('roles', 'roles.id', '=', 'permission_role.role_id')
199
            ->whereIn('roles.id', $this->getRoles()->pluck('id')->toArray())
200
            ->orWhere('roles.level', '<', $this->level())
201
            ->groupBy([
202
                'permissions.id',
203
                'permissions.name',
204
                'permissions.slug',
205
                'permissions.description',
206
                'permissions.model',
207
                'permissions.created_at',
208
                'permissions.updated_at',
209
                'permissions.is_deletable',
210
                'pivot_created_at',
211
                'pivot_updated_at'
212
            ]);
213
    }
214
215
    /**
216
     * User belongs to many permissions.
217
     *
218
     * @return BelongsToMany
219
     */
220
    public function userPermissions()
221
    {
222
        return $this->belongsToMany(config('roles.models.permission'))->withTimestamps();
0 ignored issues
show
Documentation Bug introduced by
The method belongsToMany does not exist on object<Xetaravel\Models\HasRoleAndPermission>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
223
    }
224
225
    /**
226
     * Get all permissions as collection.
227
     *
228
     * @return Collection
229
     */
230
    public function getPermissions()
231
    {
232
        if (!$this->permissions) {
233
            $this->permissions = $this->rolePermissions()->get()->merge($this->userPermissions()->get());
234
235
            return $this->permissions;
236
        }
237
238
        return $this->permissions;
239
    }
240
241
    /**
242
     * Check if the user has a permission or permissions.
243
     *
244
     * @param int|string|array $permission
245
     * @param bool $all
246
     * @return bool
247
     */
248
    public function hasPermission($permission, $all = false)
249
    {
250
        if ($this->isPretendEnabled()) {
251
            return $this->pretend('hasPermission');
252
        }
253
254
        if (!$all) {
255
            return $this->hasOnePermission($permission);
256
        }
257
258
        return $this->hasAllPermissions($permission);
259
    }
260
261
    /**
262
     * Check if the user has at least one of the given permissions.
263
     *
264
     * @param int|string|array $permission
265
     * @return bool
266
     */
267
    public function hasOnePermission($permission)
268
    {
269
        foreach ($this->getArrayFrom($permission) as $permission) {
270
            if ($this->checkPermission($permission)) {
271
                return true;
272
            }
273
        }
274
275
        return false;
276
    }
277
278
    /**
279
     * Check if the user has all permissions.
280
     *
281
     * @param int|string|array $permission
282
     * @return bool
283
     */
284
    public function hasAllPermissions($permission)
285
    {
286
        foreach ($this->getArrayFrom($permission) as $permission) {
287
            if (!$this->checkPermission($permission)) {
288
                return false;
289
            }
290
        }
291
292
        return true;
293
    }
294
295
    /**
296
     * Check if the user has a permission.
297
     *
298
     * @param int|string $permission
299
     * @return bool
300
     */
301
    public function checkPermission($permission)
302
    {
303
        return $this->getPermissions()->contains(function ($value) use ($permission) {
304
            return $permission == $value->id || Str::is($permission, $value->slug);
305
        });
306
    }
307
308
    /**
309
     * Check if the user is allowed to manipulate with entity.
310
     *
311
     * @param string $providedPermission
312
     * @param Model $entity
313
     * @param bool $owner
314
     * @param string $ownerColumn
315
     * @return bool
316
     */
317
    public function allowed($providedPermission, Model $entity, $owner = true, $ownerColumn = 'user_id')
318
    {
319
        if ($this->isPretendEnabled()) {
320
            return $this->pretend('allowed');
321
        }
322
323
        if ($owner === true && $entity->{$ownerColumn} == $this->id) {
0 ignored issues
show
Bug introduced by
The property id 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...
324
            return true;
325
        }
326
327
        return $this->isAllowed($providedPermission, $entity);
328
    }
329
330
    /**
331
     * Check if the user is allowed to manipulate with provided entity.
332
     *
333
     * @param string $providedPermission
334
     * @param Model $entity
335
     * @return bool
336
     */
337
    protected function isAllowed($providedPermission, Model $entity)
338
    {
339
        foreach ($this->getPermissions() as $permission) {
340
            if ($permission->model != '' && get_class($entity) == $permission->model
341
                && ($permission->id == $providedPermission || $permission->slug === $providedPermission)
342
            ) {
343
                return true;
344
            }
345
        }
346
347
        return false;
348
    }
349
350
    /**
351
     * Attach permission to a user.
352
     *
353
     * @param int|Permission $permission
354
     * @return null|bool
355
     */
356
    public function attachPermission($permission)
357
    {
358
        return (!$this->getPermissions()->contains($permission)) ? $this->userPermissions()->attach($permission) : true;
359
    }
360
361
    /**
362
     * Detach permission from a user.
363
     *
364
     * @param int|Permission $permission
365
     * @return int
366
     */
367
    public function detachPermission($permission)
368
    {
369
        $this->permissions = null;
370
371
        return $this->userPermissions()->detach($permission);
372
    }
373
374
    /**
375
     * Detach all permissions from a user.
376
     *
377
     * @return int
378
     */
379
    public function detachAllPermissions()
380
    {
381
        $this->permissions = null;
382
383
        return $this->userPermissions()->detach();
384
    }
385
386
    /**
387
     * Sync permissions for a user.
388
     *
389
     * @param array|\Ultraware\Roles\Models\Permission[]|\Illuminate\Database\Eloquent\Collection $permissions
390
     * @return array
391
     */
392
    public function syncPermissions($permissions)
393
    {
394
        $this->permissions = null;
395
396
        return $this->userPermissions()->sync($permissions);
397
    }
398
399
    /**
400
     * Check if pretend option is enabled.
401
     *
402
     * @return bool
403
     */
404
    private function isPretendEnabled()
405
    {
406
        return (bool) config('roles.pretend.enabled');
407
    }
408
409
    /**
410
     * Allows to pretend or simulate package behavior.
411
     *
412
     * @param string $option
413
     * @return bool
414
     */
415
    private function pretend($option)
416
    {
417
        return (bool) config('roles.pretend.options.' . $option);
418
    }
419
420
    /**
421
     * Get an array from argument.
422
     *
423
     * @param int|string|array $argument
424
     * @return array
425
     */
426
    private function getArrayFrom($argument)
427
    {
428
        return (!is_array($argument)) ? preg_split('/ ?[,|] ?/', $argument) : $argument;
429
    }
430
431
    public function callMagic($method, $parameters)
432
    {
433
        if (starts_with($method, 'is')) {
434
            return $this->hasRole(snake_case(substr($method, 2), config('roles.separator')));
435
        } elseif (starts_with($method, 'can')) {
436
            return $this->hasPermission(snake_case(substr($method, 3), config('roles.separator')));
437
        } elseif (starts_with($method, 'allowed')) {
438
            return $this->allowed(
439
                snake_case(substr($method, 7), config('roles.separator')),
440
                $parameters[0],
441
                (isset($parameters[1])) ? $parameters[1] : true,
442
                (isset($parameters[2])) ? $parameters[2] : 'user_id'
443
            );
444
        }
445
446
        return parent::__call($method, $parameters);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (__call() instead of callMagic()). Are you sure this is correct? If so, you might want to change this to $this->__call().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
447
    }
448
449
    public function __call($method, $parameters)
450
    {
451
        return $this->callMagic($method, $parameters);
452
    }
453
}
454