1<?php 2 3namespace Sabre\Event; 4 5/** 6 * Event Emitter Trait 7 * 8 * This trait contains all the basic functions to implement an 9 * EventEmitterInterface. 10 * 11 * Using the trait + interface allows you to add EventEmitter capabilities 12 * without having to change your base-class. 13 * 14 * @copyright Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/). 15 * @author Evert Pot (http://evertpot.com/) 16 * @license http://sabre.io/license/ Modified BSD License 17 */ 18trait EventEmitterTrait { 19 20 /** 21 * The list of listeners 22 * 23 * @var array 24 */ 25 protected $listeners = []; 26 27 /** 28 * Subscribe to an event. 29 * 30 * @param string $eventName 31 * @param callable $callBack 32 * @param int $priority 33 * @return void 34 */ 35 function on($eventName, callable $callBack, $priority = 100) { 36 37 if (!isset($this->listeners[$eventName])) { 38 $this->listeners[$eventName] = [ 39 true, // If there's only one item, it's sorted 40 [$priority], 41 [$callBack] 42 ]; 43 } else { 44 $this->listeners[$eventName][0] = false; // marked as unsorted 45 $this->listeners[$eventName][1][] = $priority; 46 $this->listeners[$eventName][2][] = $callBack; 47 } 48 49 } 50 51 /** 52 * Subscribe to an event exactly once. 53 * 54 * @param string $eventName 55 * @param callable $callBack 56 * @param int $priority 57 * @return void 58 */ 59 function once($eventName, callable $callBack, $priority = 100) { 60 61 $wrapper = null; 62 $wrapper = function() use ($eventName, $callBack, &$wrapper) { 63 64 $this->removeListener($eventName, $wrapper); 65 return call_user_func_array($callBack, func_get_args()); 66 67 }; 68 69 $this->on($eventName, $wrapper, $priority); 70 71 } 72 73 /** 74 * Emits an event. 75 * 76 * This method will return true if 0 or more listeners were succesfully 77 * handled. false is returned if one of the events broke the event chain. 78 * 79 * If the continueCallBack is specified, this callback will be called every 80 * time before the next event handler is called. 81 * 82 * If the continueCallback returns false, event propagation stops. This 83 * allows you to use the eventEmitter as a means for listeners to implement 84 * functionality in your application, and break the event loop as soon as 85 * some condition is fulfilled. 86 * 87 * Note that returning false from an event subscriber breaks propagation 88 * and returns false, but if the continue-callback stops propagation, this 89 * is still considered a 'successful' operation and returns true. 90 * 91 * Lastly, if there are 5 event handlers for an event. The continueCallback 92 * will be called at most 4 times. 93 * 94 * @param string $eventName 95 * @param array $arguments 96 * @param callback $continueCallBack 97 * @return bool 98 */ 99 function emit($eventName, array $arguments = [], callable $continueCallBack = null) { 100 101 if (is_null($continueCallBack)) { 102 103 foreach ($this->listeners($eventName) as $listener) { 104 105 $result = call_user_func_array($listener, $arguments); 106 if ($result === false) { 107 return false; 108 } 109 } 110 111 } else { 112 113 $listeners = $this->listeners($eventName); 114 $counter = count($listeners); 115 116 foreach ($listeners as $listener) { 117 118 $counter--; 119 $result = call_user_func_array($listener, $arguments); 120 if ($result === false) { 121 return false; 122 } 123 124 if ($counter > 0) { 125 if (!$continueCallBack()) break; 126 } 127 128 } 129 130 } 131 132 return true; 133 134 } 135 136 /** 137 * Returns the list of listeners for an event. 138 * 139 * The list is returned as an array, and the list of events are sorted by 140 * their priority. 141 * 142 * @param string $eventName 143 * @return callable[] 144 */ 145 function listeners($eventName) { 146 147 if (!isset($this->listeners[$eventName])) { 148 return []; 149 } 150 151 // The list is not sorted 152 if (!$this->listeners[$eventName][0]) { 153 154 // Sorting 155 array_multisort($this->listeners[$eventName][1], SORT_NUMERIC, $this->listeners[$eventName][2]); 156 157 // Marking the listeners as sorted 158 $this->listeners[$eventName][0] = true; 159 } 160 161 return $this->listeners[$eventName][2]; 162 163 } 164 165 /** 166 * Removes a specific listener from an event. 167 * 168 * If the listener could not be found, this method will return false. If it 169 * was removed it will return true. 170 * 171 * @param string $eventName 172 * @param callable $listener 173 * @return bool 174 */ 175 function removeListener($eventName, callable $listener) { 176 177 if (!isset($this->listeners[$eventName])) { 178 return false; 179 } 180 foreach ($this->listeners[$eventName][2] as $index => $check) { 181 if ($check === $listener) { 182 unset($this->listeners[$eventName][1][$index]); 183 unset($this->listeners[$eventName][2][$index]); 184 return true; 185 } 186 } 187 return false; 188 189 } 190 191 /** 192 * Removes all listeners. 193 * 194 * If the eventName argument is specified, all listeners for that event are 195 * removed. If it is not specified, every listener for every event is 196 * removed. 197 * 198 * @param string $eventName 199 * @return void 200 */ 201 function removeAllListeners($eventName = null) { 202 203 if (!is_null($eventName)) { 204 unset($this->listeners[$eventName]); 205 } else { 206 $this->listeners = []; 207 } 208 209 } 210 211} 212