PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/application/libraries/Engine/Hooks/Dispatcher.php

https://github.com/shopaholiccompany/shopaholic
PHP | 326 lines | 178 code | 39 blank | 109 comment | 34 complexity | d41359069a893e65c2a960a8567de973 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. * SocialEngine
  4. *
  5. * @category Engine
  6. * @package Engine_Hooks
  7. * @copyright Copyright 2006-2010 Webligo Developments
  8. * @license http://www.socialengine.net/license/
  9. * @version $Id: Dispatcher.php 7244 2010-09-01 01:49:53Z john $
  10. */
  11. /**
  12. * @category Engine
  13. * @package Engine_Hooks
  14. * @copyright Copyright 2006-2010 Webligo Developments
  15. * @license http://www.socialengine.net/license/
  16. */
  17. class Engine_Hooks_Dispatcher
  18. {
  19. // Constants
  20. const TYPE_CALLBACK = 'callback';
  21. const TYPE_RESOURCE = 'resource';
  22. /**
  23. * Stores the current singleton instance
  24. *
  25. * @var Core_Model_Hooks_Dispatcher
  26. */
  27. protected static $_instance;
  28. /**
  29. * Array of events that have registered callbacks
  30. *
  31. * @var array
  32. */
  33. protected $_events = array();
  34. /**
  35. *
  36. * @var array
  37. */
  38. protected $_plugins = array();
  39. /**
  40. * Array of events that need sorting
  41. *
  42. * @var array
  43. */
  44. protected $_needSorts = array();
  45. /**
  46. * Default priority for hooks
  47. *
  48. * @var integer
  49. */
  50. protected $_defaultPriority = 500;
  51. /**
  52. * Sets when plugins/callbacks are validated.
  53. * 0 - register
  54. * 1 - call
  55. *
  56. * @var integer
  57. */
  58. protected $_sanityMode = 1;
  59. /**
  60. * Gets the current singleton instance
  61. *
  62. * @return Engine_Hooks_Dispatcher
  63. */
  64. public static function getInstance()
  65. {
  66. if( null === self::$_instance )
  67. {
  68. self::$_instance = new self();
  69. }
  70. return self::$_instance;
  71. }
  72. /**
  73. * Shorthand for {@link Engine_Hooks_Dispatcher::getInstance()}
  74. *
  75. * @return Engine_Hooks_Dispatcher
  76. */
  77. public static function _()
  78. {
  79. return self::getInstance();
  80. }
  81. /**
  82. * Sets (or if param is not specified, resets) the current singleton instance
  83. *
  84. * @param Core_Model_Hooks_Dispatcher $instance - OPTIONAL
  85. * @return Core_Model_Hooks_Dispatcher
  86. */
  87. public static function setInstance(Engine_Hooks_Dispatcher $instance = null)
  88. {
  89. if( null === $instance )
  90. {
  91. $instance = new self();
  92. }
  93. self::$_instance = $instance;
  94. return self::$_instance;
  95. }
  96. // Registration
  97. /**
  98. * Register a callback/resource to an event
  99. *
  100. * @param string $event The event name
  101. * @param array $params (OPTIONAL)
  102. * @return Engine_Hooks_Dispatcher
  103. */
  104. public function addEvent($event, $params = null)
  105. {
  106. if( null === $params )
  107. {
  108. $params = $event;
  109. $event = @$params['event'];
  110. }
  111. if( empty($event) )
  112. {
  113. throw new Engine_Hooks_Exception('No event? Really?');
  114. }
  115. if( is_object($params) && method_exists($params, 'toArray') )
  116. {
  117. $params = $params->toArray();
  118. }
  119. if( !is_array($params) )
  120. {
  121. throw new Engine_Hooks_Exception('Array or array-type object must be passed to register');
  122. }
  123. if( empty($params['callback']) == empty($params['resource']) )
  124. {
  125. throw new Engine_Hooks_Exception('Callback or resource must be defined, not neither or both');
  126. }
  127. if( !isset($params['priority']) )
  128. {
  129. $params['priority'] = $this->_defaultPriority;
  130. }
  131. if( !empty($params['resource']) && is_array($params['resource']) )
  132. {
  133. $params['resource'] = str_replace(' ', '_', ucwords(join(' ', $params['resource'])));
  134. }
  135. $this->_isCallable($params, $event, $this->_sanityMode);
  136. $name = $this->_getName($params);
  137. $this->_needSorts[$event] = true;
  138. $this->_events[$event][$name] = $params['priority'];
  139. $this->_plugins[$name] = $params;
  140. return $this;
  141. }
  142. /**
  143. * Register an array of events. Formats:
  144. * event => params, OR
  145. * array('event' => event, params)
  146. *
  147. * @param array $events Array of events
  148. * @return Engine_Hooks_Dispatcher
  149. */
  150. public function addEvents($events)
  151. {
  152. foreach( $events as $event => $params )
  153. {
  154. if( is_numeric($event) )
  155. {
  156. $this->addEvent($params);
  157. }
  158. else
  159. {
  160. $this->addEvent($event, $params);
  161. }
  162. }
  163. return $this;
  164. }
  165. /**
  166. * Trigger an event
  167. *
  168. * @param string $event The event to trigger
  169. * @param mixed $payload Arbitrary payload data
  170. * @return Engine_Hooks_Event
  171. */
  172. public function callEvent($event, $payload = null)
  173. {
  174. // Sort
  175. $this->_sort($event);
  176. // Create event object, if necessary
  177. if( !($payload instanceof Engine_Hooks_Event) )
  178. {
  179. $payload = new Engine_Hooks_Event($event, $payload);
  180. }
  181. // Ignore if no plugin
  182. if( empty($this->_events[$event]) )
  183. {
  184. return $payload;
  185. }
  186. // Send
  187. foreach( $this->_events[$event] as $name => $priority )
  188. {
  189. $params = $this->_plugins[$name];
  190. $this->_isCallable($params, $event, (bool) $this->_sanityMode);
  191. if( !empty($params['callback']) )
  192. {
  193. call_user_func($params['callback'], $payload);
  194. }
  195. else if( !empty($params['resource']) )
  196. {
  197. Engine_Api::_()->loadClass($params['resource'])->$event($payload);
  198. }
  199. }
  200. // Return the event object so we can get the responses
  201. return $payload;
  202. }
  203. /**
  204. * Check if a hook registered to an event is callable
  205. *
  206. * @param array $params
  207. * @param string $event The event name (need for resources)
  208. * @param boolean $syntaxOnly Will check syntax only
  209. */
  210. protected function _isCallable($params, $event, $syntaxOnly = false)
  211. {
  212. if( !empty($params['callback']) )
  213. {
  214. if( !is_callable($params['callback'], $syntaxOnly) )
  215. {
  216. throw new Engine_Hooks_Exception('Callback is not callable');
  217. }
  218. }
  219. else if( !empty($params['resource']) )
  220. {
  221. if( !is_string($params['resource']) )
  222. {
  223. throw new Engine_Hooks_Exception('Resource must be a string class name');
  224. }
  225. if( !$syntaxOnly && !class_exists($params['resource'] /*, false */) && !method_exists($params['resource'], $event) )
  226. {
  227. throw new Engine_Hooks_Exception('Resource is not callable');
  228. }
  229. }
  230. else
  231. {
  232. throw new Engine_Hooks_Exception('No callback or resource specified to verify if callable');
  233. }
  234. }
  235. /**
  236. * Get a unique name based on params
  237. *
  238. * @param array $params
  239. * @return string
  240. */
  241. protected function _getName($params)
  242. {
  243. if( !empty($params['callback']) )
  244. {
  245. if( is_array($params['callback']) )
  246. {
  247. return 'a-' . join('-', $params['callback']);
  248. }
  249. else if( is_scalar($params['callback']) )
  250. {
  251. return 's-' . $params['callback'];
  252. }
  253. else
  254. {
  255. throw new Engine_Hooks_Exception('Callback must be scalar or array');
  256. }
  257. }
  258. else if( !empty($params['resource']) )
  259. {
  260. if( is_string($params['resource']) )
  261. {
  262. return 'r-' . $params['resource'];
  263. }
  264. else
  265. {
  266. throw new Engine_Hooks_Exception('Resource must be a string');
  267. }
  268. }
  269. else
  270. {
  271. throw new Engine_Hooks_Exception('No callback or resource specified');
  272. }
  273. }
  274. /**
  275. * Sort an event
  276. *
  277. * @param string $event
  278. */
  279. protected function _sort($event)
  280. {
  281. if( !empty($this->_needSorts[$event]) )
  282. {
  283. asort($this->_events[$event]);
  284. unset($this->_needSorts[$event]);
  285. }
  286. }
  287. }