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