/src/Illuminate/Events/Dispatcher.php

https://bitbucket.org/mikebosire/framework · PHP · 257 lines · 102 code · 38 blank · 117 comment · 8 complexity · bfef6eea2a531cd96542b6ad7eef72c1 MD5 · raw file

  1. <?php namespace Illuminate\Events;
  2. use Illuminate\Container\Container;
  3. class Dispatcher {
  4. /**
  5. * The IoC container instance.
  6. *
  7. * @var \Illuminate\Container\Container
  8. */
  9. protected $container;
  10. /**
  11. * The registered event listeners.
  12. *
  13. * @var array
  14. */
  15. protected $listeners = array();
  16. /**
  17. * The sorted event listeners.
  18. *
  19. * @var array
  20. */
  21. protected $sorted = array();
  22. /**
  23. * Create a new event dispatcher instance.
  24. *
  25. * @param \Illuminate\Container\Container $container
  26. * @return void
  27. */
  28. public function __construct(Container $container = null)
  29. {
  30. $this->container = $container;
  31. }
  32. /**
  33. * Register an event listener with the dispatcher.
  34. *
  35. * @param string $event
  36. * @param mixed $listener
  37. * @param int $priority
  38. * @return void
  39. */
  40. public function listen($event, $listener, $priority = 0)
  41. {
  42. $this->listeners[$event][$priority][] = $this->makeListener($listener);
  43. unset($this->sorted[$event]);
  44. }
  45. /**
  46. * Determine if a given event has listeners.
  47. *
  48. * @param string $eventName
  49. * @return bool
  50. */
  51. public function hasListeners($eventName)
  52. {
  53. return isset($this->listeners[$eventName]);
  54. }
  55. /**
  56. * Register a queued event and payload.
  57. *
  58. * @param string $event
  59. * @param array $payload
  60. * @return void
  61. */
  62. public function queue($event, $payload = array())
  63. {
  64. $me = $this;
  65. $this->listen($event.'_queue', function() use ($me, $event, $payload)
  66. {
  67. $me->fire($event, $payload);
  68. });
  69. }
  70. /**
  71. * Register an event subscriber with the dispatcher.
  72. *
  73. * @param string $subscriber
  74. * @return void
  75. */
  76. public function subscribe($subscriber)
  77. {
  78. $subscriber = $this->resolveSubscriber($subscriber);
  79. $subscriber->subscribe($this);
  80. }
  81. /**
  82. * Resolve the subscriber instance.
  83. *
  84. * @param mixed $subscriber
  85. * @return mixed
  86. */
  87. protected function resolveSubscriber($subscriber)
  88. {
  89. if (is_string($subscriber))
  90. {
  91. return $this->container->make($subscriber);
  92. }
  93. return $subscriber;
  94. }
  95. /**
  96. * Fire an event until the first non-null response is returned.
  97. *
  98. * @param string $event
  99. * @param array $payload
  100. * @return mixed
  101. */
  102. public function until($event, $payload = array())
  103. {
  104. return $this->fire($event, $payload, true);
  105. }
  106. /**
  107. * Flush a set of queued events.
  108. *
  109. * @param string $event
  110. * @return void
  111. */
  112. public function flush($event)
  113. {
  114. $this->fire($event.'_queue');
  115. }
  116. /**
  117. * Fire an event and call the listeners.
  118. *
  119. * @param string $event
  120. * @param mixed $payload
  121. * @param boolean $halt
  122. * @return void
  123. */
  124. public function fire($event, $payload = array(), $halt = false)
  125. {
  126. $responses = array();
  127. // If an array is not given to us as the payload, we will turn it into one so
  128. // we can easily use call_user_func_array on the listeners, passing in the
  129. // payload to each of them so that they receive each of these arguments.
  130. if ( ! is_array($payload)) $payload = array($payload);
  131. foreach ($this->getListeners($event) as $listener)
  132. {
  133. $response = call_user_func_array($listener, $payload);
  134. // If a response is returned from the listener and event halting is enabled
  135. // we will just return this response, and not call the rest of the event
  136. // listeners. Otherwise we will add the response on the response list.
  137. if ( ! is_null($response) and $halt)
  138. {
  139. return $response;
  140. }
  141. // If a boolean false is returned from a listener, we will stop propogating
  142. // the event to any further listeners down in the chain, else we keep on
  143. // looping through the listeners and firing every one in our sequence.
  144. if ($response === false) break;
  145. $responses[] = $response;
  146. }
  147. return $halt ? null : $responses;
  148. }
  149. /**
  150. * Get all of the listeners for a given event name.
  151. *
  152. * @param string $eventName
  153. * @return array
  154. */
  155. public function getListeners($eventName)
  156. {
  157. if ( ! isset($this->sorted[$eventName]))
  158. {
  159. $this->sortListeners($eventName);
  160. }
  161. return $this->sorted[$eventName];
  162. }
  163. /**
  164. * Sort the listeners for a given event by priority.
  165. *
  166. * @param string $eventName
  167. * @return array
  168. */
  169. protected function sortListeners($eventName)
  170. {
  171. $this->sorted[$eventName] = array();
  172. // If listeners exist for the given event, we will sort them by the priority
  173. // so that we can call them in the correct order. We will cache off these
  174. // sorted event listeners so we do not have to re-sort on every events.
  175. if (isset($this->listeners[$eventName]))
  176. {
  177. krsort($this->listeners[$eventName]);
  178. $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
  179. }
  180. }
  181. /**
  182. * Register an event listener with the dispatcher.
  183. *
  184. * @param mixed $listener
  185. * @return void
  186. */
  187. public function makeListener($listener)
  188. {
  189. if (is_string($listener))
  190. {
  191. $listener = $this->createClassListener($listener);
  192. }
  193. return $listener;
  194. }
  195. /**
  196. * Create a class based listener using the IoC container.
  197. *
  198. * @param mixed $listener
  199. * @return Closure
  200. */
  201. public function createClassListener($listener)
  202. {
  203. $container = $this->container;
  204. return function() use ($listener, $container)
  205. {
  206. // If the listener has an @ sign, we will assume it is being used to delimit
  207. // the class name from the handle method name. This allows for handlers
  208. // to run multiple handler methods in a single class for convenience.
  209. $segments = explode('@', $listener);
  210. $method = count($segments) == 2 ? $segments[1] : 'handle';
  211. $callable = array($container->make($segments[0]), $method);
  212. // We will make a callable of the listener instance and a method that should
  213. // be called on that instance, then we will pass in the arguments that we
  214. // received in this method into this listener class instance's methods.
  215. $data = func_get_args();
  216. return call_user_func_array($callable, $data);
  217. };
  218. }
  219. }