ActionFilter.php 4.13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\base;

/**
11 12 13 14
 * 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.
15
 *
16
 * Check implementation of [[\yii\filters\AccessControl]], [[\yii\filters\PageCache]] and [[\yii\filters\HttpCache]] as examples on how to use it.
17
 *
18 19 20
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
Qiang Xue committed
21
class ActionFilter extends Behavior
22
{
23 24 25 26
    /**
     * @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.
Qiang Xue committed
27 28 29
     *
     * Note that if the filter is attached to a module, the action IDs should also include child module IDs (if any)
     * and controller IDs.
30
     *
31 32 33 34 35 36 37 38
     * @see except
     */
    public $only;
    /**
     * @var array list of action IDs that this filter should not apply to.
     * @see only
     */
    public $except = [];
Qiang Xue committed
39

40

41
    /**
42
     * @inheritdoc
43
     */
44
    public function attach($owner)
45
    {
46 47 48 49 50 51 52 53 54 55 56 57 58 59
        $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;
        }
60
    }
61

62
    /**
63
     * @param ActionEvent $event
64 65 66
     */
    public function beforeFilter($event)
    {
67 68
        if (!$this->isActive($event->action)) {
            return;
69
        }
70

71 72 73 74 75 76 77 78
        $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;
        }
79
    }
Qiang Xue committed
80

81
    /**
82
     * @param ActionEvent $event
83 84 85
     */
    public function afterFilter($event)
    {
86 87
        $event->result = $this->afterAction($event->action, $event->result);
        $this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);
88
    }
Qiang Xue committed
89

90 91 92
    /**
     * 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.
93
     * @param Action $action the action to be executed.
94 95 96 97 98 99
     * @return boolean whether the action should continue to be executed.
     */
    public function beforeAction($action)
    {
        return true;
    }
100

101 102 103
    /**
     * This method is invoked right after an action is executed.
     * You may override this method to do some postprocessing for the action.
104 105 106
     * @param Action $action the action just executed.
     * @param mixed $result the action execution result
     * @return mixed the processed action result.
107 108 109 110 111 112 113 114
     */
    public function afterAction($action, $result)
    {
        return $result;
    }

    /**
     * Returns a value indicating whether the filer is active for the given action.
115
     * @param Action $action the action being filtered
116 117 118 119
     * @return boolean whether the filer is active for the given action.
     */
    protected function isActive($action)
    {
120 121 122 123 124 125 126 127 128 129 130
        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));
131
    }
Zander Baldwin committed
132
}