1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* ActionFilter is the base class for action filters.
*
* An action filter will participate in the action execution workflow by responding to
* the `beforeAction` and `afterAction` events triggered by modules and controllers.
*
* Check implementation of [[\yii\filters\AccessControl]], [[\yii\filters\PageCache]] and [[\yii\filters\HttpCache]] as examples on how to use it.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ActionFilter extends Behavior
{
/**
* @var array list of action IDs that this filter should apply to. If this property is not set,
* then the filter applies to all actions, unless they are listed in [[except]].
* If an action ID appears in both [[only]] and [[except]], this filter will NOT apply to it.
*
* Note that if the filter is attached to a module, the action IDs should also include child module IDs (if any)
* and controller IDs.
*
* @see except
*/
public $only;
/**
* @var array list of action IDs that this filter should not apply to.
* @see only
*/
public $except = [];
/**
* @inheritdoc
*/
public function attach($owner)
{
$this->owner = $owner;
$owner->on(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);
}
/**
* @inheritdoc
*/
public function detach()
{
if ($this->owner) {
$this->owner->off(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);
$this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);
$this->owner = null;
}
}
/**
* @param ActionEvent $event
*/
public function beforeFilter($event)
{
if (!$this->isActive($event->action)) {
return;
}
$event->isValid = $this->beforeAction($event->action);
if ($event->isValid) {
// call afterFilter only if beforeFilter succeeds
// beforeFilter and afterFilter should be properly nested
$this->owner->on(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter'], null, false);
} else {
$event->handled = true;
}
}
/**
* @param ActionEvent $event
*/
public function afterFilter($event)
{
$event->result = $this->afterAction($event->action, $event->result);
$this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);
}
/**
* This method is invoked right before an action is to be executed (after all possible filters.)
* You may override this method to do last-minute preparation for the action.
* @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed.
*/
public function beforeAction($action)
{
return true;
}
/**
* This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action.
* @param Action $action the action just executed.
* @param mixed $result the action execution result
* @return mixed the processed action result.
*/
public function afterAction($action, $result)
{
return $result;
}
/**
* Returns a value indicating whether the filer is active for the given action.
* @param Action $action the action being filtered
* @return boolean whether the filer is active for the given action.
*/
protected function isActive($action)
{
if ($this->owner instanceof Module) {
// convert action uniqueId into an ID relative to the module
$mid = $this->owner->getUniqueId();
$id = $action->getUniqueId();
if ($mid !== '' && strpos($id, $mid) === 0) {
$id = substr($id, strlen($mid) + 1);
}
} else {
$id = $action->id;
}
return !in_array($id, $this->except, true) && (empty($this->only) || in_array($id, $this->only, true));
}
}