xref: /plugin/davcal/vendor/sabre/event/lib/EventEmitterTrait.php (revision a1a3b6794e0e143a4a8b51d3185ce2d339be61ab)
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