Merge pull request #13854 from eileenmcnaughton/lock
[civicrm-core.git] / Civi / Core / CiviEventDispatcher.php
1 <?php
2
3 namespace Civi\Core;
4
5 use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
6 use Symfony\Component\EventDispatcher\Event;
7
8 /**
9 * Class CiviEventDispatcher
10 * @package Civi\Core
11 *
12 * The CiviEventDispatcher is a Symfony dispatcher. Additionally, if an event
13 * follows the naming convention of "hook_*", then it will also be dispatched
14 * through CRM_Utils_Hook::invoke().
15 *
16 * @see \CRM_Utils_Hook
17 */
18 class CiviEventDispatcher extends ContainerAwareEventDispatcher {
19
20 const DEFAULT_HOOK_PRIORITY = -100;
21
22 /**
23 * Track the list of hook-events for which we have autoregistered
24 * the hook adapter.
25 *
26 * @var array
27 * Array(string $eventName => trueish).
28 */
29 private $autoListeners = [];
30
31 /**
32 * Determine whether $eventName should delegate to the CMS hook system.
33 *
34 * @param string $eventName
35 * Ex: 'civi.token.eval', 'hook_civicrm_post`.
36 * @return bool
37 */
38 protected function isHookEvent($eventName) {
39 return (substr($eventName, 0, 5) === 'hook_') && (strpos($eventName, '::') === FALSE);
40 }
41
42 /**
43 * @inheritDoc
44 */
45 public function dispatch($eventName, Event $event = NULL) {
46 $this->bindPatterns($eventName);
47 return parent::dispatch($eventName, $event);
48 }
49
50 /**
51 * @inheritDoc
52 */
53 public function getListeners($eventName = NULL) {
54 $this->bindPatterns($eventName);
55 return parent::getListeners($eventName);
56 }
57
58 /**
59 * @inheritDoc
60 */
61 public function hasListeners($eventName = NULL) {
62 // All hook_* events have default listeners, so hasListeners(NULL) is a truism.
63 return ($eventName === NULL || $this->isHookEvent($eventName))
64 ? TRUE : parent::hasListeners($eventName);
65 }
66
67 /**
68 * Invoke hooks using an event object.
69 *
70 * @param \Civi\Core\Event\GenericHookEvent $event
71 * @param string $eventName
72 * Ex: 'hook_civicrm_dashboard'.
73 */
74 public static function delegateToUF($event, $eventName) {
75 $hookName = substr($eventName, 5);
76 $hooks = \CRM_Utils_Hook::singleton();
77 $params = $event->getHookValues();
78 $count = count($params);
79
80 switch ($count) {
81 case 0:
82 $fResult = $hooks->invokeViaUF($count, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName);
83 break;
84
85 case 1:
86 $fResult = $hooks->invokeViaUF($count, $params[0], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName);
87 break;
88
89 case 2:
90 $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName);
91 break;
92
93 case 3:
94 $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName);
95 break;
96
97 case 4:
98 $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], $params[3], \CRM_Utils_Hook::$_nullObject, \CRM_Utils_Hook::$_nullObject, $hookName);
99 break;
100
101 case 5:
102 $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], $params[3], $params[4], \CRM_Utils_Hook::$_nullObject, $hookName);
103 break;
104
105 case 6:
106 $fResult = $hooks->invokeViaUF($count, $params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $hookName);
107 break;
108
109 default:
110 throw new \RuntimeException("hook_{$hookName} cannot support more than 6 parameters");
111 }
112
113 $event->addReturnValues($fResult);
114 }
115
116 /**
117 * @param string $eventName
118 * Ex: 'civi.api.resolve' or 'hook_civicrm_dashboard'.
119 */
120 protected function bindPatterns($eventName) {
121 if ($eventName !== NULL && !isset($this->autoListeners[$eventName])) {
122 $this->autoListeners[$eventName] = 1;
123 if ($this->isHookEvent($eventName)) {
124 // WISHLIST: For native extensions (and possibly D6/D7/D8/BD), enumerate
125 // the listeners and list them one-by-one. This would make it easier to
126 // inspect via "cv debug:event-dispatcher".
127 $this->addListener($eventName, [
128 '\Civi\Core\CiviEventDispatcher',
129 'delegateToUF',
130 ], self::DEFAULT_HOOK_PRIORITY);
131 }
132 }
133 }
134
135 }