/libraries/vendor/joomla/event/src/Dispatcher.php

https://github.com/pjwiseman/joomla-cms · PHP · 506 lines · 244 code · 59 blank · 203 comment · 32 complexity · 340e0edc199be64e6079d8dcd85ee4ee MD5 · raw file

  1. <?php
  2. /**
  3. * Part of the Joomla Framework Event Package
  4. *
  5. * @copyright Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
  6. * @license GNU General Public License version 2 or later; see LICENSE
  7. */
  8. namespace Joomla\Event;
  9. use InvalidArgumentException;
  10. use Closure;
  11. /**
  12. * Implementation of a DispatcherInterface supporting
  13. * prioritized listeners.
  14. *
  15. * @since 1.0
  16. */
  17. class Dispatcher implements DispatcherInterface
  18. {
  19. /**
  20. * An array of registered events indexed by
  21. * the event names.
  22. *
  23. * @var EventInterface[]
  24. *
  25. * @since 1.0
  26. */
  27. protected $events = array();
  28. /**
  29. * A regular expression that will filter listener method names.
  30. *
  31. * @var string
  32. * @since 1.0
  33. * @deprecated
  34. */
  35. protected $listenerFilter;
  36. /**
  37. * An array of ListenersPriorityQueue indexed
  38. * by the event names.
  39. *
  40. * @var ListenersPriorityQueue[]
  41. *
  42. * @since 1.0
  43. */
  44. protected $listeners = array();
  45. /**
  46. * Set an event to the dispatcher.
  47. * It will replace any event with the same name.
  48. *
  49. * @param EventInterface $event The event.
  50. *
  51. * @return Dispatcher This method is chainable.
  52. *
  53. * @since 1.0
  54. */
  55. public function setEvent(EventInterface $event)
  56. {
  57. $this->events[$event->getName()] = $event;
  58. return $this;
  59. }
  60. /**
  61. * Sets a regular expression to filter the class methods when adding a listener.
  62. *
  63. * @param string $regex A regular expression (for example '^on' will only register methods starting with "on").
  64. *
  65. * @return Dispatcher This method is chainable.
  66. *
  67. * @since 1.0
  68. * @deprecated Incorporate a method in your listener object such as `getEvents` to feed into the `setListener` method.
  69. */
  70. public function setListenerFilter($regex)
  71. {
  72. $this->listenerFilter = $regex;
  73. return $this;
  74. }
  75. /**
  76. * Add an event to this dispatcher, only if it is not existing.
  77. *
  78. * @param EventInterface $event The event.
  79. *
  80. * @return Dispatcher This method is chainable.
  81. *
  82. * @since 1.0
  83. */
  84. public function addEvent(EventInterface $event)
  85. {
  86. if (!isset($this->events[$event->getName()]))
  87. {
  88. $this->events[$event->getName()] = $event;
  89. }
  90. return $this;
  91. }
  92. /**
  93. * Tell if the given event has been added to this dispatcher.
  94. *
  95. * @param EventInterface|string $event The event object or name.
  96. *
  97. * @return boolean True if the listener has the given event, false otherwise.
  98. *
  99. * @since 1.0
  100. */
  101. public function hasEvent($event)
  102. {
  103. if ($event instanceof EventInterface)
  104. {
  105. $event = $event->getName();
  106. }
  107. return isset($this->events[$event]);
  108. }
  109. /**
  110. * Get the event object identified by the given name.
  111. *
  112. * @param string $name The event name.
  113. * @param mixed $default The default value if the event was not registered.
  114. *
  115. * @return EventInterface|mixed The event of the default value.
  116. *
  117. * @since 1.0
  118. */
  119. public function getEvent($name, $default = null)
  120. {
  121. if (isset($this->events[$name]))
  122. {
  123. return $this->events[$name];
  124. }
  125. return $default;
  126. }
  127. /**
  128. * Remove an event from this dispatcher.
  129. * The registered listeners will remain.
  130. *
  131. * @param EventInterface|string $event The event object or name.
  132. *
  133. * @return Dispatcher This method is chainable.
  134. *
  135. * @since 1.0
  136. */
  137. public function removeEvent($event)
  138. {
  139. if ($event instanceof EventInterface)
  140. {
  141. $event = $event->getName();
  142. }
  143. if (isset($this->events[$event]))
  144. {
  145. unset($this->events[$event]);
  146. }
  147. return $this;
  148. }
  149. /**
  150. * Get the registered events.
  151. *
  152. * @return EventInterface[] The registered event.
  153. *
  154. * @since 1.0
  155. */
  156. public function getEvents()
  157. {
  158. return $this->events;
  159. }
  160. /**
  161. * Clear all events.
  162. *
  163. * @return EventInterface[] The old events.
  164. *
  165. * @since 1.0
  166. */
  167. public function clearEvents()
  168. {
  169. $events = $this->events;
  170. $this->events = array();
  171. return $events;
  172. }
  173. /**
  174. * Count the number of registered event.
  175. *
  176. * @return integer The numer of registered events.
  177. *
  178. * @since 1.0
  179. */
  180. public function countEvents()
  181. {
  182. return count($this->events);
  183. }
  184. /**
  185. * Add a listener to this dispatcher, only if not already registered to these events.
  186. * If no events are specified, it will be registered to all events matching it's methods name.
  187. * In the case of a closure, you must specify at least one event name.
  188. *
  189. * @param object|Closure $listener The listener
  190. * @param array $events An associative array of event names as keys
  191. * and the corresponding listener priority as values.
  192. *
  193. * @return Dispatcher This method is chainable.
  194. *
  195. * @throws InvalidArgumentException
  196. *
  197. * @since 1.0
  198. */
  199. public function addListener($listener, array $events = array())
  200. {
  201. if (!is_object($listener))
  202. {
  203. throw new InvalidArgumentException('The given listener is not an object.');
  204. }
  205. // We deal with a closure.
  206. if ($listener instanceof Closure)
  207. {
  208. if (empty($events))
  209. {
  210. throw new InvalidArgumentException('No event name(s) and priority
  211. specified for the Closure listener.');
  212. }
  213. foreach ($events as $name => $priority)
  214. {
  215. if (!isset($this->listeners[$name]))
  216. {
  217. $this->listeners[$name] = new ListenersPriorityQueue;
  218. }
  219. $this->listeners[$name]->add($listener, $priority);
  220. }
  221. return $this;
  222. }
  223. // We deal with a "normal" object.
  224. $methods = get_class_methods($listener);
  225. if (!empty($events))
  226. {
  227. $methods = array_intersect($methods, array_keys($events));
  228. }
  229. // @deprecated
  230. $regex = $this->listenerFilter ?: '.*';
  231. foreach ($methods as $event)
  232. {
  233. // @deprecated - this outer `if` is deprecated.
  234. if (preg_match("#$regex#", $event))
  235. {
  236. // Retain this inner code after removal of the outer `if`.
  237. if (!isset($this->listeners[$event]))
  238. {
  239. $this->listeners[$event] = new ListenersPriorityQueue;
  240. }
  241. $priority = isset($events[$event]) ? $events[$event] : Priority::NORMAL;
  242. $this->listeners[$event]->add($listener, $priority);
  243. }
  244. }
  245. return $this;
  246. }
  247. /**
  248. * Get the priority of the given listener for the given event.
  249. *
  250. * @param object|Closure $listener The listener.
  251. * @param EventInterface|string $event The event object or name.
  252. *
  253. * @return mixed The listener priority or null if the listener doesn't exist.
  254. *
  255. * @since 1.0
  256. */
  257. public function getListenerPriority($listener, $event)
  258. {
  259. if ($event instanceof EventInterface)
  260. {
  261. $event = $event->getName();
  262. }
  263. if (isset($this->listeners[$event]))
  264. {
  265. return $this->listeners[$event]->getPriority($listener);
  266. }
  267. return null;
  268. }
  269. /**
  270. * Get the listeners registered to the given event.
  271. *
  272. * @param EventInterface|string $event The event object or name.
  273. *
  274. * @return object[] An array of registered listeners sorted according to their priorities.
  275. *
  276. * @since 1.0
  277. */
  278. public function getListeners($event)
  279. {
  280. if ($event instanceof EventInterface)
  281. {
  282. $event = $event->getName();
  283. }
  284. if (isset($this->listeners[$event]))
  285. {
  286. return $this->listeners[$event]->getAll();
  287. }
  288. return array();
  289. }
  290. /**
  291. * Tell if the given listener has been added.
  292. * If an event is specified, it will tell if the listener is registered for that event.
  293. *
  294. * @param object|Closure $listener The listener.
  295. * @param EventInterface|string $event The event object or name.
  296. *
  297. * @return boolean True if the listener is registered, false otherwise.
  298. *
  299. * @since 1.0
  300. */
  301. public function hasListener($listener, $event = null)
  302. {
  303. if ($event)
  304. {
  305. if ($event instanceof EventInterface)
  306. {
  307. $event = $event->getName();
  308. }
  309. if (isset($this->listeners[$event]))
  310. {
  311. return $this->listeners[$event]->has($listener);
  312. }
  313. }
  314. else
  315. {
  316. foreach ($this->listeners as $queue)
  317. {
  318. if ($queue->has($listener))
  319. {
  320. return true;
  321. }
  322. }
  323. }
  324. return false;
  325. }
  326. /**
  327. * Remove the given listener from this dispatcher.
  328. * If no event is specified, it will be removed from all events it is listening to.
  329. *
  330. * @param object|Closure $listener The listener to remove.
  331. * @param EventInterface|string $event The event object or name.
  332. *
  333. * @return Dispatcher This method is chainable.
  334. *
  335. * @since 1.0
  336. */
  337. public function removeListener($listener, $event = null)
  338. {
  339. if ($event)
  340. {
  341. if ($event instanceof EventInterface)
  342. {
  343. $event = $event->getName();
  344. }
  345. if (isset($this->listeners[$event]))
  346. {
  347. $this->listeners[$event]->remove($listener);
  348. }
  349. }
  350. else
  351. {
  352. foreach ($this->listeners as $queue)
  353. {
  354. $queue->remove($listener);
  355. }
  356. }
  357. return $this;
  358. }
  359. /**
  360. * Clear the listeners in this dispatcher.
  361. * If an event is specified, the listeners will be cleared only for that event.
  362. *
  363. * @param EventInterface|string $event The event object or name.
  364. *
  365. * @return Dispatcher This method is chainable.
  366. *
  367. * @since 1.0
  368. */
  369. public function clearListeners($event = null)
  370. {
  371. if ($event)
  372. {
  373. if ($event instanceof EventInterface)
  374. {
  375. $event = $event->getName();
  376. }
  377. if (isset($this->listeners[$event]))
  378. {
  379. unset($this->listeners[$event]);
  380. }
  381. }
  382. else
  383. {
  384. $this->listeners = array();
  385. }
  386. return $this;
  387. }
  388. /**
  389. * Count the number of registered listeners for the given event.
  390. *
  391. * @param EventInterface|string $event The event object or name.
  392. *
  393. * @return integer The number of registered listeners for the given event.
  394. *
  395. * @since 1.0
  396. */
  397. public function countListeners($event)
  398. {
  399. if ($event instanceof EventInterface)
  400. {
  401. $event = $event->getName();
  402. }
  403. return isset($this->listeners[$event]) ? count($this->listeners[$event]) : 0;
  404. }
  405. /**
  406. * Trigger an event.
  407. *
  408. * @param EventInterface|string $event The event object or name.
  409. *
  410. * @return EventInterface The event after being passed through all listeners.
  411. *
  412. * @since 1.0
  413. */
  414. public function triggerEvent($event)
  415. {
  416. if (!($event instanceof EventInterface))
  417. {
  418. if (isset($this->events[$event]))
  419. {
  420. $event = $this->events[$event];
  421. }
  422. else
  423. {
  424. $event = new Event($event);
  425. }
  426. }
  427. if (isset($this->listeners[$event->getName()]))
  428. {
  429. foreach ($this->listeners[$event->getName()] as $listener)
  430. {
  431. if ($event->isStopped())
  432. {
  433. return $event;
  434. }
  435. if ($listener instanceof Closure)
  436. {
  437. call_user_func($listener, $event);
  438. }
  439. else
  440. {
  441. call_user_func(array($listener, $event->getName()), $event);
  442. }
  443. }
  444. }
  445. return $event;
  446. }
  447. }