3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
28 namespace Civi\Core\Event
;
31 * Class GenericHookEvent
32 * @package Civi\API\Event
34 * The GenericHookEvent is used to expose all traditional hooks to the
35 * Symfony EventDispatcher.
37 * The traditional notation for a hook is based on a function signature:
39 * function hook_civicrm_foo($bar, &$whiz, &$bang);
41 * The notation for Symfony Events is based on a class with properties
42 * and methods. This requires some kind of mapping. `GenericHookEvent`
43 * maps each parameter to a field (using magic methods):
46 * // Creating an event object.
47 * $event = GenericHookEvent::create(array(
53 * // Accessing event properties.
55 * $event->whiz['array_field'] = 123;
56 * $event->bang->objProperty = 'abcd';
58 * // Dispatching an event.
59 * Civi::service('dispatcher')->dispatch('hook_civicrm_foo', $event);
64 * 1. Implementing new event classes for every hook would produce a
65 * large amount of boilerplate. Symfony Events have an interesting solution to
66 * that problem: use `GenericEvent` instead of custom event classes.
67 * `GenericHookEvent` is conceptually similar to `GenericEvent`, but it adds
68 * support for (a) altering properties and (b) mapping properties to hook notation
69 * (an ordered parameter list).
71 * 2. A handful of hooks define a return-value. The return-value is treated
72 * as an array, and all the returned values are merged into one big array.
73 * You can add and retrieve return-values using these methods:
76 * $event->addReturnValues(array(...));
77 * foreach ($event->getReturnValues() as $retVal) { ... }
80 class GenericHookEvent
extends \Symfony\Component\EventDispatcher\Event
{
84 * Ex: array(0 => &$contactID, 1 => &$contentPlacement).
86 protected $hookValues;
90 * Ex: array(0 => 'contactID', 1 => 'contentPlacement').
92 protected $hookFields;
96 * Ex: array('contactID' => 0, 'contentPlacement' => 1).
98 protected $hookFieldsFlip;
101 * Some legacy hooks expect listener-functions to return a value.
102 * OOP listeners may set the $returnValue.
104 * This field is not recommended for use in new hooks. The return-value
105 * convention is not portable across different implementations of the hook
106 * system. Instead, it's more portable to provide an alterable, named field.
111 private $returnValues = array();
114 * List of field names that are prohibited due to conflicts
115 * in the class-hierarchy.
119 private static $BLACKLIST = array(
122 'propagationStopped',
130 * Create a GenericHookEvent using key-value pairs.
132 * @param array $params
133 * Ex: array('contactID' => &$contactID, 'contentPlacement' => &$contentPlacement).
134 * @return \Civi\Core\Event\GenericHookEvent
136 public static function create($params) {
138 $e->hookValues
= array_values($params);
139 $e->hookFields
= array_keys($params);
140 $e->hookFieldsFlip
= array_flip($e->hookFields
);
141 self
::assertValidHookFields($e->hookFields
);
146 * Create a GenericHookEvent using ordered parameters.
148 * @param array $hookFields
149 * Ex: array(0 => 'contactID', 1 => 'contentPlacement').
150 * @param array $hookValues
151 * Ex: array(0 => &$contactID, 1 => &$contentPlacement).
152 * @return \Civi\Core\Event\GenericHookEvent
154 public static function createOrdered($hookFields, $hookValues) {
156 if (count($hookValues) > count($hookFields)) {
157 $hookValues = array_slice($hookValues, 0, count($hookFields));
159 $e->hookValues
= $hookValues;
160 $e->hookFields
= $hookFields;
161 $e->hookFieldsFlip
= array_flip($e->hookFields
);
162 self
::assertValidHookFields($e->hookFields
);
167 * @param array $fields
168 * List of field names.
170 private static function assertValidHookFields($fields) {
171 $bad = array_intersect($fields, self
::$BLACKLIST);
173 throw new \
RuntimeException("Hook relies on conflicted field names: "
174 . implode(', ', $bad));
180 * Ex: array(0 => &$contactID, 1 => &$contentPlacement).
182 public function getHookValues() {
183 return $this->hookValues
;
190 public function getReturnValues() {
191 return empty($this->returnValues
) ?
TRUE : $this->returnValues
;
195 * @param mixed $fResult
196 * @return GenericHookEvent
199 public function addReturnValues($fResult) {
200 if (!empty($fResult) && is_array($fResult)) {
201 $this->returnValues
= array_merge($this->returnValues
, $fResult);
209 public function &__get($name) {
210 if (isset($this->hookFieldsFlip
[$name])) {
211 return $this->hookValues
[$this->hookFieldsFlip
[$name]];
218 public function __set($name, $value) {
219 if (isset($this->hookFieldsFlip
[$name])) {
220 $this->hookValues
[$this->hookFieldsFlip
[$name]] = $value;
227 public function __isset($name) {
228 return isset($this->hookFieldsFlip
[$name])
229 && isset($this->hookValues
[$this->hookFieldsFlip
[$name]]);
235 public function __unset($name) {
236 if (isset($this->hookFieldsFlip
[$name])) {
237 // Unset while preserving order.
238 $this->hookValues
[$this->hookFieldsFlip
[$name]] = NULL;
243 * Determine whether the hook supports the given field.
245 * The field may or may not be empty. Use isset() or empty() to
248 * @param string $name
251 public function hasField($name) {
252 return isset($this->hookFieldsFlip
[$name]);