5 * Class CiviEventInspector
7 * The event inspector is a development tool which provides metadata about events.
8 * It can be used for code-generators and documentation-generators.
11 * $i = new CiviEventInspector();
12 * print_r(CRM_Utils_Array::collect('name', $i->getAll()));
15 * An event definition includes these fields:
16 * - type: string, required. Ex: 'hook' or 'object'
17 * - name: string, required. Ex: 'hook_civicrm_post' or 'civi.dao.postInsert'
18 * - class: string, required. Ex: 'Civi\Core\Event\GenericHookEvent'.
19 * - signature: string, required FOR HOOKS. Ex: '$first, &$second'.
20 * - fields: array, required FOR HOOKS. List of hook parameters.
21 * - stub: ReflectionMethod, optional. An example function with docblocks/inputs.
23 * Note: The inspector is only designed for use in developer workflows, such
24 * as code-generation and inspection. It should be not called by regular
27 class CiviEventInspector
{
30 * Register the default hooks defined by 'CRM_Utils_Hook'.
32 * @param \Civi\Core\Event\GenericHookEvent $e
33 * @see \CRM_Utils_Hook::eventDefs()
35 public static function findBuiltInEvents(\Civi\Core\Event\GenericHookEvent
$e) {
36 $skipList = ['singleton'];
37 $e->inspector
->addStaticStubs('CRM_Utils_Hook', 'hook_civicrm_',
38 function ($eventDef, $method) use ($skipList) {
39 return in_array($method->name
, $skipList) ?
NULL : $eventDef;
45 * Array(string $name => array $eventDef).
47 * Ex: $eventDefs['hook_civicrm_foo']['description_html'] = 'Hello world';
52 * Perform a scan to identify/describe all events.
55 * @return CiviEventInspector
57 public function build($force = FALSE) {
58 if ($force ||
$this->eventDefs
=== NULL) {
59 $this->eventDefs
= [];
60 \CRM_Utils_Hook
::eventDefs($this);
61 ksort($this->eventDefs
);
67 * Get a list of all events.
70 * Array(string $name => array $eventDef).
71 * Ex: $result['hook_civicrm_foo']['description_html'] = 'Hello world';
73 public function getAll() {
75 return $this->eventDefs
;
79 * Find any events that match a pattern.
81 * @param string $regex
83 * Array(string $name => array $eventDef).
84 * Ex: $result['hook_civicrm_foo']['description_html'] = 'Hello world';
86 public function find($regex) {
88 return array_filter($this->eventDefs
, function ($e) use ($regex) {
89 return preg_match($regex, $e['name']);
94 * Get the definition of one event.
97 * Ex: 'hook_civicrm_alterSettingsMetaData'.
99 * Ex: $result['description_html'] = 'Hello world';
101 public function get($name) {
103 return $this->eventDefs
[$name];
111 public function validate($eventDef) {
112 if (!is_array($eventDef) ||
empty($eventDef['name']) ||
!isset($eventDef['type'])) {
116 if (!in_array($eventDef['type'], ['hook', 'object'])) {
120 if ($eventDef['type'] === 'hook') {
121 if (!isset($eventDef['signature']) ||
!is_array($eventDef['fields'])) {
130 * Add a new event definition.
132 * @param array $eventDef
133 * @return CiviEventInspector
135 public function add($eventDef) {
136 $name = $eventDef['name'] ??
NULL;
138 if (!isset($eventDef['type'])) {
139 $eventDef['type'] = preg_match('/^hook_/', $eventDef['name']) ?
'hook' : 'object';
142 if ($eventDef['type'] === 'hook' && empty($eventDef['signature'])) {
143 $eventDef['signature'] = implode(', ', array_map(
145 $sigil = $field['ref'] ?
'&$' : '$';
146 return $sigil . $field['name'];
152 if (TRUE !== $this->validate($eventDef)) {
153 throw new \
CRM_Core_Exception("Failed to register event ($name). Invalid definition.");
156 $this->eventDefs
[$name] = $eventDef;
161 * Scan a Symfony event class for metadata, and add it.
163 * @param string $event
164 * Ex: 'civi.api.authorize'.
165 * @param string $className
166 * Ex: 'Civi\API\Event\AuthorizeEvent'.
167 * @return CiviEventInspector
169 public function addEventClass($event, $className) {
172 'class' => $className,
178 * Scan a class for hook stubs, and add all of them.
180 * @param string $className
181 * The name of a class which contains static stub functions.
182 * Ex: 'CRM_Utils_Hook'.
183 * @param string $prefix
184 * A prefix to apply to all hook names.
185 * Ex: 'hook_civicrm_'.
186 * @param null|callable $filter
187 * An optional function to filter/rewrite the metadata for each hook.
188 * @return CiviEventInspector
190 public function addStaticStubs($className, $prefix, $filter = NULL) {
191 $class = new \
ReflectionClass($className);
193 foreach ($class->getMethods(\ReflectionMethod
::IS_STATIC
) as $method) {
194 if (!isset($method->name
)) {
199 'name' => $prefix . $method->name
,
200 'description_html' => $method->getDocComment() ? \CRM_Admin_Page_APIExplorer
::formatDocBlock($method->getDocComment()) : '',
202 'class' => 'Civi\Core\Event\GenericHookEvent',
206 foreach ($method->getParameters() as $parameter) {
207 $eventDef['fields'][$parameter->getName()] = [
208 'name' => $parameter->getName(),
209 'ref' => (bool) $parameter->isPassedByReference(),
210 // WISHLIST: 'type' => 'mixed',
214 if ($filter !== NULL) {
215 $eventDef = $filter($eventDef, $method);
216 if ($eventDef === NULL) {
221 $this->add($eventDef);