Completed
Push — rbac-get-user-ids-by-role ( 0e0079 )
by Alexander
07:35
created

PhpManager::getChildren()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2
Metric Value
dl 0
loc 4
rs 10
ccs 2
cts 2
cp 1
cc 2
eloc 2
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\rbac;
9
10
use yii\base\InvalidCallException;
11
use yii\base\InvalidParamException;
12
use Yii;
13
use yii\helpers\VarDumper;
14
15
/**
16
 * PhpManager represents an authorization manager that stores authorization
17
 * information in terms of a PHP script file.
18
 *
19
 * The authorization data will be saved to and loaded from three files
20
 * specified by [[itemFile]], [[assignmentFile]] and [[ruleFile]].
21
 *
22
 * PhpManager is mainly suitable for authorization data that is not too big
23
 * (for example, the authorization data for a personal blog system).
24
 * Use [[DbManager]] for more complex authorization data.
25
 *
26
 * Note that PhpManager is not compatible with facebooks [HHVM](http://hhvm.com/) because
27
 * it relies on writing php files and including them afterwards which is not supported by HHVM.
28
 *
29
 * @author Qiang Xue <[email protected]>
30
 * @author Alexander Kochetov <[email protected]>
31
 * @author Christophe Boulain <[email protected]>
32
 * @author Alexander Makarov <[email protected]>
33
 * @since 2.0
34
 */
