Issues (915)

framework/base/ActionFilter.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * @link https://www.yiiframework.com/
5
 * @copyright Copyright (c) 2008 Yii Software LLC
6
 * @license https://www.yiiframework.com/license/
7
 */
8
9
namespace yii\base;
10
11
use yii\helpers\StringHelper;
12
13
/**
14
 * ActionFilter is the base class for action filters.
15
 *
16
 * An action filter will participate in the action execution workflow by responding to
17
 * the `beforeAction` and `afterAction` events triggered by modules and controllers.
18
 *
19
 * Check implementation of [[\yii\filters\AccessControl]], [[\yii\filters\PageCache]] and [[\yii\filters\HttpCache]] as examples on how to use it.
20
 *
21
 * For more details and usage information on ActionFilter, see the [guide article on filters](guide:structure-filters).
22
 *
23
 * @author Qiang Xue <[email protected]>
24
 * @since 2.0
25
 */
26
class ActionFilter extends Behavior
27
{
28
    /**
29
     * @var array list of action IDs that this filter should apply to. If this property is not set,
30
     * then the filter applies to all actions, unless they are listed in [[except]].
31
     * If an action ID appears in both [[only]] and [[except]], this filter will NOT apply to it.
32
     *
33
     * Note that if the filter is attached to a module, the action IDs should also include child module IDs (if any)
34
     * and controller IDs.
35
     *
36
     * Since version 2.0.9 action IDs can be specified as wildcards, e.g. `site/*`.
37
     *
38
     * @see except
39
     */
40
    public $only = [];
41
    /**
42
     * @var array list of action IDs that this filter should not apply to.
43
     * @see only
44
     */
45
    public $except = [];
46
47
48
    /**
49
     * {@inheritdoc}
50
     */
51 76
    public function attach($owner)
52
    {
53 76
        $this->owner = $owner;
54 76
        $owner->on(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function detach()
61
    {
62
        if ($this->owner) {
63
            $this->owner->off(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);
64
            $this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);
65
            $this->owner = null;
66
        }
67
    }
68
69
    /**
70
     * @param ActionEvent $event
71
     */
72 76
    public function beforeFilter($event)
73
    {
74 76
        if (!$this->isActive($event->action)) {
75
            return;
76
        }
77
78 76
        $event->isValid = $this->beforeAction($event->action);
79 70
        if ($event->isValid) {
80
            // call afterFilter only if beforeFilter succeeds
81
            // beforeFilter and afterFilter should be properly nested
82 70
            $this->owner->on(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter'], null, false);
0 ignored issues
show
The method on() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

82
            $this->owner->/** @scrutinizer ignore-call */ 
83
                          on(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter'], null, false);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
83
        } else {
84 1
            $event->handled = true;
85
        }
86
    }
87
88
    /**
89
     * @param ActionEvent $event
90
     */
91 70
    public function afterFilter($event)
92
    {
93 70
        $event->result = $this->afterAction($event->action, $event->result);
94 70
        $this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);
95
    }
96
97
    /**
98
     * This method is invoked right before an action is to be executed (after all possible filters.)
99
     * You may override this method to do last-minute preparation for the action.
100
     * @param Action $action the action to be executed.
101
     * @return bool whether the action should continue to be executed.
102
     */
103
    public function beforeAction($action)
104
    {
105
        return true;
106
    }
107
108
    /**
109
     * This method is invoked right after an action is executed.
110
     * You may override this method to do some postprocessing for the action.
111
     * @param Action $action the action just executed.
112
     * @param mixed $result the action execution result
113
     * @return mixed the processed action result.
114
     */
115 69
    public function afterAction($action, $result)
116
    {
117 69
        return $result;
118
    }
119
120
    /**
121
     * Returns an action ID by converting [[Action::$uniqueId]] into an ID relative to the module.
122
     * @param Action $action
123
     * @return string
124
     * @since 2.0.7
125
     */
126 90
    protected function getActionId($action)
127
    {
128 90
        if ($this->owner instanceof Module) {
129
            $mid = $this->owner->getUniqueId();
130
            $id = $action->getUniqueId();
131
            if ($mid !== '' && strpos($id, $mid) === 0) {
132
                $id = substr($id, strlen($mid) + 1);
133
            }
134
        } else {
135 90
            $id = $action->id;
136
        }
137
138 90
        return $id;
139
    }
140
141
    /**
142
     * Returns a value indicating whether the filter is active for the given action.
143
     * @param Action $action the action being filtered
144
     * @return bool whether the filter is active for the given action.
145
     */
146 88
    protected function isActive($action)
147
    {
148 88
        $id = $this->getActionId($action);
149
150 88
        if (empty($this->only)) {
151 86
            $onlyMatch = true;
152
        } else {
153 76
            $onlyMatch = false;
154 76
            foreach ($this->only as $pattern) {
155 76
                if (StringHelper::matchWildcard($pattern, $id)) {
156 75
                    $onlyMatch = true;
157 75
                    break;
158
                }
159
            }
160
        }
161
162 88
        $exceptMatch = false;
163 88
        foreach ($this->except as $pattern) {
164 74
            if (StringHelper::matchWildcard($pattern, $id)) {
165 13
                $exceptMatch = true;
166 13
                break;
167
            }
168
        }
169
170 88
        return !$exceptMatch && $onlyMatch;
171
    }
172
}
173