Teknik is a suite of services with attractive and functional interfaces. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EventDispatcher.php 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik;
  10. use Piwik\Plugin;
  11. /**
  12. * This class allows code to post events from anywhere in Piwik and for
  13. * plugins to associate callbacks to be executed when events are posted.
  14. *
  15. * @method static \Piwik\EventDispatcher getInstance()
  16. */
  17. class EventDispatcher extends Singleton
  18. {
  19. // implementation details for postEvent
  20. const EVENT_CALLBACK_GROUP_FIRST = 0;
  21. const EVENT_CALLBACK_GROUP_SECOND = 1;
  22. const EVENT_CALLBACK_GROUP_THIRD = 2;
  23. /**
  24. * Array of observers (callbacks attached to events) that are not methods
  25. * of plugin classes.
  26. *
  27. * @var array
  28. */
  29. private $extraObservers = array();
  30. /**
  31. * Array storing information for all pending events. Each item in the array
  32. * will be an array w/ two elements:
  33. *
  34. * array(
  35. * 'Event.Name', // the event name
  36. * array('event', 'parameters') // the parameters to pass to event observers
  37. * )
  38. *
  39. * @var array
  40. */
  41. private $pendingEvents = array();
  42. /**
  43. * Plugin\Manager instance used to get list of loaded plugins.
  44. *
  45. * @var Piwik\Plugin\Manager
  46. */
  47. private $pluginManager;
  48. /**
  49. * Constructor.
  50. */
  51. public function __construct($pluginManager = null)
  52. {
  53. $this->pluginManager = $pluginManager;
  54. }
  55. /**
  56. * Triggers an event, executing all callbacks associated with it.
  57. *
  58. * @param string $eventName The name of the event, ie, API.getReportMetadata.
  59. * @param array $params The parameters to pass to each callback when executing.
  60. * @param bool $pending Whether this event should be posted again for plugins
  61. * loaded after the event is fired.
  62. * @param array|null $plugins The plugins to post events to. If null, the event
  63. * is posted to all plugins. The elements of this array
  64. * can be either the Plugin objects themselves
  65. * or their string names.
  66. */
  67. public function postEvent($eventName, $params, $pending = false, $plugins = null)
  68. {
  69. if ($pending) {
  70. $this->pendingEvents[] = array($eventName, $params);
  71. }
  72. if (empty($plugins)) {
  73. $plugins = $this->getPluginManager()->getPluginsLoadedAndActivated();
  74. }
  75. $callbacks = array();
  76. // collect all callbacks to execute
  77. foreach ($plugins as $plugin) {
  78. if (is_string($plugin)) {
  79. $plugin = $this->getPluginManager()->getLoadedPlugin($plugin);
  80. }
  81. $hooks = $plugin->getListHooksRegistered();
  82. if (isset($hooks[$eventName])) {
  83. list($pluginFunction, $callbackGroup) = $this->getCallbackFunctionAndGroupNumber($hooks[$eventName]);
  84. $callbacks[$callbackGroup][] = is_string($pluginFunction) ? array($plugin, $pluginFunction) : $pluginFunction;
  85. }
  86. }
  87. if (isset($this->extraObservers[$eventName])) {
  88. foreach ($this->extraObservers[$eventName] as $callbackInfo) {
  89. list($callback, $callbackGroup) = $this->getCallbackFunctionAndGroupNumber($callbackInfo);
  90. $callbacks[$callbackGroup][] = $callback;
  91. }
  92. }
  93. // sort callbacks by their importance
  94. ksort($callbacks);
  95. // execute callbacks in order
  96. foreach ($callbacks as $callbackGroup) {
  97. foreach ($callbackGroup as $callback) {
  98. call_user_func_array($callback, $params);
  99. }
  100. }
  101. }
  102. /**
  103. * Associates a callback that is not a plugin class method with an event
  104. * name.
  105. *
  106. * @param string $eventName
  107. * @param array|callable $callback This can be a normal PHP callback or an array
  108. * that looks like this:
  109. * array(
  110. * 'function' => $callback,
  111. * 'before' => true
  112. * )
  113. * or this:
  114. * array(
  115. * 'function' => $callback,
  116. * 'after' => true
  117. * )
  118. * If 'before' is set, the callback will be executed
  119. * before normal & 'after' ones. If 'after' then it
  120. * will be executed after normal ones.
  121. */
  122. public function addObserver($eventName, $callback)
  123. {
  124. $this->extraObservers[$eventName][] = $callback;
  125. }
  126. /**
  127. * Removes all registered extra observers for an event name. Only used for testing.
  128. *
  129. * @param string $eventName
  130. */
  131. public function clearObservers($eventName)
  132. {
  133. $this->extraObservers[$eventName] = array();
  134. }
  135. /**
  136. * Removes all registered extra observers. Only used for testing.
  137. */
  138. public function clearAllObservers()
  139. {
  140. foreach ($this->extraObservers as $eventName => $eventObservers) {
  141. if (strpos($eventName, 'Log.format') === 0) {
  142. continue;
  143. }
  144. $this->extraObservers[$eventName] = array();
  145. }
  146. }
  147. /**
  148. * Re-posts all pending events to the given plugin.
  149. *
  150. * @param Plugin $plugin
  151. */
  152. public function postPendingEventsTo($plugin)
  153. {
  154. foreach ($this->pendingEvents as $eventInfo) {
  155. list($eventName, $eventParams) = $eventInfo;
  156. $this->postEvent($eventName, $eventParams, $pending = false, array($plugin));
  157. }
  158. }
  159. private function getCallbackFunctionAndGroupNumber($hookInfo)
  160. {
  161. if (is_array($hookInfo)
  162. && !empty($hookInfo['function'])
  163. ) {
  164. $pluginFunction = $hookInfo['function'];
  165. if (!empty($hookInfo['before'])) {
  166. $callbackGroup = self::EVENT_CALLBACK_GROUP_FIRST;
  167. } else if (!empty($hookInfo['after'])) {
  168. $callbackGroup = self::EVENT_CALLBACK_GROUP_THIRD;
  169. } else {
  170. $callbackGroup = self::EVENT_CALLBACK_GROUP_SECOND; }
  171. } else {
  172. $pluginFunction = $hookInfo;
  173. $callbackGroup = self::EVENT_CALLBACK_GROUP_SECOND;
  174. }
  175. return array($pluginFunction, $callbackGroup);
  176. }
  177. /**
  178. * Returns the Plugin\Manager instance used by the event dispatcher.
  179. *
  180. * @return Plugin\Manager
  181. */
  182. private function getPluginManager()
  183. {
  184. if ($this->pluginManager === null) {
  185. $this->pluginManager = Plugin\Manager::getInstance();
  186. }
  187. return $this->pluginManager;
  188. }
  189. }