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