1 <?php
2 /**
3 * Part of the Joomla Framework Event Package
4 *
5 * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
6 * @license GNU General Public License version 2 or later; see LICENSE
7 */
8
9 namespace Joomla\Event;
10
11 use SplPriorityQueue;
12 use SplObjectStorage;
13 use IteratorAggregate;
14 use Countable;
15
16 /**
17 * A class containing an inner listeners priority queue that can be iterated multiple times.
18 * One instance of ListenersPriorityQueue is used per Event in the Dispatcher.
19 *
20 * @since 1.0
21 */
22 class ListenersPriorityQueue implements IteratorAggregate, Countable
23 {
24 /**
25 * The inner priority queue.
26 *
27 * @var SplPriorityQueue
28 *
29 * @since 1.0
30 */
31 protected $queue;
32
33 /**
34 * A copy of the listeners contained in the queue
35 * that is used when detaching them to
36 * recreate the queue or to see if the queue contains
37 * a given listener.
38 *
39 * @var SplObjectStorage
40 *
41 * @since 1.0
42 */
43 protected $storage;
44
45 /**
46 * A decreasing counter used to compute
47 * the internal priority as an array because
48 * SplPriorityQueue dequeues elements with the same priority.
49 *
50 * @var integer
51 *
52 * @since 1.0
53 */
54 private $counter = PHP_INT_MAX;
55
56 /**
57 * Constructor.
58 *
59 * @since 1.0
60 */
61 public function __construct()
62 {
63 $this->queue = new SplPriorityQueue;
64 $this->storage = new SplObjectStorage;
65 }
66
67 /**
68 * Add a listener with the given priority only if not already present.
69 *
70 * @param \Closure|object $listener The listener.
71 * @param integer $priority The listener priority.
72 *
73 * @return ListenersPriorityQueue This method is chainable.
74 *
75 * @since 1.0
76 */
77 public function add($listener, $priority)
78 {
79 if (!$this->storage->contains($listener))
80 {
81 // Compute the internal priority as an array.
82 $priority = array($priority, $this->counter--);
83
84 $this->storage->attach($listener, $priority);
85 $this->queue->insert($listener, $priority);
86 }
87
88 return $this;
89 }
90
91 /**
92 * Remove a listener from the queue.
93 *
94 * @param \Closure|object $listener The listener.
95 *
96 * @return ListenersPriorityQueue This method is chainable.
97 *
98 * @since 1.0
99 */
100 public function remove($listener)
101 {
102 if ($this->storage->contains($listener))
103 {
104 $this->storage->detach($listener);
105 $this->storage->rewind();
106
107 $this->queue = new SplPriorityQueue;
108
109 foreach ($this->storage as $listener)
110 {
111 $priority = $this->storage->getInfo();
112 $this->queue->insert($listener, $priority);
113 }
114 }
115
116 return $this;
117 }
118
119 /**
120 * Tell if the listener exists in the queue.
121 *
122 * @param \Closure|object $listener The listener.
123 *
124 * @return boolean True if it exists, false otherwise.
125 *
126 * @since 1.0
127 */
128 public function has($listener)
129 {
130 return $this->storage->contains($listener);
131 }
132
133 /**
134 * Get the priority of the given listener.
135 *
136 * @param \Closure|object $listener The listener.
137 * @param mixed $default The default value to return if the listener doesn't exist.
138 *
139 * @return mixed The listener priority if it exists, null otherwise.
140 *
141 * @since 1.0
142 */
143 public function getPriority($listener, $default = null)
144 {
145 if ($this->storage->contains($listener))
146 {
147 return $this->storage[$listener][0];
148 }
149
150 return $default;
151 }
152
153 /**
154 * Get all listeners contained in this queue, sorted according to their priority.
155 *
156 * @return object[] An array of listeners.
157 *
158 * @since 1.0
159 */
160 public function getAll()
161 {
162 $listeners = array();
163
164 // Get a clone of the queue.
165 $queue = $this->getIterator();
166
167 foreach ($queue as $listener)
168 {
169 $listeners[] = $listener;
170 }
171
172 return $listeners;
173 }
174
175 /**
176 * Get the inner queue with its cursor on top of the heap.
177 *
178 * @return SplPriorityQueue The inner queue.
179 *
180 * @since 1.0
181 */
182 public function getIterator()
183 {
184 // SplPriorityQueue queue is a heap.
185 $queue = clone $this->queue;
186
187 if (!$queue->isEmpty())
188 {
189 $queue->top();
190 }
191
192 return $queue;
193 }
194
195 /**
196 * Count the number of listeners in the queue.
197 *
198 * @return integer The number of listeners in the queue.
199 *
200 * @since 1.0
201 */
202 public function count()
203 {
204 return count($this->queue);
205 }
206 }
207