35
class PhpManager extends BaseManager
36
{
37
    /**
38
     * @var string the path of the PHP script that contains the authorization items.
39
     * This can be either a file path or a path alias to the file.
40
     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
41
     * @see loadFromFile()
42
     * @see saveToFile()
43
     */
44
    public $itemFile = '@app/rbac/items.php';
45
    /**
46
     * @var string the path of the PHP script that contains the authorization assignments.
47
     * This can be either a file path or a path alias to the file.
48
     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
49
     * @see loadFromFile()
50
     * @see saveToFile()
51
     */
52
    public $assignmentFile = '@app/rbac/assignments.php';
53
    /**
54
     * @var string the path of the PHP script that contains the authorization rules.
55
     * This can be either a file path or a path alias to the file.
56
     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
57
     * @see loadFromFile()
58
     * @see saveToFile()
59
     */
60
    public $ruleFile = '@app/rbac/rules.php';
61
62
    /**
63
     * @var Item[]
64
     */
65
    protected $items = []; // itemName => item
66
    /**
67
     * @var array
68
     */
69
    protected $children = []; // itemName, childName => child
70
    /**
71
     * @var array
72
     */
73
    protected $assignments = []; // userId, itemName => assignment
74
    /**
75
     * @var Rule[]
76
     */
77
    protected $rules = []; // ruleName => rule
78
79
80
    /**
81
     * Initializes the application component.
82
     * This method overrides parent implementation by loading the authorization data
83
     * from PHP script.
84
     */
85 21
    public function init()
86
    {
87 21
        parent::init();
88 21
        $this->itemFile = Yii::getAlias($this->itemFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Yii::getAlias($this->itemFile) can also be of type boolean. However, the property $itemFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
89 21
        $this->assignmentFile = Yii::getAlias($this->assignmentFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Yii::getAlias($this->assignmentFile) can also be of type boolean. However, the property $assignmentFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
90 21
        $this->ruleFile = Yii::getAlias($this->ruleFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Yii::getAlias($this->ruleFile) can also be of type boolean. However, the property $ruleFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
91 21
        $this->load();
92 21
    }
93
94
    /**
95
     * @inheritdoc
96
     */
97 1
    public function checkAccess($userId, $permissionName, $params = [])
98
    {
99 1
        $assignments = $this->getAssignments($userId);
100 1
        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);
101
    }
102
103
    /**
104
     * @inheritdoc
105
     */
106 5
    public function getAssignments($userId)
107
    {
108 5
        return isset($this->assignments[$userId]) ? $this->assignments[$userId] : [];
109
    }
110
111
    /**
112
     * Performs access check for the specified user.
113
     * This method is internally called by [[checkAccess()]].
114
     *
115
     * @param string|integer $user the user ID. This should can be either an integer or a string representing
116
     * the unique identifier of a user. See [[\yii\web\User::id]].
117
     * @param string $itemName the name of the operation that need access check
118
     * @param array $params name-value pairs that would be passed to rules associated
119
     * with the tasks and roles assigned to the user. A param with name 'user' is added to this array,
120
     * which holds the value of `$userId`.
121
     * @param Assignment[] $assignments the assignments to the specified user
122
     * @return boolean whether the operations can be performed by the user.
123
     */
124 1
    protected function checkAccessRecursive($user, $itemName, $params, $assignments)
125
    {
126 1
        if (!isset($this->items[$itemName])) {
127
            return false;
128
        }
129
130
        /* @var $item Item */
131 1
        $item = $this->items[$itemName];
132 1
        Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission : $itemName", __METHOD__);
133
134 1
        if (!$this->executeRule($user, $item, $params)) {
135 1
            return false;
136
        }
137
138 1
        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {
139 1
            return true;
140
        }
141
142 1
        foreach ($this->children as $parentName => $children) {
143 1
            if (isset($children[$itemName]) && $this->checkAccessRecursive($user, $parentName, $params, $assignments)) {
144 1
                return true;
145
            }
146 1
        }
147
148 1
        return false;
149
    }
150
151
    /**
152
     * @inheritdoc
153
     */
154 17
    public function addChild($parent, $child)
155
    {
156 17
        if (!isset($this->items[$parent->name], $this->items[$child->name])) {
157
            throw new InvalidParamException("Either '{$parent->name}' or '{$child->name}' does not exist.");
158
        }
159
160 17
        if ($parent->name === $child->name) {
161
            throw new InvalidParamException("Cannot add '{$parent->name} ' as a child of itself.");
162
        }
163 17
        if ($parent instanceof Permission && $child instanceof Role) {
164
            throw new InvalidParamException('Cannot add a role as a child of a permission.');
165
        }
166
167 17
        if ($this->detectLoop($parent, $child)) {
168
            throw new InvalidCallException("Cannot add '{$child->name}' as a child of '{$parent->name}'. A loop has been detected.");
169
        }
170 17
        if (isset($this->children[$parent->name][$child->name])) {
171
            throw new InvalidCallException("The item '{$parent->name}' already has a child '{$child->name}'.");
172
        }
173 17
        $this->children[$parent->name][$child->name] = $this->items[$child->name];
174 17
        $this->saveItems();
175
176 17
        return true;
177
    }
178
179
    /**
180
     * Checks whether there is a loop in the authorization item hierarchy.
181
     *
182
     * @param Item $parent parent item
183
     * @param Item $child the child item that is to be added to the hierarchy
184
     * @return boolean whether a loop exists
185
     */
186 17
    protected function detectLoop($parent, $child)
187
    {
188 17
        if ($child->name === $parent->name) {
189
            return true;
190
        }
191 17
        if (!isset($this->children[$child->name], $this->items[$parent->name])) {
192 17
            return false;
193
        }
194 16
        foreach ($this->children[$child->name] as $grandchild) {
195
            /* @var $grandchild Item */
196 16
            if ($this->detectLoop($parent, $grandchild)) {
197
                return true;
198
            }
199 16
        }
200
201 16
        return false;
202
    }
203
204
    /**
205
     * @inheritdoc
206
     */
207
    public function removeChild($parent, $child)
208
    {
209
        if (isset($this->children[$parent->name][$child->name])) {
210
            unset($this->children[$parent->name][$child->name]);
211
            $this->saveItems();
212
            return true;
213
        } else {
214
            return false;
215
        }
216
    }
217
218
    /**
219
     * @inheritdoc
220
     */
221
    public function removeChildren($parent)
222
    {
223
        if (isset($this->children[$parent->name])) {
224
            unset($this->children[$parent->name]);
225
            $this->saveItems();
226
            return true;
227
        } else {
228
            return false;
229
        }
230
    }
231
232
    /**
233
     * @inheritdoc
234
     */
235
    public function hasChild($parent, $child)
236
    {
237
        return isset($this->children[$parent->name][$child->name]);
238
    }
239
240
    /**
241
     * @inheritdoc
242
     */
243 17
    public function assign($role, $userId)
244
    {
245 17
        if (!isset($this->items[$role->name])) {
246
            throw new InvalidParamException("Unknown role '{$role->name}'.");
247 17
        } elseif (isset($this->assignments[$userId][$role->name])) {
248
            throw new InvalidParamException("Authorization item '{$role->name}' has already been assigned to user '$userId'.");
249
        } else {
250 17
            $this->assignments[$userId][$role->name] = new Assignment([
251 17
                'userId' => $userId,
252 17
                'roleName' => $role->name,
253 17
                'createdAt' => time(),
254 17
            ]);
255 17
            $this->saveAssignments();
256 17
            return $this->assignments[$userId][$role->name];
257
        }
258
    }
259
260
    /**
261
     * @inheritdoc
262
     */
263
    public function revoke($role, $userId)
264
    {
265
        if (isset($this->assignments[$userId][$role->name])) {
266
            unset($this->assignments[$userId][$role->name]);
267
            $this->saveAssignments();
268
            return true;
269
        } else {
270
            return false;
271
        }
272
    }
273
274
    /**
275
     * @inheritdoc
276
     */
277
    public function revokeAll($userId)
278
    {
279
        if (isset($this->assignments[$userId]) && is_array($this->assignments[$userId])) {
280
            foreach ($this->assignments[$userId] as $itemName => $value) {
281
                unset($this->assignments[$userId][$itemName]);
282
            }
283
            $this->saveAssignments();
284
            return true;
285
        } else {
286
            return false;
287
        }
288
    }
289
290
    /**
291
     * @inheritdoc
292
     */
293
    public function getAssignment($roleName, $userId)
294
    {
295
        return isset($this->assignments[$userId][$roleName]) ? $this->assignments[$userId][$roleName] : null;
296
    }
297
298
    /**
299
     * @inheritdoc
300
     */
301
    public function getItems($type)
302
    {
303
        $items = [];
304
305
        foreach ($this->items as $name => $item) {
306
            /* @var $item Item */
307
            if ($item->type == $type) {
308
                $items[$name] = $item;
309
            }
310
        }
311
312
        return $items;
313
    }
314
315
316
    /**
317
     * @inheritdoc
318
     */
319 1
    public function removeItem($item)
320
    {
321 1
        if (isset($this->items[$item->name])) {
322 1
            foreach ($this->children as &$children) {
323
                unset($children[$item->name]);
324 1
            }
325 1
            foreach ($this->assignments as &$assignments) {
326 1
                unset($assignments[$item->name]);
327 1
            }
328 1
            unset($this->items[$item->name]);
329 1
            $this->saveItems();
330 1
            $this->saveAssignments();
331 1
            return true;
332
        } else {
333
            return false;
334
        }
335
    }
336
337
    /**
338
     * @inheritdoc
339
     */
340 6
    public function getItem($name)
341
    {
342 6
        return isset($this->items[$name]) ? $this->items[$name] : null;
343
    }
344
345
    /**
346
     * @inheritdoc
347
     */
348 1
    public function updateRule($name, $rule)
349
    {
350 1
        if ($rule->name !== $name) {
351 1
            unset($this->rules[$name]);
352 1
        }
353 1
        $this->rules[$rule->name] = $rule;
354 1
        $this->saveRules();
355 1
        return true;
356
    }
357
358
    /**
359
     * @inheritdoc
360
     */
361 5
    public function getRule($name)
362
    {
363 5
        return isset($this->rules[$name]) ? $this->rules[$name] : null;
364
    }
365
366
    /**
367
     * @inheritdoc
368
     */
369 2
    public function getRules()
370
    {
371 2
        return $this->rules;
372
    }
373
374
    /**
375
     * @inheritdoc
376
     */
377 2
    public function getRolesByUser($userId)
378
    {
379 2
        $roles = [];
380 2
        foreach ($this->getAssignments($userId) as $name => $assignment) {
381 2
            $role = $this->items[$assignment->roleName];
382 2
            if ($role->type === Item::TYPE_ROLE) {
383 2
                $roles[$name] = $role;
384 2
            }
385 2
        }
386
387 2
        return $roles;
388
    }
389
390
    /**
391
     * @inheritdoc
392
     */
393 1
    public function getPermissionsByRole($roleName)
394
    {
395 1
        $result = [];
396 1
        $this->getChildrenRecursive($roleName, $result);
397 1
        if (empty($result)) {
398
            return [];
399
        }
400 1
        $permissions = [];
401 1
        foreach (array_keys($result) as $itemName) {
402 1
            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
403 1
                $permissions[$itemName] = $this->items[$itemName];
404 1
            }
405 1
        }
406 1
        return $permissions;
407
    }
408
409
    /**
410
     * Recursively finds all children and grand children of the specified item.
411
     *
412
     * @param string $name the name of the item whose children are to be looked for.
413
     * @param array $result the children and grand children (in array keys)
414
     */
415 2
    protected function getChildrenRecursive($name, &$result)
416
    {
417 2
        if (isset($this->children[$name])) {
418 2
            foreach ($this->children[$name] as $child) {
419 2
                $result[$child->name] = true;
420 2
                $this->getChildrenRecursive($child->name, $result);
421 2
            }
422 2
        }
423 2
    }
424
425
    /**
426
     * @inheritdoc
427
     */
428 1
    public function getPermissionsByUser($userId)
429
    {
430 1
        $directPermission = $this->getDirectPermissionsByUser($userId);
431 1
        $inheritedPermission = $this->getInheritedPermissionsByUser($userId);
432
433 1
        return array_merge($directPermission, $inheritedPermission);
434
    }
435
436
    /**
437
     * Returns all permissions that are directly assigned to user.
438
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
439
     * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.
440
     *
441
     * @since 2.0.7
442
     */
443 1
    protected function getDirectPermissionsByUser($userId)
444
    {
445 1
        $permissions = [];
446 1
        foreach ($this->getAssignments($userId) as $name => $assignment) {
447 1
            $permission = $this->items[$assignment->roleName];
448 1
            if ($permission->type === Item::TYPE_PERMISSION) {
449 1
                $permissions[$name] = $permission;
450 1
            }
451 1
        }
452
453 1
        return $permissions;
454
    }
455
456
    /**
457
     * Returns all permissions that the user inherits from the roles assigned to him.
458
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
459
     * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.
460
     *
461
     * @since 2.0.7
462
     */
463 1
    protected function getInheritedPermissionsByUser($userId)
464
    {
465 1
        $assignments = $this->getAssignments($userId);
466 1
        $result = [];
467 1
        foreach (array_keys($assignments) as $roleName) {
468 1
            $this->getChildrenRecursive($roleName, $result);
469 1
        }
470
471 1
        if (empty($result)) {
472
            return [];
473
        }
474
475 1
        $permissions = [];
476 1
        foreach (array_keys($result) as $itemName) {
477 1
            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
478 1
                $permissions[$itemName] = $this->items[$itemName];
479 1
            }
480 1
        }
481 1
        return $permissions;
482
    }
483
484
    /**
485
     * @inheritdoc
486
     */
487 1
    public function getChildren($name)
488
    {
489 1
        return isset($this->children[$name]) ? $this->children[$name] : [];
490
    }
491
492
    /**
493
     * @inheritdoc
494
     */
495 1
    public function removeAll()
496
    {
497 1
        $this->children = [];
498 1
        $this->items = [];
499 1
        $this->assignments = [];
500 1
        $this->rules = [];
501 1
        $this->save();
502 1
    }
503
504
    /**
505
     * @inheritdoc
506
     */
507
    public function removeAllPermissions()
508
    {
509
        $this->removeAllItems(Item::TYPE_PERMISSION);
510
    }
511
512
    /**
513
     * @inheritdoc
514
     */
515
    public function removeAllRoles()
516
    {
517
        $this->removeAllItems(Item::TYPE_ROLE);
518
    }
519
520
    /**
521
     * Removes all auth items of the specified type.
522
     * @param integer $type the auth item type (either Item::TYPE_PERMISSION or Item::TYPE_ROLE)
523
     */
524
    protected function removeAllItems($type)
525
    {
526
        $names = [];
527
        foreach ($this->items as $name => $item) {
528
            if ($item->type == $type) {
529
                unset($this->items[$name]);
530
                $names[$name] = true;
531
            }
532
        }
533
        if (empty($names)) {
534
            return;
535
        }
536
537
        foreach ($this->assignments as $i => $assignment) {
538
            if (isset($names[$assignment->roleName])) {
539
                unset($this->assignments[$i]);
540
            }
541
        }
542
        foreach ($this->children as $name => $children) {
543
            if (isset($names[$name])) {
544
                unset($this->children[$name]);
545
            } else {
546
                foreach ($children as $childName => $item) {
547
                    if (isset($names[$childName])) {
548
                        unset($children[$childName]);
549
                    }
550
                }
551
                $this->children[$name] = $children;
552
            }
553
        }
554
555
        $this->saveItems();
556
    }
557
558
    /**
559
     * @inheritdoc
560
     */
561
    public function removeAllRules()
562
    {
563
        foreach ($this->items as $item) {
564
            $item->ruleName = null;
565
        }
566
        $this->rules = [];
567
        $this->saveRules();
568
    }
569
570
    /**
571
     * @inheritdoc
572
     */
573
    public function removeAllAssignments()
574
    {
575
        $this->assignments = [];
576
        $this->saveAssignments();
577
    }
578
579
    /**
580
     * @inheritdoc
581
     */
582 1
    protected function removeRule($rule)
583
    {
584 1
        if (isset($this->rules[$rule->name])) {
585 1
            unset($this->rules[$rule->name]);
586 1
            foreach ($this->items as $item) {
587 1
                if ($item->ruleName === $rule->name) {
588 1
                    $item->ruleName = null;
589 1
                }
590 1
            }
591 1
            $this->saveRules();
592 1
            return true;
593
        } else {
594
            return false;
595
        }
596
    }
597
598
    /**
599
     * @inheritdoc
600
     */
601 17
    protected function addRule($rule)
602
    {
603 17
        $this->rules[$rule->name] = $rule;
604 17
        $this->saveRules();
605 17
        return true;
606
    }
607
608
    /**
609
     * @inheritdoc
610
     */
611 4
    protected function updateItem($name, $item)
612
    {
613 4
        if ($name !== $item->name) {
614 3
            if (isset($this->items[$item->name])) {
615 1
                throw new InvalidParamException("Unable to change the item name. The name '{$item->name}' is already used by another item.");
616
            } else {
617
                // Remove old item in case of renaming
618 2
                unset($this->items[$name]);
619
620 2
                if (isset($this->children[$name])) {
621
                    $this->children[$item->name] = $this->children[$name];
622
                    unset($this->children[$name]);
623
                }
624 2
                foreach ($this->children as &$children) {
625 1
                    if (isset($children[$name])) {
626 1
                        $children[$item->name] = $children[$name];
627 1
                        unset($children[$name]);
628 1
                    }
629 2
                }
630 2
                foreach ($this->assignments as &$assignments) {
631 2
                    if (isset($assignments[$name])) {
632 1
                        $assignments[$item->name] = $assignments[$name];
633 1
                        $assignments[$item->name]->roleName = $item->name;
634 1
                        unset($assignments[$name]);
635 1
                    }
636 2
                }
637 2
                $this->saveAssignments();
638
            }
639 2
        }
640
641 3
        $this->items[$item->name] = $item;
642
643 3
        $this->saveItems();
644 3
        return true;
645
    }
646
647
    /**
648
     * @inheritdoc
649
     */
650 19
    protected function addItem($item)
651
    {
652 19
        $time = time();
653 19
        if ($item->createdAt === null) {
654 19
            $item->createdAt = $time;
655 19
        }
656 19
        if ($item->updatedAt === null) {
657 19
            $item->updatedAt = $time;
658 19
        }
659
660 19
        $this->items[$item->name] = $item;
661
662 19
        $this->saveItems();
663
664 19
        return true;
665
666
    }
667
668
    /**
669
     * Loads authorization data from persistent storage.
670
     */
671 21
    protected function load()
672
    {
673 21
        $this->children = [];
674 21
        $this->rules = [];
675 21
        $this->assignments = [];
676 21
        $this->items = [];
677
678 21
        $items = $this->loadFromFile($this->itemFile);
679 21
        $itemsMtime = @filemtime($this->itemFile);
680 21
        $assignments = $this->loadFromFile($this->assignmentFile);
681 21
        $assignmentsMtime = @filemtime($this->assignmentFile);
682 21
        $rules = $this->loadFromFile($this->ruleFile);
683
684 21
        foreach ($items as $name => $item) {
685 4
            $class = $item['type'] == Item::TYPE_PERMISSION ? Permission::className() : Role::className();
686
687 4
            $this->items[$name] = new $class([
688 4
                'name' => $name,
689 4
                'description' => isset($item['description']) ? $item['description'] : null,
690 4
                'ruleName' => isset($item['ruleName']) ? $item['ruleName'] : null,
691 4
                'data' => isset($item['data']) ? $item['data'] : null,
692 4
                'createdAt' => $itemsMtime,
693 4
                'updatedAt' => $itemsMtime,
694 4
            ]);
695 21
        }
696
697 21
        foreach ($items as $name => $item) {
698 4
            if (isset($item['children'])) {
699 4
                foreach ($item['children'] as $childName) {
700 4
                    if (isset($this->items[$childName])) {
701 4
                        $this->children[$name][$childName] = $this->items[$childName];
702 4
                    }
703 4
                }
704 4
            }
705 21
        }
706
707 21
        foreach ($assignments as $userId => $roles) {
708 4
            foreach ($roles as $role) {
709 4
                $this->assignments[$userId][$role] = new Assignment([
710 4
                    'userId' => $userId,
711 4
                    'roleName' => $role,
712 4
                    'createdAt' => $assignmentsMtime,
713 4
                ]);
714 4
            }
715 21
        }
716
717 21
        foreach ($rules as $name => $ruleData) {
718 4
            $this->rules[$name] = unserialize($ruleData);
719 21
        }
720 21
    }
721
722
    /**
723
     * Saves authorization data into persistent storage.
724
     */
725 2
    protected function save()
726
    {
727 2
        $this->saveItems();
728 2
        $this->saveAssignments();
729 2
        $this->saveRules();
730 2
    }
731
732
    /**
733
     * Loads the authorization data from a PHP script file.
734
     *
735
     * @param string $file the file path.
736
     * @return array the authorization data
737
     * @see saveToFile()
738
     */
739 21
    protected function loadFromFile($file)
740
    {
741 21
        if (is_file($file)) {
742 4
            return require($file);
743
        } else {
744 21
            return [];
745
        }
746
    }
747
748
    /**
749
     * Saves the authorization data to a PHP script file.
750
     *
751
     * @param array $data the authorization data
752
     * @param string $file the file path.
753
     * @see loadFromFile()
754
     */
755 19
    protected function saveToFile($data, $file)
756
    {
757 19
        file_put_contents($file, "<?php\nreturn " . VarDumper::export($data) . ";\n", LOCK_EX);
758 19
    }
759
760
    /**
761
     * Saves items data into persistent storage.
762
     */
763 19
    protected function saveItems()
764
    {
765 19
        $items = [];
766 19
        foreach ($this->items as $name => $item) {
767
            /* @var $item Item */
768 19
            $items[$name] = array_filter(
769
                [
770 19
                    'type' => $item->type,
771 19
                    'description' => $item->description,
772 19
                    'ruleName' => $item->ruleName,
773 19
                    'data' => $item->data,
774
                ]
775 19
            );
776 19
            if (isset($this->children[$name])) {
777 17
                foreach ($this->children[$name] as $child) {
778
                    /* @var $child Item */
779 17
                    $items[$name]['children'][] = $child->name;
780 17
                }
781 17
            }
782 19
        }
783 19
        $this->saveToFile($items, $this->itemFile);
784 19
    }
785
786
    /**
787
     * Saves assignments data into persistent storage.
788
     */
789 17
    protected function saveAssignments()
790
    {
791 17
        $assignmentData = [];
792 17
        foreach ($this->assignments as $userId => $assignments) {
793 17
            foreach ($assignments as $name => $assignment) {
794
                /* @var $assignment Assignment */
795 17
                $assignmentData[$userId][] = $assignment->roleName;
796 17
            }
797 17
        }
798 17
        $this->saveToFile($assignmentData, $this->assignmentFile);
799 17
    }
800
801
    /**
802
     * Saves rules data into persistent storage.
803
     */
804 18
    protected function saveRules()
805
    {
806 18
        $rules = [];
807 18
        foreach ($this->rules as $name => $rule) {
808 17
            $rules[$name] = serialize($rule);
809 18
        }
810 18
        $this->saveToFile($rules, $this->ruleFile);
811 18
    }
812
813
    /**
814
     * @inheritdoc
815
     */
816 1
    public function getUserIDsByRole($roleName)
817
    {
818 1
        $result = [];
819 1
        foreach ($this->assignments as $userID => $assignments) {
820 1
            foreach ($assignments as $userAssignment) {
821 1
                if ($userAssignment->roleName === $roleName && $userAssignment->userId === $userID) {
822 1
                    $result[] = (string)$userID;
823 1
                }
824 1
            }
825 1
        }
826 1
        return $result;
827
    }
828
}
829