Base for a static organization website

CakeEventManager.php 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @package Cake.Event
  13. * @since CakePHP(tm) v 2.1
  14. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  15. */
  16. App::uses('CakeEventListener', 'Event');
  17. /**
  18. * The event manager is responsible for keeping track of event listeners, passing the correct
  19. * data to them, and firing them in the correct order, when associated events are triggered. You
  20. * can create multiple instances of this object to manage local events or keep a single instance
  21. * and pass it around to manage all events in your app.
  22. *
  23. * @package Cake.Event
  24. */
  25. class CakeEventManager {
  26. /**
  27. * The default priority queue value for new, attached listeners
  28. *
  29. * @var int
  30. */
  31. public static $defaultPriority = 10;
  32. /**
  33. * The globally available instance, used for dispatching events attached from any scope
  34. *
  35. * @var CakeEventManager
  36. */
  37. protected static $_generalManager = null;
  38. /**
  39. * List of listener callbacks associated to
  40. *
  41. * @var object
  42. */
  43. protected $_listeners = array();
  44. /**
  45. * Internal flag to distinguish a common manager from the singleton
  46. *
  47. * @var bool
  48. */
  49. protected $_isGlobal = false;
  50. /**
  51. * Returns the globally available instance of a CakeEventManager
  52. * this is used for dispatching events attached from outside the scope
  53. * other managers were created. Usually for creating hook systems or inter-class
  54. * communication
  55. *
  56. * If called with the first parameter, it will be set as the globally available instance
  57. *
  58. * @param CakeEventManager $manager Optional event manager instance.
  59. * @return CakeEventManager the global event manager
  60. */
  61. public static function instance($manager = null) {
  62. if ($manager instanceof CakeEventManager) {
  63. static::$_generalManager = $manager;
  64. }
  65. if (empty(static::$_generalManager)) {
  66. static::$_generalManager = new CakeEventManager();
  67. }
  68. static::$_generalManager->_isGlobal = true;
  69. return static::$_generalManager;
  70. }
  71. /**
  72. * Adds a new listener to an event. Listeners
  73. *
  74. * @param callback|CakeEventListener $callable PHP valid callback type or instance of CakeEventListener to be called
  75. * when the event named with $eventKey is triggered. If a CakeEventListener instance is passed, then the `implementedEvents`
  76. * method will be called on the object to register the declared events individually as methods to be managed by this class.
  77. * It is possible to define multiple event handlers per event name.
  78. *
  79. * @param string $eventKey The event unique identifier name with which the callback will be associated. If $callable
  80. * is an instance of CakeEventListener this argument will be ignored
  81. *
  82. * @param array $options used to set the `priority` and `passParams` flags to the listener.
  83. * Priorities are handled like queues, and multiple attachments added to the same priority queue will be treated in
  84. * the order of insertion. `passParams` means that the event data property will be converted to function arguments
  85. * when the listener is called. If $called is an instance of CakeEventListener, this parameter will be ignored
  86. *
  87. * @return void
  88. * @throws InvalidArgumentException When event key is missing or callable is not an
  89. * instance of CakeEventListener.
  90. */
  91. public function attach($callable, $eventKey = null, $options = array()) {
  92. if (!$eventKey && !($callable instanceof CakeEventListener)) {
  93. throw new InvalidArgumentException(__d('cake_dev', 'The eventKey variable is required'));
  94. }
  95. if ($callable instanceof CakeEventListener) {
  96. $this->_attachSubscriber($callable);
  97. return;
  98. }
  99. $options = $options + array('priority' => static::$defaultPriority, 'passParams' => false);
  100. $this->_listeners[$eventKey][$options['priority']][] = array(
  101. 'callable' => $callable,
  102. 'passParams' => $options['passParams'],
  103. );
  104. }
  105. /**
  106. * Auxiliary function to attach all implemented callbacks of a CakeEventListener class instance
  107. * as individual methods on this manager
  108. *
  109. * @param CakeEventListener $subscriber Event listener.
  110. * @return void
  111. */
  112. protected function _attachSubscriber(CakeEventListener $subscriber) {
  113. foreach ((array)$subscriber->implementedEvents() as $eventKey => $function) {
  114. $options = array();
  115. $method = $function;
  116. if (is_array($function) && isset($function['callable'])) {
  117. list($method, $options) = $this->_extractCallable($function, $subscriber);
  118. } elseif (is_array($function) && is_numeric(key($function))) {
  119. foreach ($function as $f) {
  120. list($method, $options) = $this->_extractCallable($f, $subscriber);
  121. $this->attach($method, $eventKey, $options);
  122. }
  123. continue;
  124. }
  125. if (is_string($method)) {
  126. $method = array($subscriber, $function);
  127. }
  128. $this->attach($method, $eventKey, $options);
  129. }
  130. }
  131. /**
  132. * Auxiliary function to extract and return a PHP callback type out of the callable definition
  133. * from the return value of the `implementedEvents` method on a CakeEventListener
  134. *
  135. * @param array $function the array taken from a handler definition for an event
  136. * @param CakeEventListener $object The handler object
  137. * @return callback
  138. */
  139. protected function _extractCallable($function, $object) {
  140. $method = $function['callable'];
  141. $options = $function;
  142. unset($options['callable']);
  143. if (is_string($method)) {
  144. $method = array($object, $method);
  145. }
  146. return array($method, $options);
  147. }
  148. /**
  149. * Removes a listener from the active listeners.
  150. *
  151. * @param callback|CakeEventListener $callable any valid PHP callback type or an instance of CakeEventListener
  152. * @param string $eventKey The event unique identifier name with which the callback has been associated
  153. * @return void
  154. */
  155. public function detach($callable, $eventKey = null) {
  156. if ($callable instanceof CakeEventListener) {
  157. return $this->_detachSubscriber($callable, $eventKey);
  158. }
  159. if (empty($eventKey)) {
  160. foreach (array_keys($this->_listeners) as $eventKey) {
  161. $this->detach($callable, $eventKey);
  162. }
  163. return;
  164. }
  165. if (empty($this->_listeners[$eventKey])) {
  166. return;
  167. }
  168. foreach ($this->_listeners[$eventKey] as $priority => $callables) {
  169. foreach ($callables as $k => $callback) {
  170. if ($callback['callable'] === $callable) {
  171. unset($this->_listeners[$eventKey][$priority][$k]);
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. /**
  178. * Auxiliary function to help detach all listeners provided by an object implementing CakeEventListener
  179. *
  180. * @param CakeEventListener $subscriber the subscriber to be detached
  181. * @param string $eventKey optional event key name to unsubscribe the listener from
  182. * @return void
  183. */
  184. protected function _detachSubscriber(CakeEventListener $subscriber, $eventKey = null) {
  185. $events = (array)$subscriber->implementedEvents();
  186. if (!empty($eventKey) && empty($events[$eventKey])) {
  187. return;
  188. } elseif (!empty($eventKey)) {
  189. $events = array($eventKey => $events[$eventKey]);
  190. }
  191. foreach ($events as $key => $function) {
  192. if (is_array($function)) {
  193. if (is_numeric(key($function))) {
  194. foreach ($function as $handler) {
  195. $handler = isset($handler['callable']) ? $handler['callable'] : $handler;
  196. $this->detach(array($subscriber, $handler), $key);
  197. }
  198. continue;
  199. }
  200. $function = $function['callable'];
  201. }
  202. $this->detach(array($subscriber, $function), $key);
  203. }
  204. }
  205. /**
  206. * Dispatches a new event to all configured listeners
  207. *
  208. * @param string|CakeEvent $event the event key name or instance of CakeEvent
  209. * @return CakeEvent
  210. * @triggers $event
  211. */
  212. public function dispatch($event) {
  213. if (is_string($event)) {
  214. $event = new CakeEvent($event);
  215. }
  216. $listeners = $this->listeners($event->name());
  217. if (empty($listeners)) {
  218. return $event;
  219. }
  220. foreach ($listeners as $listener) {
  221. if ($event->isStopped()) {
  222. break;
  223. }
  224. if ($listener['passParams'] === true) {
  225. $result = call_user_func_array($listener['callable'], $event->data);
  226. } else {
  227. $result = call_user_func($listener['callable'], $event);
  228. }
  229. if ($result === false) {
  230. $event->stopPropagation();
  231. }
  232. if ($result !== null) {
  233. $event->result = $result;
  234. }
  235. }
  236. return $event;
  237. }
  238. /**
  239. * Returns a list of all listeners for an eventKey in the order they should be called
  240. *
  241. * @param string $eventKey Event key.
  242. * @return array
  243. */
  244. public function listeners($eventKey) {
  245. $localListeners = array();
  246. $priorities = array();
  247. if (!$this->_isGlobal) {
  248. $localListeners = $this->prioritisedListeners($eventKey);
  249. $localListeners = empty($localListeners) ? array() : $localListeners;
  250. }
  251. $globalListeners = static::instance()->prioritisedListeners($eventKey);
  252. $globalListeners = empty($globalListeners) ? array() : $globalListeners;
  253. $priorities = array_merge(array_keys($globalListeners), array_keys($localListeners));
  254. $priorities = array_unique($priorities);
  255. asort($priorities);
  256. $result = array();
  257. foreach ($priorities as $priority) {
  258. if (isset($globalListeners[$priority])) {
  259. $result = array_merge($result, $globalListeners[$priority]);
  260. }
  261. if (isset($localListeners[$priority])) {
  262. $result = array_merge($result, $localListeners[$priority]);
  263. }
  264. }
  265. return $result;
  266. }
  267. /**
  268. * Returns the listeners for the specified event key indexed by priority
  269. *
  270. * @param string $eventKey Event key.
  271. * @return array
  272. */
  273. public function prioritisedListeners($eventKey) {
  274. if (empty($this->_listeners[$eventKey])) {
  275. return array();
  276. }
  277. return $this->_listeners[$eventKey];
  278. }
  279. }