/Event/Dispatcher.php

https://github.com/Kazuzeya/OpenFlame-Framework · PHP · 214 lines · 118 code · 24 blank · 72 comment · 11 complexity · 9a9f67688e2cd3273b75a7b7991802c8 MD5 · raw file

  1. <?php
  2. /**
  3. *
  4. * @package openflame-framework
  5. * @subpackage event
  6. * @copyright (c) 2010 - 2011 openflame-project.org
  7. * @license http://opensource.org/licenses/mit-license.php The MIT License
  8. * @link https://github.com/OpenFlame/OpenFlame-Framework
  9. *
  10. * Minimum Requirement: PHP 5.3.0
  11. */
  12. namespace OpenFlame\Framework\Event;
  13. use OpenFlame\Framework\Core;
  14. /**
  15. * OpenFlame Framework - Event dispatcher object
  16. * Provides event dispatcher functionality for ease of extensibility.
  17. *
  18. *
  19. * @license http://opensource.org/licenses/mit-license.php The MIT License
  20. * @link https://github.com/OpenFlame/OpenFlame-Framework
  21. */
  22. class Dispatcher
  23. {
  24. /**
  25. * @var array - Our array of stored listeners and any extra data.
  26. */
  27. protected $listeners = array();
  28. /**
  29. * @var array - Array of "unsorted" listeners
  30. */
  31. protected $unsorted = array();
  32. /**#@+
  33. * @var integer - Constants representing the type of listener being interacted with.
  34. */
  35. const LISTENER_CLOSURE = 1;
  36. const LISTENER_FUNCTION = 2;
  37. const LISTENER_STATIC_METHOD = 3;
  38. const LISTENER_CALL_USER_FUNC = 4;
  39. /**#@-*/
  40. /**#@+
  41. * @var integer - Constants representing the type of trigger mechanism to use.
  42. */
  43. const TRIGGER_NOBREAK = 1;
  44. const TRIGGER_MANUALBREAK = 2;
  45. const TRIGGER_RETURNBREAK = 3;
  46. /**#@-*/
  47. /**
  48. * Register a new listener with the dispatcher
  49. * @param string $event_name - The name of the event to attach the listener to.
  50. * @param integer $priority - The priority for the listener to be registered as, similar to *nix "nice" values for CPU processes (-20 top priority, 20 bottom priority)
  51. * @param callable $listener - The callable reference for the listener.
  52. * @return \OpenFlame\Framework\Event\Dispatcher - Provides a fluent interface.
  53. */
  54. public function register($event_name, $priority, $listener)
  55. {
  56. if(!isset($this->listeners[$event_name]) || !is_array($this->listeners[$event_name]))
  57. {
  58. $this->listeners[$event_name] = array();
  59. }
  60. // Handle priority settings (a la UNIX nice values)
  61. $priority = (int) $priority;
  62. if($priority > 20)
  63. {
  64. $priority = 20;
  65. }
  66. elseif($priority < -20)
  67. {
  68. $priority = -20;
  69. }
  70. // Check to see what type of listener we're dealing with here; this allows us to use some shortcuts down the road.
  71. $listener_type = NULL;
  72. if($listener instanceof \Closure)
  73. {
  74. // It's a closure! <3
  75. $listener_type = self::LISTENER_CLOSURE;
  76. }
  77. elseif(function_exists($listener))
  78. {
  79. $listener_type = self::LISTENER_FUNCTION;
  80. }
  81. elseif(is_string($listener) && sizeof(explode('::', $listener, 2)) > 1) // checking to see if we're actually using a static call and doing so properly
  82. {
  83. $listener = explode('::', $listener, 2);
  84. $listener_type = self::LISTENER_STATIC_METHOD;
  85. }
  86. else
  87. {
  88. // Worst case scenario. We HAVE to use call_user_func() now.
  89. $listener_type = self::LISTENER_CALL_USER_FUNC;
  90. }
  91. $this->listeners[$event_name][$priority][] = array(
  92. 'listener' => $listener,
  93. 'type' => $listener_type,
  94. );
  95. // Flag this event as needing a sort before the next event dispatch
  96. $this->unsorted[$event_name] = true;
  97. return $this;
  98. }
  99. /**
  100. * Check to see if an event has any listeners registered to it
  101. * @param string $event_type - The type of event to check.
  102. * @return boolean - Does the event have listeners attached?
  103. */
  104. public function hasListeners($event_type)
  105. {
  106. return !empty($this->listeners[$event_type]);
  107. }
  108. /**
  109. * Dispatch an event to registered listeners
  110. * @param \OpenFlame\Framework\Event\Instance $event - The event to dispatch.
  111. * @param integer $dispatch_type - The type of dispatch method to use (run all listeners, allow listeners to trigger break, break on a non-NULL return value)
  112. * @return \OpenFlame\Framework\Event\Instance - The event dispatched.
  113. */
  114. public function trigger(\OpenFlame\Framework\Event\Instance $event, $dispatch_type = self::TRIGGER_NOBREAK)
  115. {
  116. $event_name = $event->getName();
  117. // Check to see if this event has ANY listeners - if it doesn't, just bail out.
  118. if(!$this->hasListeners($event_name))
  119. {
  120. return $event;
  121. }
  122. // On-the-fly priority sorting
  123. if(isset($this->unsorted[$event_name]))
  124. {
  125. ksort($this->listeners[$event_name]);
  126. unset($this->unsorted[$event_name]);
  127. }
  128. foreach($this->listeners[$event_name] as $priority => $priority_thread)
  129. {
  130. foreach($priority_thread as $listener_entry)
  131. {
  132. $listener = $listener_entry['listener'];
  133. $listener_type = $listener_entry['type'];
  134. // Use faster, quicker methods than call_user_func() for triggering listeners if they're available
  135. switch($listener_type)
  136. {
  137. case self::LISTENER_CLOSURE:
  138. case self::LISTENER_FUNCTION:
  139. $return = $listener($event);
  140. break;
  141. case self::LISTENER_STATIC_METHOD:
  142. list($class, $method) = $listener;
  143. $return = $class::$method($event);
  144. break;
  145. case self::LISTENER_CALL_USER_FUNC:
  146. default:
  147. $return = call_user_func($listener, $event);
  148. break;
  149. }
  150. if($return !== NULL)
  151. {
  152. $event->setReturn($return);
  153. if($dispatch_type = self::TRIGGER_RETURNBREAK)
  154. {
  155. return $event; // PHP 5.4 compat -- cannot use "break (int)" anymore, so we just return the $event
  156. }
  157. }
  158. if($dispatch_type = self::TRIGGER_MANUALBREAK && $event->wasBreakTriggered())
  159. {
  160. return $event; // PHP 5.4 compat -- cannot use "break (int)" anymore, so we just return the $event
  161. }
  162. }
  163. }
  164. return $event;
  165. }
  166. /**
  167. * Dispatch an event to registered listeners, and checking to see if a listener wants to abort (and if so, break)
  168. * @param \OpenFlame\Framework\Event\Instance $event - The event to dispatch.
  169. * @return \OpenFlame\Framework\Event\Instance - The event dispatched.
  170. *
  171. * @deprecated since 1.2.0
  172. */
  173. public function triggerUntilBreak(\OpenFlame\Framework\Event\Instance $event)
  174. {
  175. trigger_error('\\OpenFlame\\Framework\\Event\\Dispatcher->triggerUntilBreak() is deprecated', E_USER_DEPRECATED);
  176. return $this->trigger($event, self::TRIGGER_MANUALBREAK);
  177. }
  178. /**
  179. * Dispatch an event to registered listeners, and checking to see if a listener returned a value yet or not (and if so, break)
  180. * @param \OpenFlame\Framework\Event\Instance $event - The event to dispatch.
  181. * @return \OpenFlame\Framework\Event\Instance - The event dispatched.
  182. *
  183. * @deprecated since 1.2.0
  184. */
  185. public function triggerUntilReturn(\OpenFlame\Framework\Event\Instance $event)
  186. {
  187. trigger_error('\\OpenFlame\\Framework\\Event\\Dispatcher->triggerUntilReturn() is deprecated', E_USER_DEPRECATED);
  188. return $this->trigger($event, self::TRIGGER_RETURNBREAK);
  189. }
  190. }