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

namespace yii\base;

/**
Qiang Xue committed
11
 * Event is the base class for all event classes.
Qiang Xue committed
12 13
 *
 * It encapsulates the parameters associated with an event.
w  
Qiang Xue committed
14 15 16
 * The [[sender]] property describes who raises the event.
 * And the [[handled]] property indicates if the event is handled.
 * If an event handler sets [[handled]] to be true, the rest of the
m  
Qiang Xue committed
17
 * uninvoked handlers will no longer be called to handle the event.
Qiang Xue committed
18 19 20
 *
 * Additionally, when attaching an event handler, extra data may be passed
 * and be available via the [[data]] property when the event handler is invoked.
Qiang Xue committed
21 22
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
23
 * @since 2.0
Qiang Xue committed
24
 */
Qiang Xue committed
25
class Event extends Object
Qiang Xue committed
26
{
w  
Qiang Xue committed
27
	/**
Carsten Brandt committed
28
	 * @var string the event name. This property is set by [[Component::trigger()]] and [[trigger()]].
w  
Qiang Xue committed
29 30 31
	 * Event handlers may use this property to check what event it is handling.
	 */
	public $name;
Qiang Xue committed
32
	/**
Qiang Xue committed
33 34
	 * @var object the sender of this event. If not set, this property will be
	 * set as the object whose "trigger()" method is called.
Carsten Brandt committed
35 36
	 * This property may also be a `null` when this event is a
	 * class-level event which is triggered in a static context.
Qiang Xue committed
37 38 39 40
	 */
	public $sender;
	/**
	 * @var boolean whether the event is handled. Defaults to false.
w  
Qiang Xue committed
41 42
	 * When a handler sets this to be true, the event processing will stop and
	 * ignore the rest of the uninvoked event handlers.
Qiang Xue committed
43
	 */
Qiang Xue committed
44
	public $handled = false;
m  
Qiang Xue committed
45
	/**
Qiang Xue committed
46 47
	 * @var mixed the data that is passed to [[Component::on()]] when attaching an event handler.
	 * Note that this varies according to which event handler is currently executing.
m  
Qiang Xue committed
48
	 */
Qiang Xue committed
49
	public $data;
50 51 52 53 54 55 56 57 58 59 60 61 62

	private static $_events = [];

	/**
	 * Attaches an event handler to a class-level event.
	 *
	 * When a class-level event is triggered, event handlers attached
	 * to that class and all parent classes will be invoked.
	 *
	 * For example, the following code attaches an event handler to `ActiveRecord`'s
	 * `afterInsert` event:
	 *
	 * ~~~
Carsten Brandt committed
63
	 * Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
64 65 66 67 68 69 70 71
	 *     Yii::trace(get_class($event->sender) . ' is inserted.');
	 * });
	 * ~~~
	 *
	 * The handler will be invoked for EVERY successful ActiveRecord insertion.
	 *
	 * For more details about how to declare an event handler, please refer to [[Component::on()]].
	 *
72 73 74
	 * @param string $class the fully qualified class name to which the event handler needs to attach.
	 * @param string $name the event name.
	 * @param callback $handler the event handler.
75 76 77 78 79 80 81 82 83 84 85 86 87 88
	 * @param mixed $data the data to be passed to the event handler when the event is triggered.
	 * When the event handler is invoked, this data can be accessed via [[Event::data]].
	 * @see off()
	 */
	public static function on($class, $name, $handler, $data = null)
	{
		self::$_events[$name][ltrim($class, '\\')][] = [$handler, $data];
	}

	/**
	 * Detaches an event handler from a class-level event.
	 *
	 * This method is the opposite of [[on()]].
	 *
89 90
	 * @param string $class the fully qualified class name from which the event handler needs to be detached.
	 * @param string $name the event name.
91 92
	 * @param callback $handler the event handler to be removed.
	 * If it is null, all handlers attached to the named event will be removed.
Carsten Brandt committed
93
	 * @return boolean whether a handler is found and detached.
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
	 * @see on()
	 */
	public static function off($class, $name, $handler = null)
	{
		$class = ltrim($class, '\\');
		if (empty(self::$_events[$name][$class])) {
			return false;
		}
		if ($handler === null) {
			unset(self::$_events[$name][$class]);
			return true;
		} else {
			$removed = false;
			foreach (self::$_events[$name][$class] as $i => $event) {
				if ($event[0] === $handler) {
					unset(self::$_events[$name][$class][$i]);
					$removed = true;
				}
			}
			if ($removed) {
				self::$_events[$name][$class] = array_values(self::$_events[$name][$class]);
			}
			return $removed;
		}
	}

	/**
	 * Returns a value indicating whether there is any handler attached to the specified class-level event.
	 * Note that this method will also check all parent classes to see if there is any handler attached
	 * to the named event.
124 125
	 * @param string|object $class the object or the fully qualified class name specifying the class-level event.
	 * @param string $name the event name.
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	 * @return boolean whether there is any handler attached to the event.
	 */
	public static function hasHandlers($class, $name)
	{
		if (empty(self::$_events[$name])) {
			return false;
		}
		if (is_object($class)) {
			$class = get_class($class);
		} else {
			$class = ltrim($class, '\\');
		}
		do {
			if (!empty(self::$_events[$name][$class])) {
				return true;
			}
		} while (($class = get_parent_class($class)) !== false);
		return false;
	}

	/**
	 * Triggers a class-level event.
	 * This method will cause invocation of event handlers that are attached to the named event
	 * for the specified class and all its parent classes.
150 151
	 * @param string|object $class the object or the fully qualified class name specifying the class-level event.
	 * @param string $name the event name.
152 153 154 155 156 157 158 159
	 * @param Event $event the event parameter. If not set, a default [[Event]] object will be created.
	 */
	public static function trigger($class, $name, $event = null)
	{
		if (empty(self::$_events[$name])) {
			return;
		}
		if ($event === null) {
160
			$event = new static;
161 162 163 164 165
		}
		$event->handled = false;
		$event->name = $name;

		if (is_object($class)) {
166 167 168
			if ($event->sender === null) {
				$event->sender = $class;
			}
169 170 171 172 173 174 175 176 177
			$class = get_class($class);
		} else {
			$class = ltrim($class, '\\');
		}
		do {
			if (!empty(self::$_events[$name][$class])) {
				foreach (self::$_events[$name][$class] as $handler) {
					$event->data = $handler[1];
					call_user_func($handler[0], $event);
178
					if ($event->handled) {
179 180 181 182 183 184
						return;
					}
				}
			}
		} while (($class = get_parent_class($class)) !== false);
	}
Qiang Xue committed
185
}