Completed
Pull Request — 2.1 (#12704)
by Robert
08:59
created

Event::off()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.0061

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 24
ccs 17
cts 18
cp 0.9444
rs 8.5125
cc 6
eloc 16
nc 8
nop 3
crap 6.0061
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
8
namespace yii\base;
9
10
/**
11
 * Event is the base class for all event classes.
12
 *
13
 * It encapsulates the parameters associated with an event.
14
 * The [[sender]] property describes who raises the event.
15
 * And the [[handled]] property indicates if the event is handled.
16
 * If an event handler sets [[handled]] to be `true`, the rest of the
17
 * uninvoked handlers will no longer be called to handle the event.
18
 *
19
 * Additionally, when attaching an event handler, extra data may be passed
20
 * and be available via the [[data]] property when the event handler is invoked.
21
 *
22
 * @author Qiang Xue <[email protected]>
23
 * @since 2.0
24
 */
25
class Event extends Object
26
{
27
    /**
28
     * @var string the event name. This property is set by [[Component::trigger()]] and [[trigger()]].
29
     * Event handlers may use this property to check what event it is handling.
30
     */
31
    public $name;
32
    /**
33
     * @var object the sender of this event. If not set, this property will be
34
     * set as the object whose `trigger()` method is called.
35
     * This property may also be a `null` when this event is a
36
     * class-level event which is triggered in a static context.
37
     */
38
    public $sender;
39
    /**
40
     * @var boolean whether the event is handled. Defaults to `false`.
41
     * When a handler sets this to be `true`, the event processing will stop and
42
     * ignore the rest of the uninvoked event handlers.
43
     */
44
    public $handled = false;
45
    /**
46
     * @var mixed the data that is passed to [[Component::on()]] when attaching an event handler.
47
     * Note that this varies according to which event handler is currently executing.
48
     */
49
    public $data;
50
51
    /**
52
     * @var array contains all globally registered event handlers.
53
     */
54
    private static $_events = [];
55
56
57
    /**
58
     * Attaches an event handler to a class-level event.
59
     *
60
     * When a class-level event is triggered, event handlers attached
61
     * to that class and all parent classes will be invoked.
62
     *
63
     * For example, the following code attaches an event handler to `ActiveRecord`'s
64
     * `afterInsert` event:
65
     *
66
     * ```php
67
     * Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
68
     *     Yii::trace(get_class($event->sender) . ' is inserted.');
69
     * });
70
     * ```
71
     *
72
     * The handler will be invoked for EVERY successful ActiveRecord insertion.
73
     *
74
     * For more details about how to declare an event handler, please refer to [[Component::on()]].
75
     *
76
     * @param string $class the fully qualified class name to which the event handler needs to attach.
77
     * @param string $name the event name.
78
     * @param callable $handler the event handler.
79
     * @param mixed $data the data to be passed to the event handler when the event is triggered.
80
     * When the event handler is invoked, this data can be accessed via [[Event::data]].
81
     * @param boolean $append whether to append new event handler to the end of the existing
82
     * handler list. If `false`, the new handler will be inserted at the beginning of the existing
83
     * handler list.
84
     * @see off()
85
     */
86 11
    public static function on($class, $name, $handler, $data = null, $append = true)
87
    {
88 11
        $class = ltrim($class, '\\');
89 11
        if ($append || empty(self::$_events[$name][$class])) {
90 11
            self::$_events[$name][$class][] = [$handler, $data];
91 11
        } else {
92
            array_unshift(self::$_events[$name][$class], [$handler, $data]);
93
        }
94 11
    }
95
96
    /**
97
     * Detaches an event handler from a class-level event.
98
     *
99
     * This method is the opposite of [[on()]].
100
     *
101
     * @param string $class the fully qualified class name from which the event handler needs to be detached.
102
     * @param string $name the event name.
103
     * @param callable $handler the event handler to be removed.
104
     * If it is `null`, all handlers attached to the named event will be removed.
105
     * @return boolean whether a handler is found and detached.
106
     * @see on()
107
     */
108 9
    public static function off($class, $name, $handler = null)
109
    {
110 9
        $class = ltrim($class, '\\');
111 9
        if (empty(self::$_events[$name][$class])) {
112
            return false;
113
        }
114 9
        if ($handler === null) {
115 8
            unset(self::$_events[$name][$class]);
116 8
            return true;
117
        } else {
118 1
            $removed = false;
119 1
            foreach (self::$_events[$name][$class] as $i => $event) {
120 1
                if ($event[0] === $handler) {
121 1
                    unset(self::$_events[$name][$class][$i]);
122 1
                    $removed = true;
123 1
                }
124 1
            }
125 1
            if ($removed) {
126 1
                self::$_events[$name][$class] = array_values(self::$_events[$name][$class]);
127 1
            }
128
129 1
            return $removed;
130
        }
131
    }
132
133
    /**
134
     * Detaches all registered class-level event handlers.
135
     * @see on()
136
     * @see off()
137
     * @since 2.0.10
138
     */
139 3
    public static function offAll()
140
    {
141 3
        self::$_events = [];
142 3
    }
143
144
    /**
145
     * Returns a value indicating whether there is any handler attached to the specified class-level event.
146
     * Note that this method will also check all parent classes to see if there is any handler attached
147
     * to the named event.
148
     * @param string|object $class the object or the fully qualified class name specifying the class-level event.
149
     * @param string $name the event name.
150
     * @return boolean whether there is any handler attached to the event.
151
     */
152 66
    public static function hasHandlers($class, $name)
153
    {
154 66
        if (empty(self::$_events[$name])) {
155 66
            return false;
156
        }
157 4
        if (is_object($class)) {
158 2
            $class = get_class($class);
159 2
        } else {
160 2
            $class = ltrim($class, '\\');
161
        }
162
163 4
        $classes = array_merge(
164 4
            [$class],
165 4
            class_parents($class, true),
166 4
            class_implements($class, true)
167 4
        );
168
169 4
        foreach ($classes as $class) {
170 4
            if (!empty(self::$_events[$name][$class])) {
171 4
                return true;
172
            }
173 2
        }
174
175 2
        return false;
176
    }
177
178
    /**
179
     * Triggers a class-level event.
180
     * This method will cause invocation of event handlers that are attached to the named event
181
     * for the specified class and all its parent classes.
182
     * @param string|object $class the object or the fully qualified class name specifying the class-level event.
183
     * @param string $name the event name.
184
     * @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
185
     */
186 706
    public static function trigger($class, $name, $event = null)
187
    {
188 706
        if (empty(self::$_events[$name])) {
189 703
            return;
190
        }
191 9
        if ($event === null) {
192 7
            $event = new static;
193 7
        }
194 9
        $event->handled = false;
195 9
        $event->name = $name;
196
197 9
        if (is_object($class)) {
198 9
            if ($event->sender === null) {
199 9
                $event->sender = $class;
200 9
            }
201 9
            $class = get_class($class);
202 9
        } else {
203
            $class = ltrim($class, '\\');
204
        }
205
206 9
        $classes = array_merge(
207 9
            [$class],
208 9
            class_parents($class, true),
209 9
            class_implements($class, true)
210 9
        );
211
212 9
        foreach ($classes as $class) {
213 9
            if (!empty(self::$_events[$name][$class])) {
214 9
                foreach (self::$_events[$name][$class] as $handler) {
215 9
                    $event->data = $handler[1];
216 9
                    call_user_func($handler[0], $event);
217 9
                    if ($event->handled) {
218
                        return;
219
                    }
220 9
                }
221 9
            }
222 9
        }
223 9
    }
224
}
225