PhpManager.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  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. namespace yii\rbac;
  8. use yii\base\InvalidCallException;
  9. use yii\base\InvalidParamException;
  10. use Yii;
  11. use yii\helpers\VarDumper;
  12. /**
  13. * PhpManager represents an authorization manager that stores authorization
  14. * information in terms of a PHP script file.
  15. *
  16. * The authorization data will be saved to and loaded from three files
  17. * specified by [[itemFile]], [[assignmentFile]] and [[ruleFile]].
  18. *
  19. * PhpManager is mainly suitable for authorization data that is not too big
  20. * (for example, the authorization data for a personal blog system).
  21. * Use [[DbManager]] for more complex authorization data.
  22. *
  23. * Note that PhpManager is not compatible with facebooks [HHVM](http://hhvm.com/) because
  24. * it relies on writing php files and including them afterwards which is not supported by HHVM.
  25. *
  26. * For more details and usage information on PhpManager, see the [guide article on security authorization](guide:security-authorization).
  27. *
  28. * @author Qiang Xue <qiang.xue@gmail.com>
  29. * @author Alexander Kochetov <creocoder@gmail.com>
  30. * @author Christophe Boulain <christophe.boulain@gmail.com>
  31. * @author Alexander Makarov <sam@rmcreative.ru>
  32. * @since 2.0
  33. */
  34. class PhpManager extends BaseManager
  35. {
  36. /**
  37. * @var string the path of the PHP script that contains the authorization items.
  38. * This can be either a file path or a path alias to the file.
  39. * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
  40. * @see loadFromFile()
  41. * @see saveToFile()
  42. */
  43. public $itemFile = '@app/rbac/items.php';
  44. /**
  45. * @var string the path of the PHP script that contains the authorization assignments.
  46. * This can be either a file path or a path alias to the file.
  47. * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
  48. * @see loadFromFile()
  49. * @see saveToFile()
  50. */
  51. public $assignmentFile = '@app/rbac/assignments.php';
  52. /**
  53. * @var string the path of the PHP script that contains the authorization rules.
  54. * This can be either a file path or a path alias to the file.
  55. * Make sure this file is writable by the Web server process if the authorization needs to be changed online.
  56. * @see loadFromFile()
  57. * @see saveToFile()
  58. */
  59. public $ruleFile = '@app/rbac/rules.php';
  60. /**
  61. * @var Item[]
  62. */
  63. protected $items = []; // itemName => item
  64. /**
  65. * @var array
  66. */
  67. protected $children = []; // itemName, childName => child
  68. /**
  69. * @var array
  70. */
  71. protected $assignments = []; // userId, itemName => assignment
  72. /**
  73. * @var Rule[]
  74. */
  75. protected $rules = []; // ruleName => rule
  76. /**
  77. * Initializes the application component.
  78. * This method overrides parent implementation by loading the authorization data
  79. * from PHP script.
  80. */
  81. public function init()
  82. {
  83. parent::init();
  84. $this->itemFile = Yii::getAlias($this->itemFile);
  85. $this->assignmentFile = Yii::getAlias($this->assignmentFile);
  86. $this->ruleFile = Yii::getAlias($this->ruleFile);
  87. $this->load();
  88. }
  89. /**
  90. * @inheritdoc
  91. */
  92. public function checkAccess($userId, $permissionName, $params = [])
  93. {
  94. $assignments = $this->getAssignments($userId);
  95. if ($this->hasNoAssignments($assignments)) {
  96. return false;
  97. }
  98. return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);
  99. }
  100. /**
  101. * @inheritdoc
  102. */
  103. public function getAssignments($userId)
  104. {
  105. return isset($this->assignments[$userId]) ? $this->assignments[$userId] : [];
  106. }
  107. /**
  108. * Performs access check for the specified user.
  109. * This method is internally called by [[checkAccess()]].
  110. *
  111. * @param string|int $user the user ID. This should can be either an integer or a string representing
  112. * the unique identifier of a user. See [[\yii\web\User::id]].
  113. * @param string $itemName the name of the operation that need access check
  114. * @param array $params name-value pairs that would be passed to rules associated
  115. * with the tasks and roles assigned to the user. A param with name 'user' is added to this array,
  116. * which holds the value of `$userId`.
  117. * @param Assignment[] $assignments the assignments to the specified user
  118. * @return bool whether the operations can be performed by the user.
  119. */
  120. protected function checkAccessRecursive($user, $itemName, $params, $assignments)
  121. {
  122. if (!isset($this->items[$itemName])) {
  123. return false;
  124. }
  125. /* @var $item Item */
  126. $item = $this->items[$itemName];
  127. Yii::trace($item instanceof Role ? "Checking role: $itemName" : "Checking permission : $itemName", __METHOD__);
  128. if (!$this->executeRule($user, $item, $params)) {
  129. return false;
  130. }
  131. if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {
  132. return true;
  133. }
  134. foreach ($this->children as $parentName => $children) {
  135. if (isset($children[$itemName]) && $this->checkAccessRecursive($user, $parentName, $params, $assignments)) {
  136. return true;
  137. }
  138. }
  139. return false;
  140. }
  141. /**
  142. * @inheritdoc
  143. * @since 2.0.8
  144. */
  145. public function canAddChild($parent, $child)
  146. {
  147. return !$this->detectLoop($parent, $child);
  148. }
  149. /**
  150. * @inheritdoc
  151. */
  152. public function addChild($parent, $child)
  153. {
  154. if (!isset($this->items[$parent->name], $this->items[$child->name])) {
  155. throw new InvalidParamException("Either '{$parent->name}' or '{$child->name}' does not exist.");
  156. }
  157. if ($parent->name === $child->name) {
  158. throw new InvalidParamException("Cannot add '{$parent->name} ' as a child of itself.");
  159. }
  160. if ($parent instanceof Permission && $child instanceof Role) {
  161. throw new InvalidParamException('Cannot add a role as a child of a permission.');
  162. }
  163. if ($this->detectLoop($parent, $child)) {
  164. throw new InvalidCallException("Cannot add '{$child->name}' as a child of '{$parent->name}'. A loop has been detected.");
  165. }
  166. if (isset($this->children[$parent->name][$child->name])) {
  167. throw new InvalidCallException("The item '{$parent->name}' already has a child '{$child->name}'.");
  168. }
  169. $this->children[$parent->name][$child->name] = $this->items[$child->name];
  170. $this->saveItems();
  171. return true;
  172. }
  173. /**
  174. * Checks whether there is a loop in the authorization item hierarchy.
  175. *
  176. * @param Item $parent parent item
  177. * @param Item $child the child item that is to be added to the hierarchy
  178. * @return bool whether a loop exists
  179. */
  180. protected function detectLoop($parent, $child)
  181. {
  182. if ($child->name === $parent->name) {
  183. return true;
  184. }
  185. if (!isset($this->children[$child->name], $this->items[$parent->name])) {
  186. return false;
  187. }
  188. foreach ($this->children[$child->name] as $grandchild) {
  189. /* @var $grandchild Item */
  190. if ($this->detectLoop($parent, $grandchild)) {
  191. return true;
  192. }
  193. }
  194. return false;
  195. }
  196. /**
  197. * @inheritdoc
  198. */
  199. public function removeChild($parent, $child)
  200. {
  201. if (isset($this->children[$parent->name][$child->name])) {
  202. unset($this->children[$parent->name][$child->name]);
  203. $this->saveItems();
  204. return true;
  205. } else {
  206. return false;
  207. }
  208. }
  209. /**
  210. * @inheritdoc
  211. */
  212. public function removeChildren($parent)
  213. {
  214. if (isset($this->children[$parent->name])) {
  215. unset($this->children[$parent->name]);
  216. $this->saveItems();
  217. return true;
  218. } else {
  219. return false;
  220. }
  221. }
  222. /**
  223. * @inheritdoc
  224. */
  225. public function hasChild($parent, $child)
  226. {
  227. return isset($this->children[$parent->name][$child->name]);
  228. }
  229. /**
  230. * @inheritdoc
  231. */
  232. public function assign($role, $userId)
  233. {
  234. if (!isset($this->items[$role->name])) {
  235. throw new InvalidParamException("Unknown role '{$role->name}'.");
  236. } elseif (isset($this->assignments[$userId][$role->name])) {
  237. throw new InvalidParamException("Authorization item '{$role->name}' has already been assigned to user '$userId'.");
  238. } else {
  239. $this->assignments[$userId][$role->name] = new Assignment([
  240. 'userId' => $userId,
  241. 'roleName' => $role->name,
  242. 'createdAt' => time(),
  243. ]);
  244. $this->saveAssignments();
  245. return $this->assignments[$userId][$role->name];
  246. }
  247. }
  248. /**
  249. * @inheritdoc
  250. */
  251. public function revoke($role, $userId)
  252. {
  253. if (isset($this->assignments[$userId][$role->name])) {
  254. unset($this->assignments[$userId][$role->name]);
  255. $this->saveAssignments();
  256. return true;
  257. } else {
  258. return false;
  259. }
  260. }
  261. /**
  262. * @inheritdoc
  263. */
  264. public function revokeAll($userId)
  265. {
  266. if (isset($this->assignments[$userId]) && is_array($this->assignments[$userId])) {
  267. foreach ($this->assignments[$userId] as $itemName => $value) {
  268. unset($this->assignments[$userId][$itemName]);
  269. }
  270. $this->saveAssignments();
  271. return true;
  272. } else {
  273. return false;
  274. }
  275. }
  276. /**
  277. * @inheritdoc
  278. */
  279. public function getAssignment($roleName, $userId)
  280. {
  281. return isset($this->assignments[$userId][$roleName]) ? $this->assignments[$userId][$roleName] : null;
  282. }
  283. /**
  284. * @inheritdoc
  285. */
  286. public function getItems($type)
  287. {
  288. $items = [];
  289. foreach ($this->items as $name => $item) {
  290. /* @var $item Item */
  291. if ($item->type == $type) {
  292. $items[$name] = $item;
  293. }
  294. }
  295. return $items;
  296. }
  297. /**
  298. * @inheritdoc
  299. */
  300. public function removeItem($item)
  301. {
  302. if (isset($this->items[$item->name])) {
  303. foreach ($this->children as &$children) {
  304. unset($children[$item->name]);
  305. }
  306. foreach ($this->assignments as &$assignments) {
  307. unset($assignments[$item->name]);
  308. }
  309. unset($this->items[$item->name]);
  310. $this->saveItems();
  311. $this->saveAssignments();
  312. return true;
  313. } else {
  314. return false;
  315. }
  316. }
  317. /**
  318. * @inheritdoc
  319. */
  320. public function getItem($name)
  321. {
  322. return isset($this->items[$name]) ? $this->items[$name] : null;
  323. }
  324. /**
  325. * @inheritdoc
  326. */
  327. public function updateRule($name, $rule)
  328. {
  329. if ($rule->name !== $name) {
  330. unset($this->rules[$name]);
  331. }
  332. $this->rules[$rule->name] = $rule;
  333. $this->saveRules();
  334. return true;
  335. }
  336. /**
  337. * @inheritdoc
  338. */
  339. public function getRule($name)
  340. {
  341. return isset($this->rules[$name]) ? $this->rules[$name] : null;
  342. }
  343. /**
  344. * @inheritdoc
  345. */
  346. public function getRules()
  347. {
  348. return $this->rules;
  349. }
  350. /**
  351. * @inheritdoc
  352. */
  353. public function getRolesByUser($userId)
  354. {
  355. $roles = [];
  356. foreach ($this->getAssignments($userId) as $name => $assignment) {
  357. $role = $this->items[$assignment->roleName];
  358. if ($role->type === Item::TYPE_ROLE) {
  359. $roles[$name] = $role;
  360. }
  361. }
  362. return $roles;
  363. }
  364. /**
  365. * @inheritdoc
  366. */
  367. public function getChildRoles($roleName)
  368. {
  369. $role = $this->getRole($roleName);
  370. if (is_null($role)) {
  371. throw new InvalidParamException("Role \"$roleName\" not found.");
  372. }
  373. $result = [];
  374. $this->getChildrenRecursive($roleName, $result);
  375. $roles = [$roleName => $role];
  376. $roles += array_filter($this->getRoles(), function (Role $roleItem) use ($result) {
  377. return array_key_exists($roleItem->name, $result);
  378. });
  379. return $roles;
  380. }
  381. /**
  382. * @inheritdoc
  383. */
  384. public function getPermissionsByRole($roleName)
  385. {
  386. $result = [];
  387. $this->getChildrenRecursive($roleName, $result);
  388. if (empty($result)) {
  389. return [];
  390. }
  391. $permissions = [];
  392. foreach (array_keys($result) as $itemName) {
  393. if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
  394. $permissions[$itemName] = $this->items[$itemName];
  395. }
  396. }
  397. return $permissions;
  398. }
  399. /**
  400. * Recursively finds all children and grand children of the specified item.
  401. *
  402. * @param string $name the name of the item whose children are to be looked for.
  403. * @param array $result the children and grand children (in array keys)
  404. */
  405. protected function getChildrenRecursive($name, &$result)
  406. {
  407. if (isset($this->children[$name])) {
  408. foreach ($this->children[$name] as $child) {
  409. $result[$child->name] = true;
  410. $this->getChildrenRecursive($child->name, $result);
  411. }
  412. }
  413. }
  414. /**
  415. * @inheritdoc
  416. */
  417. public function getPermissionsByUser($userId)
  418. {
  419. $directPermission = $this->getDirectPermissionsByUser($userId);
  420. $inheritedPermission = $this->getInheritedPermissionsByUser($userId);
  421. return array_merge($directPermission, $inheritedPermission);
  422. }
  423. /**
  424. * Returns all permissions that are directly assigned to user.
  425. * @param string|int $userId the user ID (see [[\yii\web\User::id]])
  426. * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.
  427. * @since 2.0.7
  428. */
  429. protected function getDirectPermissionsByUser($userId)
  430. {
  431. $permissions = [];
  432. foreach ($this->getAssignments($userId) as $name => $assignment) {
  433. $permission = $this->items[$assignment->roleName];
  434. if ($permission->type === Item::TYPE_PERMISSION) {
  435. $permissions[$name] = $permission;
  436. }
  437. }
  438. return $permissions;
  439. }
  440. /**
  441. * Returns all permissions that the user inherits from the roles assigned to him.
  442. * @param string|int $userId the user ID (see [[\yii\web\User::id]])
  443. * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.
  444. * @since 2.0.7
  445. */
  446. protected function getInheritedPermissionsByUser($userId)
  447. {
  448. $assignments = $this->getAssignments($userId);
  449. $result = [];
  450. foreach (array_keys($assignments) as $roleName) {
  451. $this->getChildrenRecursive($roleName, $result);
  452. }
  453. if (empty($result)) {
  454. return [];
  455. }
  456. $permissions = [];
  457. foreach (array_keys($result) as $itemName) {
  458. if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {
  459. $permissions[$itemName] = $this->items[$itemName];
  460. }
  461. }
  462. return $permissions;
  463. }
  464. /**
  465. * @inheritdoc
  466. */
  467. public function getChildren($name)
  468. {
  469. return isset($this->children[$name]) ? $this->children[$name] : [];
  470. }
  471. /**
  472. * @inheritdoc
  473. */
  474. public function removeAll()
  475. {
  476. $this->children = [];
  477. $this->items = [];
  478. $this->assignments = [];
  479. $this->rules = [];
  480. $this->save();
  481. }
  482. /**
  483. * @inheritdoc
  484. */
  485. public function removeAllPermissions()
  486. {
  487. $this->removeAllItems(Item::TYPE_PERMISSION);
  488. }
  489. /**
  490. * @inheritdoc
  491. */
  492. public function removeAllRoles()
  493. {
  494. $this->removeAllItems(Item::TYPE_ROLE);
  495. }
  496. /**
  497. * Removes all auth items of the specified type.
  498. * @param int $type the auth item type (either Item::TYPE_PERMISSION or Item::TYPE_ROLE)
  499. */
  500. protected function removeAllItems($type)
  501. {
  502. $names = [];
  503. foreach ($this->items as $name => $item) {
  504. if ($item->type == $type) {
  505. unset($this->items[$name]);
  506. $names[$name] = true;
  507. }
  508. }
  509. if (empty($names)) {
  510. return;
  511. }
  512. foreach ($this->assignments as $i => $assignments) {
  513. foreach ($assignments as $n => $assignment) {
  514. if (isset($names[$assignment->roleName])) {
  515. unset($this->assignments[$i][$n]);
  516. }
  517. }
  518. }
  519. foreach ($this->children as $name => $children) {
  520. if (isset($names[$name])) {
  521. unset($this->children[$name]);
  522. } else {
  523. foreach ($children as $childName => $item) {
  524. if (isset($names[$childName])) {
  525. unset($children[$childName]);
  526. }
  527. }
  528. $this->children[$name] = $children;
  529. }
  530. }
  531. $this->saveItems();
  532. }
  533. /**
  534. * @inheritdoc
  535. */
  536. public function removeAllRules()
  537. {
  538. foreach ($this->items as $item) {
  539. $item->ruleName = null;
  540. }
  541. $this->rules = [];
  542. $this->saveRules();
  543. }
  544. /**
  545. * @inheritdoc
  546. */
  547. public function removeAllAssignments()
  548. {
  549. $this->assignments = [];
  550. $this->saveAssignments();
  551. }
  552. /**
  553. * @inheritdoc
  554. */
  555. protected function removeRule($rule)
  556. {
  557. if (isset($this->rules[$rule->name])) {
  558. unset($this->rules[$rule->name]);
  559. foreach ($this->items as $item) {
  560. if ($item->ruleName === $rule->name) {
  561. $item->ruleName = null;
  562. }
  563. }
  564. $this->saveRules();
  565. return true;
  566. } else {
  567. return false;
  568. }
  569. }
  570. /**
  571. * @inheritdoc
  572. */
  573. protected function addRule($rule)
  574. {
  575. $this->rules[$rule->name] = $rule;
  576. $this->saveRules();
  577. return true;
  578. }
  579. /**
  580. * @inheritdoc
  581. */
  582. protected function updateItem($name, $item)
  583. {
  584. if ($name !== $item->name) {
  585. if (isset($this->items[$item->name])) {
  586. throw new InvalidParamException("Unable to change the item name. The name '{$item->name}' is already used by another item.");
  587. } else {
  588. // Remove old item in case of renaming
  589. unset($this->items[$name]);
  590. if (isset($this->children[$name])) {
  591. $this->children[$item->name] = $this->children[$name];
  592. unset($this->children[$name]);
  593. }
  594. foreach ($this->children as &$children) {
  595. if (isset($children[$name])) {
  596. $children[$item->name] = $children[$name];
  597. unset($children[$name]);
  598. }
  599. }
  600. foreach ($this->assignments as &$assignments) {
  601. if (isset($assignments[$name])) {
  602. $assignments[$item->name] = $assignments[$name];
  603. $assignments[$item->name]->roleName = $item->name;
  604. unset($assignments[$name]);
  605. }
  606. }
  607. $this->saveAssignments();
  608. }
  609. }
  610. $this->items[$item->name] = $item;
  611. $this->saveItems();
  612. return true;
  613. }
  614. /**
  615. * @inheritdoc
  616. */
  617. protected function addItem($item)
  618. {
  619. $time = time();
  620. if ($item->createdAt === null) {
  621. $item->createdAt = $time;
  622. }
  623. if ($item->updatedAt === null) {
  624. $item->updatedAt = $time;
  625. }
  626. $this->items[$item->name] = $item;
  627. $this->saveItems();
  628. return true;
  629. }
  630. /**
  631. * Loads authorization data from persistent storage.
  632. */
  633. protected function load()
  634. {
  635. $this->children = [];
  636. $this->rules = [];
  637. $this->assignments = [];
  638. $this->items = [];
  639. $items = $this->loadFromFile($this->itemFile);
  640. $itemsMtime = @filemtime($this->itemFile);
  641. $assignments = $this->loadFromFile($this->assignmentFile);
  642. $assignmentsMtime = @filemtime($this->assignmentFile);
  643. $rules = $this->loadFromFile($this->ruleFile);
  644. foreach ($items as $name => $item) {
  645. $class = $item['type'] == Item::TYPE_PERMISSION ? Permission::className() : Role::className();
  646. $this->items[$name] = new $class([
  647. 'name' => $name,
  648. 'description' => isset($item['description']) ? $item['description'] : null,
  649. 'ruleName' => isset($item['ruleName']) ? $item['ruleName'] : null,
  650. 'data' => isset($item['data']) ? $item['data'] : null,
  651. 'createdAt' => $itemsMtime,
  652. 'updatedAt' => $itemsMtime,
  653. ]);
  654. }
  655. foreach ($items as $name => $item) {
  656. if (isset($item['children'])) {
  657. foreach ($item['children'] as $childName) {
  658. if (isset($this->items[$childName])) {
  659. $this->children[$name][$childName] = $this->items[$childName];
  660. }
  661. }
  662. }
  663. }
  664. foreach ($assignments as $userId => $roles) {
  665. foreach ($roles as $role) {
  666. $this->assignments[$userId][$role] = new Assignment([
  667. 'userId' => $userId,
  668. 'roleName' => $role,
  669. 'createdAt' => $assignmentsMtime,
  670. ]);
  671. }
  672. }
  673. foreach ($rules as $name => $ruleData) {
  674. $this->rules[$name] = unserialize($ruleData);
  675. }
  676. }
  677. /**
  678. * Saves authorization data into persistent storage.
  679. */
  680. protected function save()
  681. {
  682. $this->saveItems();
  683. $this->saveAssignments();
  684. $this->saveRules();
  685. }
  686. /**
  687. * Loads the authorization data from a PHP script file.
  688. *
  689. * @param string $file the file path.
  690. * @return array the authorization data
  691. * @see saveToFile()
  692. */
  693. protected function loadFromFile($file)
  694. {
  695. if (is_file($file)) {
  696. return require($file);
  697. } else {
  698. return [];
  699. }
  700. }
  701. /**
  702. * Saves the authorization data to a PHP script file.
  703. *
  704. * @param array $data the authorization data
  705. * @param string $file the file path.
  706. * @see loadFromFile()
  707. */
  708. protected function saveToFile($data, $file)
  709. {
  710. file_put_contents($file, "<?php\nreturn " . VarDumper::export($data) . ";\n", LOCK_EX);
  711. $this->invalidateScriptCache($file);
  712. }
  713. /**
  714. * Invalidates precompiled script cache (such as OPCache or APC) for the given file.
  715. * @param string $file the file path.
  716. * @since 2.0.9
  717. */
  718. protected function invalidateScriptCache($file)
  719. {
  720. if (function_exists('opcache_invalidate')) {
  721. opcache_invalidate($file, true);
  722. }
  723. if (function_exists('apc_delete_file')) {
  724. @apc_delete_file($file);
  725. }
  726. }
  727. /**
  728. * Saves items data into persistent storage.
  729. */
  730. protected function saveItems()
  731. {
  732. $items = [];
  733. foreach ($this->items as $name => $item) {
  734. /* @var $item Item */
  735. $items[$name] = array_filter(
  736. [
  737. 'type' => $item->type,
  738. 'description' => $item->description,
  739. 'ruleName' => $item->ruleName,
  740. 'data' => $item->data,
  741. ]
  742. );
  743. if (isset($this->children[$name])) {
  744. foreach ($this->children[$name] as $child) {
  745. /* @var $child Item */
  746. $items[$name]['children'][] = $child->name;
  747. }
  748. }
  749. }
  750. $this->saveToFile($items, $this->itemFile);
  751. }
  752. /**
  753. * Saves assignments data into persistent storage.
  754. */
  755. protected function saveAssignments()
  756. {
  757. $assignmentData = [];
  758. foreach ($this->assignments as $userId => $assignments) {
  759. foreach ($assignments as $name => $assignment) {
  760. /* @var $assignment Assignment */
  761. $assignmentData[$userId][] = $assignment->roleName;
  762. }
  763. }
  764. $this->saveToFile($assignmentData, $this->assignmentFile);
  765. }
  766. /**
  767. * Saves rules data into persistent storage.
  768. */
  769. protected function saveRules()
  770. {
  771. $rules = [];
  772. foreach ($this->rules as $name => $rule) {
  773. $rules[$name] = serialize($rule);
  774. }
  775. $this->saveToFile($rules, $this->ruleFile);
  776. }
  777. /**
  778. * @inheritdoc
  779. * @since 2.0.7
  780. */
  781. public function getUserIdsByRole($roleName)
  782. {
  783. $result = [];
  784. foreach ($this->assignments as $userID => $assignments) {
  785. foreach ($assignments as $userAssignment) {
  786. if ($userAssignment->roleName === $roleName && $userAssignment->userId == $userID) {
  787. $result[] = (string)$userID;
  788. }
  789. }
  790. }
  791. return $result;
  792. }
  793. }