ActionFilter.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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\base;
  8. use yii\helpers\StringHelper;
  9. /**
  10. * ActionFilter is the base class for action filters.
  11. *
  12. * An action filter will participate in the action execution workflow by responding to
  13. * the `beforeAction` and `afterAction` events triggered by modules and controllers.
  14. *
  15. * Check implementation of [[\yii\filters\AccessControl]], [[\yii\filters\PageCache]] and [[\yii\filters\HttpCache]] as examples on how to use it.
  16. *
  17. * For more details and usage information on ActionFilter, see the [guide article on filters](guide:structure-filters).
  18. *
  19. * @author Qiang Xue <qiang.xue@gmail.com>
  20. * @since 2.0
  21. */
  22. class ActionFilter extends Behavior
  23. {
  24. /**
  25. * @var array list of action IDs that this filter should apply to. If this property is not set,
  26. * then the filter applies to all actions, unless they are listed in [[except]].
  27. * If an action ID appears in both [[only]] and [[except]], this filter will NOT apply to it.
  28. *
  29. * Note that if the filter is attached to a module, the action IDs should also include child module IDs (if any)
  30. * and controller IDs.
  31. *
  32. * Since version 2.0.9 action IDs can be specified as wildcards, e.g. `site/*`.
  33. *
  34. * @see except
  35. */
  36. public $only;
  37. /**
  38. * @var array list of action IDs that this filter should not apply to.
  39. * @see only
  40. */
  41. public $except = [];
  42. /**
  43. * {@inheritdoc}
  44. */
  45. public function attach($owner)
  46. {
  47. $this->owner = $owner;
  48. $owner->on(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);
  49. }
  50. /**
  51. * {@inheritdoc}
  52. */
  53. public function detach()
  54. {
  55. if ($this->owner) {
  56. $this->owner->off(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);
  57. $this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);
  58. $this->owner = null;
  59. }
  60. }
  61. /**
  62. * @param ActionEvent $event
  63. */
  64. public function beforeFilter($event)
  65. {
  66. if (!$this->isActive($event->action)) {
  67. return;
  68. }
  69. $event->isValid = $this->beforeAction($event->action);
  70. if ($event->isValid) {
  71. // call afterFilter only if beforeFilter succeeds
  72. // beforeFilter and afterFilter should be properly nested
  73. $this->owner->on(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter'], null, false);
  74. } else {
  75. $event->handled = true;
  76. }
  77. }
  78. /**
  79. * @param ActionEvent $event
  80. */
  81. public function afterFilter($event)
  82. {
  83. $event->result = $this->afterAction($event->action, $event->result);
  84. $this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);
  85. }
  86. /**
  87. * This method is invoked right before an action is to be executed (after all possible filters.)
  88. * You may override this method to do last-minute preparation for the action.
  89. * @param Action $action the action to be executed.
  90. * @return bool whether the action should continue to be executed.
  91. */
  92. public function beforeAction($action)
  93. {
  94. return true;
  95. }
  96. /**
  97. * This method is invoked right after an action is executed.
  98. * You may override this method to do some postprocessing for the action.
  99. * @param Action $action the action just executed.
  100. * @param mixed $result the action execution result
  101. * @return mixed the processed action result.
  102. */
  103. public function afterAction($action, $result)
  104. {
  105. return $result;
  106. }
  107. /**
  108. * Returns an action ID by converting [[Action::$uniqueId]] into an ID relative to the module.
  109. * @param Action $action
  110. * @return string
  111. * @since 2.0.7
  112. */
  113. protected function getActionId($action)
  114. {
  115. if ($this->owner instanceof Module) {
  116. $mid = $this->owner->getUniqueId();
  117. $id = $action->getUniqueId();
  118. if ($mid !== '' && strpos($id, $mid) === 0) {
  119. $id = substr($id, strlen($mid) + 1);
  120. }
  121. } else {
  122. $id = $action->id;
  123. }
  124. return $id;
  125. }
  126. /**
  127. * Returns a value indicating whether the filter is active for the given action.
  128. * @param Action $action the action being filtered
  129. * @return bool whether the filter is active for the given action.
  130. */
  131. protected function isActive($action)
  132. {
  133. $id = $this->getActionId($action);
  134. if (empty($this->only)) {
  135. $onlyMatch = true;
  136. } else {
  137. $onlyMatch = false;
  138. foreach ($this->only as $pattern) {
  139. if (StringHelper::matchWildcard($pattern, $id)) {
  140. $onlyMatch = true;
  141. break;
  142. }
  143. }
  144. }
  145. $exceptMatch = false;
  146. foreach ($this->except as $pattern) {
  147. if (StringHelper::matchWildcard($pattern, $id)) {
  148. $exceptMatch = true;
  149. break;
  150. }
  151. }
  152. return !$exceptMatch && $onlyMatch;
  153. }
  154. }