CRM-20238 - Create hook for inbound SMS
[civicrm-core.git] / CRM / Utils / Hook.php
index 889302950eb9fa502df1f337cecc79e77dbf8ced..e6e130f6a88c155926c6f705a3223a14ed1c467e 100644 (file)
@@ -89,6 +89,9 @@ abstract class CRM_Utils_Hook {
     return self::$_singleton;
   }
 
+  /**
+   * CRM_Utils_Hook constructor.
+   */
   public function __construct() {
     $this->cache = CRM_Utils_Cache::create(array(
       'name' => 'hooks',
@@ -98,7 +101,8 @@ abstract class CRM_Utils_Hook {
   }
 
   /**
-   * Invoke hooks.
+   * Invoke a hook through the UF/CMS hook system and the extension-hook
+   * system.
    *
    * @param int $numParams
    *   Number of parameters to pass to the hook.
@@ -119,12 +123,53 @@ abstract class CRM_Utils_Hook {
    *
    * @return mixed
    */
-  public abstract function invoke(
+  public abstract function invokeViaUF(
     $numParams,
     &$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6,
     $fnSuffix
   );
 
+  /**
+   * Invoke a hook.
+   *
+   * This is a transitional adapter. It supports the legacy syntax
+   * but also accepts enough information to support Symfony Event
+   * dispatching.
+   *
+   * @param array|int $names
+   *   (Recommended) Array of parameter names, in order.
+   *   Using an array is recommended because it enables full
+   *   event-broadcasting behaviors.
+   *   (Legacy) Number of parameters to pass to the hook.
+   *   This is provided for transitional purposes.
+   * @param mixed $arg1
+   * @param mixed $arg2
+   * @param mixed $arg3
+   * @param mixed $arg4
+   * @param mixed $arg5
+   * @param mixed $arg6
+   * @param mixed $fnSuffix
+   * @return mixed
+   */
+  public function invoke(
+    $names,
+    &$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6,
+    $fnSuffix
+  ) {
+    if (is_array($names) && !defined('CIVICRM_FORCE_LEGACY_HOOK') && \Civi\Core\Container::isContainerBooted()) {
+      $event = \Civi\Core\Event\GenericHookEvent::createOrdered(
+        $names,
+        array(&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6)
+      );
+      \Civi::dispatcher()->dispatch('hook_' . $fnSuffix, $event);
+      return $event->getReturnValues();
+    }
+    else {
+      $count = is_array($names) ? count($names) : $names;
+      return $this->invokeViaUF($count, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $fnSuffix);
+    }
+  }
+
   /**
    * @param array $numParams
    * @param $arg1
@@ -295,10 +340,8 @@ abstract class CRM_Utils_Hook {
    */
   public static function pre($op, $objectName, $id, &$params) {
     $event = new \Civi\Core\Event\PreEvent($op, $objectName, $id, $params);
-    \Civi::service('dispatcher')->dispatch("hook_civicrm_pre", $event);
-    \Civi::service('dispatcher')->dispatch("hook_civicrm_pre::$objectName", $event);
-    return self::singleton()
-      ->invoke(4, $op, $objectName, $id, $params, self::$_nullObject, self::$_nullObject, 'civicrm_pre');
+    \Civi::dispatcher()->dispatch('hook_civicrm_pre', $event);
+    return $event->getReturnValues();
   }
 
   /**
@@ -319,10 +362,8 @@ abstract class CRM_Utils_Hook {
    */
   public static function post($op, $objectName, $objectId, &$objectRef = NULL) {
     $event = new \Civi\Core\Event\PostEvent($op, $objectName, $objectId, $objectRef);
-    \Civi::service('dispatcher')->dispatch("hook_civicrm_post", $event);
-    \Civi::service('dispatcher')->dispatch("hook_civicrm_post::$objectName", $event);
-    return self::singleton()
-      ->invoke(4, $op, $objectName, $objectId, $objectRef, self::$_nullObject, self::$_nullObject, 'civicrm_post');
+    \Civi::dispatcher()->dispatch('hook_civicrm_post', $event);
+    return $event->getReturnValues();
   }
 
   /**
@@ -346,7 +387,7 @@ abstract class CRM_Utils_Hook {
    *   the return value is ignored
    */
   public static function links($op, $objectName, &$objectId, &$links, &$mask = NULL, &$values = array()) {
-    return self::singleton()->invoke(6, $op, $objectName, $objectId, $links, $mask, $values, 'civicrm_links');
+    return self::singleton()->invoke(array('op', 'objectName', 'objectId', 'links', 'mask', 'values'), $op, $objectName, $objectId, $links, $mask, $values, 'civicrm_links');
   }
 
   /**
@@ -362,7 +403,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function preProcess($formName, &$form) {
     return self::singleton()
-      ->invoke(2, $formName, $form, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_preProcess');
+      ->invoke(array('formName', 'form'), $formName, $form, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_preProcess');
   }
 
   /**
@@ -378,7 +419,7 @@ abstract class CRM_Utils_Hook {
    *   the return value is ignored
    */
   public static function buildForm($formName, &$form) {
-    return self::singleton()->invoke(2, $formName, $form,
+    return self::singleton()->invoke(array('formName', 'form'), $formName, $form,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_buildForm'
     );
@@ -397,7 +438,7 @@ abstract class CRM_Utils_Hook {
    *   the return value is ignored
    */
   public static function postProcess($formName, &$form) {
-    return self::singleton()->invoke(2, $formName, $form,
+    return self::singleton()->invoke(array('formName', 'form'), $formName, $form,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_postProcess'
     );
@@ -420,7 +461,8 @@ abstract class CRM_Utils_Hook {
    */
   public static function validateForm($formName, &$fields, &$files, &$form, &$errors) {
     return self::singleton()
-      ->invoke(5, $formName, $fields, $files, $form, $errors, self::$_nullObject, 'civicrm_validateForm');
+      ->invoke(array('formName', 'fields', 'files', 'form', 'errors'),
+        $formName, $fields, $files, $form, $errors, self::$_nullObject, 'civicrm_validateForm');
   }
 
   /**
@@ -440,7 +482,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function custom($op, $groupID, $entityID, &$params) {
     return self::singleton()
-      ->invoke(4, $op, $groupID, $entityID, $params, self::$_nullObject, self::$_nullObject, 'civicrm_custom');
+      ->invoke(array('op', 'groupID', 'entityID', 'params'), $op, $groupID, $entityID, $params, self::$_nullObject, self::$_nullObject, 'civicrm_custom');
   }
 
   /**
@@ -463,7 +505,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function aclWhereClause($type, &$tables, &$whereTables, &$contactID, &$where) {
     return self::singleton()
-      ->invoke(5, $type, $tables, $whereTables, $contactID, $where, self::$_nullObject, 'civicrm_aclWhereClause');
+      ->invoke(array('type', 'tables', 'whereTables', 'contactID', 'where'), $type, $tables, $whereTables, $contactID, $where, self::$_nullObject, 'civicrm_aclWhereClause');
   }
 
   /**
@@ -486,7 +528,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function aclGroup($type, $contactID, $tableName, &$allGroups, &$currentGroups) {
     return self::singleton()
-      ->invoke(5, $type, $contactID, $tableName, $allGroups, $currentGroups, self::$_nullObject, 'civicrm_aclGroup');
+      ->invoke(array('type', 'contactID', 'tableName', 'allGroups', 'currentGroups'), $type, $contactID, $tableName, $allGroups, $currentGroups, self::$_nullObject, 'civicrm_aclGroup');
   }
 
   /**
@@ -496,7 +538,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function selectWhereClause($entity, &$clauses) {
     $entityName = is_object($entity) ? _civicrm_api_get_entity_name_from_dao($entity) : $entity;
-    return self::singleton()->invoke(2, $entityName, $clauses,
+    return self::singleton()->invoke(array('entity', 'clauses'), $entityName, $clauses,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_selectWhereClause'
     );
@@ -512,7 +554,7 @@ abstract class CRM_Utils_Hook {
    *   the return value is ignored
    */
   public static function xmlMenu(&$files) {
-    return self::singleton()->invoke(1, $files,
+    return self::singleton()->invoke(array('files'), $files,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_xmlMenu'
     );
@@ -527,7 +569,7 @@ abstract class CRM_Utils_Hook {
    *   the return value is ignored
    */
   public static function alterMenu(&$items) {
-    return self::singleton()->invoke(1, $items,
+    return self::singleton()->invoke(array('items'), $items,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterMenu'
     );
@@ -554,7 +596,7 @@ abstract class CRM_Utils_Hook {
    *   the return value is ignored
    */
   public static function managed(&$entities) {
-    return self::singleton()->invoke(1, $entities,
+    return self::singleton()->invoke(array('entities'), $entities,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_managed'
     );
@@ -573,7 +615,7 @@ abstract class CRM_Utils_Hook {
    *   the html snippet to include in the dashboard
    */
   public static function dashboard($contactID, &$contentPlacement = self::DASHBOARD_BELOW) {
-    $retval = self::singleton()->invoke(2, $contactID, $contentPlacement,
+    $retval = self::singleton()->invoke(array('contactID', 'contentPlacement'), $contactID, $contentPlacement,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_dashboard'
     );
@@ -599,7 +641,7 @@ abstract class CRM_Utils_Hook {
    * @return array
    */
   public static function recent(&$recentArray) {
-    return self::singleton()->invoke(1, $recentArray,
+    return self::singleton()->invoke(array('recentArray'), $recentArray,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_recent'
     );
@@ -620,7 +662,7 @@ abstract class CRM_Utils_Hook {
    *   Return is not really intended to be used.
    */
   public static function referenceCounts($dao, &$refCounts) {
-    return self::singleton()->invoke(2, $dao, $refCounts,
+    return self::singleton()->invoke(array('dao', 'refCounts'), $dao, $refCounts,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_referenceCounts'
     );
@@ -639,7 +681,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function buildAmount($pageType, &$form, &$amount) {
-    return self::singleton()->invoke(3, $pageType, $form, $amount, self::$_nullObject,
+    return self::singleton()->invoke(array('pageType', 'form', 'amount'), $pageType, $form, $amount, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, 'civicrm_buildAmount');
   }
 
@@ -653,7 +695,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function buildStateProvinceForCountry($countryID, &$states) {
-    return self::singleton()->invoke(2, $countryID, $states,
+    return self::singleton()->invoke(array('countryID', 'states'), $countryID, $states,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_buildStateProvinceForCountry'
     );
@@ -671,7 +713,7 @@ abstract class CRM_Utils_Hook {
    * @deprecated Use tabset() instead.
    */
   public static function tabs(&$tabs, $contactID) {
-    return self::singleton()->invoke(2, $tabs, $contactID,
+    return self::singleton()->invoke(array('tabs', 'contactID'), $tabs, $contactID,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_tabs'
     );
   }
@@ -690,7 +732,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function tabset($tabsetName, &$tabs, $context) {
-    return self::singleton()->invoke(3, $tabsetName, $tabs,
+    return self::singleton()->invoke(array('tabsetName', 'tabs', 'context'), $tabsetName, $tabs,
       $context, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_tabset'
     );
   }
@@ -704,7 +746,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function tokens(&$tokens) {
-    return self::singleton()->invoke(1, $tokens,
+    return self::singleton()->invoke(array('tokens'), $tokens,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_tokens'
     );
   }
@@ -734,7 +776,7 @@ abstract class CRM_Utils_Hook {
     $className = NULL
   ) {
     return self::singleton()
-      ->invoke(5, $details, $contactIDs, $jobID, $tokens, $className, self::$_nullObject, 'civicrm_tokenValues');
+      ->invoke(array('details', 'contactIDs', 'jobID', 'tokens', 'className'), $details, $contactIDs, $jobID, $tokens, $className, self::$_nullObject, 'civicrm_tokenValues');
   }
 
   /**
@@ -747,7 +789,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function pageRun(&$page) {
-    return self::singleton()->invoke(1, $page,
+    return self::singleton()->invoke(array('page'), $page,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_pageRun'
     );
@@ -765,7 +807,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function copy($objectName, &$object) {
-    return self::singleton()->invoke(2, $objectName, $object,
+    return self::singleton()->invoke(array('objectName', 'object'), $objectName, $object,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_copy'
     );
@@ -791,7 +833,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function unsubscribeGroups($op, $mailingId, $contactId, &$groups, &$baseGroups) {
     return self::singleton()
-      ->invoke(5, $op, $mailingId, $contactId, $groups, $baseGroups, self::$_nullObject, 'civicrm_unsubscribeGroups');
+      ->invoke(array('op', 'mailingId', 'contactId', 'groups', 'baseGroups'), $op, $mailingId, $contactId, $groups, $baseGroups, self::$_nullObject, 'civicrm_unsubscribeGroups');
   }
 
   /**
@@ -815,7 +857,8 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function customFieldOptions($customFieldID, &$options, $detailedFormat = FALSE, $selectAttributes = array()) {
-    return self::singleton()->invoke(3, $customFieldID, $options, $detailedFormat,
+    // Weird: $selectAttributes is inputted but not outputted.
+    return self::singleton()->invoke(array('customFieldID', 'options', 'detailedFormat'), $customFieldID, $options, $detailedFormat,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_customFieldOptions'
     );
@@ -832,7 +875,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function fieldOptions($entity, $field, &$options, $params) {
-    return self::singleton()->invoke(5, $entity, $field, $options, $params,
+    return self::singleton()->invoke(array('entity', 'field', 'options', 'params'), $entity, $field, $options, $params,
       self::$_nullObject, self::$_nullObject,
       'civicrm_fieldOptions'
     );
@@ -858,7 +901,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function searchTasks($objectType, &$tasks) {
-    return self::singleton()->invoke(2, $objectType, $tasks,
+    return self::singleton()->invoke(array('objectType', 'tasks'), $objectType, $tasks,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_searchTasks'
     );
@@ -871,7 +914,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function eventDiscount(&$form, &$params) {
-    return self::singleton()->invoke(2, $form, $params,
+    return self::singleton()->invoke(array('form', 'params'), $form, $params,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_eventDiscount'
     );
@@ -890,7 +933,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function mailingGroups(&$form, &$groups, &$mailings) {
-    return self::singleton()->invoke(3, $form, $groups, $mailings,
+    return self::singleton()->invoke(array('form', 'groups', 'mailings'), $form, $groups, $mailings,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_mailingGroups'
     );
@@ -907,7 +950,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function mailingTemplateTypes(&$types) {
-    return self::singleton()->invoke(1, $types, self::$_nullObject, self::$_nullObject,
+    return self::singleton()->invoke(array('types'), $types, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_mailingTemplateTypes'
     );
@@ -928,7 +971,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function membershipTypeValues(&$form, &$membershipTypes) {
-    return self::singleton()->invoke(2, $form, $membershipTypes,
+    return self::singleton()->invoke(array('form', 'membershipTypes'), $form, $membershipTypes,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_membershipTypeValues'
     );
@@ -948,7 +991,7 @@ abstract class CRM_Utils_Hook {
    *   The html snippet to include in the contact summary
    */
   public static function summary($contactID, &$content, &$contentPlacement = self::SUMMARY_BELOW) {
-    return self::singleton()->invoke(3, $contactID, $content, $contentPlacement,
+    return self::singleton()->invoke(array('contactID', 'content', 'contentPlacement'), $contactID, $content, $contentPlacement,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_summary'
     );
@@ -968,7 +1011,7 @@ abstract class CRM_Utils_Hook {
    *   Your query must return two columns:
    *     the contact 'data' to display in the autocomplete dropdown (usually contact.sort_name - aliased as 'data')
    *     the contact IDs
-   * @param string $name
+   * @param string $queryText
    *   The name string to execute the query against (this is the value being typed in by the user).
    * @param string $context
    *   The context in which this ajax call is being made (for example: 'customfield', 'caseview').
@@ -978,8 +1021,8 @@ abstract class CRM_Utils_Hook {
    *
    * @return mixed
    */
-  public static function contactListQuery(&$query, $name, $context, $id) {
-    return self::singleton()->invoke(4, $query, $name, $context, $id,
+  public static function contactListQuery(&$query, $queryText, $context, $id) {
+    return self::singleton()->invoke(array('query', 'queryText', 'context', 'id'), $query, $queryText, $context, $id,
       self::$_nullObject, self::$_nullObject,
       'civicrm_contactListQuery'
     );
@@ -1012,7 +1055,7 @@ abstract class CRM_Utils_Hook {
     &$rawParams,
     &$cookedParams
   ) {
-    return self::singleton()->invoke(3, $paymentObj, $rawParams, $cookedParams,
+    return self::singleton()->invoke(array('paymentObj', 'rawParams', 'cookedParams'), $paymentObj, $rawParams, $cookedParams,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterPaymentProcessorParams'
     );
@@ -1030,7 +1073,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterMailParams(&$params, $context = NULL) {
-    return self::singleton()->invoke(2, $params, $context,
+    return self::singleton()->invoke(array('params', 'context'), $params, $context,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterMailParams'
     );
@@ -1055,7 +1098,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterCalculatedMembershipStatus(&$membershipStatus, $arguments, $membership) {
-    return self::singleton()->invoke(3, $membershipStatus, $arguments,
+    return self::singleton()->invoke(array('membershipStatus', 'arguments', 'membership'), $membershipStatus, $arguments,
       $membership, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterCalculatedMembershipStatus'
     );
@@ -1070,7 +1113,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterMailContent(&$content) {
-    return self::singleton()->invoke(1, $content,
+    return self::singleton()->invoke(array('content'), $content,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterMailContent'
     );
@@ -1087,7 +1130,7 @@ abstract class CRM_Utils_Hook {
    *   and the value is an array with keys 'label' and 'value' specifying label/value pairs
    */
   public static function caseSummary($caseID) {
-    return self::singleton()->invoke(1, $caseID,
+    return self::singleton()->invoke(array('caseID'), $caseID,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_caseSummary'
     );
@@ -1102,7 +1145,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function caseTypes(&$caseTypes) {
     return self::singleton()
-      ->invoke(1, $caseTypes, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_caseTypes');
+      ->invoke(array('caseTypes'), $caseTypes, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_caseTypes');
   }
 
   /**
@@ -1115,7 +1158,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function config(&$config) {
-    return self::singleton()->invoke(1, $config,
+    return self::singleton()->invoke(array('config'), $config,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_config'
     );
@@ -1128,13 +1171,13 @@ abstract class CRM_Utils_Hook {
    *
    * @param array $options
    *   Associated array of option values / id
-   * @param string $name
+   * @param string $groupName
    *   Option group name
    *
    * @return mixed
    */
-  public static function optionValues(&$options, $name) {
-    return self::singleton()->invoke(2, $options, $name,
+  public static function optionValues(&$options, $groupName) {
+    return self::singleton()->invoke(array('options', 'groupName'), $options, $groupName,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_optionValues'
     );
@@ -1149,7 +1192,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function navigationMenu(&$params) {
-    return self::singleton()->invoke(1, $params,
+    return self::singleton()->invoke(array('params'), $params,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_navigationMenu'
     );
@@ -1172,7 +1215,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function merge($type, &$data, $mainId = NULL, $otherId = NULL, $tables = NULL) {
-    return self::singleton()->invoke(5, $type, $data, $mainId, $otherId, $tables, self::$_nullObject, 'civicrm_merge');
+    return self::singleton()->invoke(array('type', 'data', 'mainId', 'otherId', 'tables'), $type, $data, $mainId, $otherId, $tables, self::$_nullObject, 'civicrm_merge');
   }
 
   /**
@@ -1190,7 +1233,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterLocationMergeData(&$blocksDAO, $mainId, $otherId, $migrationInfo) {
-    return self::singleton()->invoke(4, $blocksDAO, $mainId, $otherId, $migrationInfo, self::$_nullObject, self::$_nullObject, 'civicrm_alterLocationMergeData');
+    return self::singleton()->invoke(array('blocksDAO', 'mainId', 'otherId', 'migrationInfo'), $blocksDAO, $mainId, $otherId, $migrationInfo, self::$_nullObject, self::$_nullObject, 'civicrm_alterLocationMergeData');
   }
 
   /**
@@ -1202,7 +1245,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function notePrivacy(&$noteValues) {
-    return self::singleton()->invoke(1, $noteValues,
+    return self::singleton()->invoke(array('noteValues'), $noteValues,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_notePrivacy'
     );
@@ -1223,7 +1266,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function export(&$exportTempTable, &$headerRows, &$sqlColumns, &$exportMode) {
-    return self::singleton()->invoke(4, $exportTempTable, $headerRows, $sqlColumns, $exportMode,
+    return self::singleton()->invoke(array('exportTempTable', 'headerRows', 'sqlColumns', 'exportMode'), $exportTempTable, $headerRows, $sqlColumns, $exportMode,
       self::$_nullObject, self::$_nullObject,
       'civicrm_export'
     );
@@ -1242,7 +1285,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function dupeQuery($obj, $type, &$query) {
-    return self::singleton()->invoke(3, $obj, $type, $query,
+    return self::singleton()->invoke(array('obj', 'type', 'query'), $obj, $type, $query,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_dupeQuery'
     );
@@ -1264,7 +1307,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function emailProcessor($type, &$params, $mail, &$result, $action = NULL) {
     return self::singleton()
-      ->invoke(5, $type, $params, $mail, $result, $action, self::$_nullObject, 'civicrm_emailProcessor');
+      ->invoke(array('type', 'params', 'mail', 'result', 'action'), $type, $params, $mail, $result, $action, self::$_nullObject, 'civicrm_emailProcessor');
   }
 
   /**
@@ -1289,7 +1332,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function import($object, $usage, &$objectRef, &$params) {
-    return self::singleton()->invoke(4, $object, $usage, $objectRef, $params,
+    return self::singleton()->invoke(array('object', 'usage', 'objectRef', 'params'), $object, $usage, $objectRef, $params,
       self::$_nullObject, self::$_nullObject,
       'civicrm_import'
     );
@@ -1309,7 +1352,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterAPIPermissions($entity, $action, &$params, &$permissions) {
-    return self::singleton()->invoke(4, $entity, $action, $params, $permissions,
+    return self::singleton()->invoke(array('entity', 'action', 'params', 'permissions'), $entity, $action, $params, $permissions,
       self::$_nullObject, self::$_nullObject,
       'civicrm_alterAPIPermissions'
     );
@@ -1322,7 +1365,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function postSave(&$dao) {
     $hookName = 'civicrm_postSave_' . $dao->getTableName();
-    return self::singleton()->invoke(1, $dao,
+    return self::singleton()->invoke(array('dao'), $dao,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       $hookName
     );
@@ -1339,7 +1382,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function summaryActions(&$actions, $contactID = NULL) {
-    return self::singleton()->invoke(2, $actions, $contactID,
+    return self::singleton()->invoke(array('actions', 'contactID'), $actions, $contactID,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_summaryActions'
     );
@@ -1367,7 +1410,7 @@ abstract class CRM_Utils_Hook {
    *   modify the header and values object to pass the data you need
    */
   public static function searchColumns($objectName, &$headers, &$rows, &$selector) {
-    return self::singleton()->invoke(4, $objectName, $headers, $rows, $selector,
+    return self::singleton()->invoke(array('objectName', 'headers', 'rows', 'selector'), $objectName, $headers, $rows, $selector,
       self::$_nullObject, self::$_nullObject,
       'civicrm_searchColumns'
     );
@@ -1384,7 +1427,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function buildUFGroupsForModule($moduleName, &$ufGroups) {
-    return self::singleton()->invoke(2, $moduleName, $ufGroups,
+    return self::singleton()->invoke(array('moduleName', 'ufGroups'), $moduleName, $ufGroups,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_buildUFGroupsForModule'
     );
@@ -1409,7 +1452,7 @@ abstract class CRM_Utils_Hook {
    * @return null
    */
   public static function emailProcessorContact($email, $contactID, &$result) {
-    return self::singleton()->invoke(3, $email, $contactID, $result,
+    return self::singleton()->invoke(array('email', 'contactID', 'result'), $email, $contactID, $result,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_emailProcessorContact'
     );
@@ -1449,7 +1492,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterMailingLabelParams(&$args) {
-    return self::singleton()->invoke(1, $args,
+    return self::singleton()->invoke(array('args'), $args,
       self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterMailingLabelParams'
@@ -1471,7 +1514,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterContent(&$content, $context, $tplName, &$object) {
-    return self::singleton()->invoke(4, $content, $context, $tplName, $object,
+    return self::singleton()->invoke(array('content', 'context', 'tplName', 'object'), $content, $context, $tplName, $object,
       self::$_nullObject, self::$_nullObject,
       'civicrm_alterContent'
     );
@@ -1493,7 +1536,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterTemplateFile($formName, &$form, $context, &$tplName) {
-    return self::singleton()->invoke(4, $formName, $form, $context, $tplName,
+    return self::singleton()->invoke(array('formName', 'form', 'context', 'tplName'), $formName, $form, $context, $tplName,
       self::$_nullObject, self::$_nullObject,
       'civicrm_alterTemplateFile'
     );
@@ -1518,7 +1561,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function triggerInfo(&$info, $tableName = NULL) {
-    return self::singleton()->invoke(2, $info, $tableName,
+    return self::singleton()->invoke(array('info', 'tableName'), $info, $tableName,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject,
       'civicrm_triggerInfo'
@@ -1532,7 +1575,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterLogTables(&$logTableSpec) {
-    return self::singleton()->invoke(1, $logTableSpec, $_nullObject,
+    return self::singleton()->invoke(array('logTableSpec'), $logTableSpec, $_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject,
       'civicrm_alterLogTables'
@@ -1599,7 +1642,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterReportVar($varType, &$var, &$object) {
-    return self::singleton()->invoke(3, $varType, $var, $object,
+    return self::singleton()->invoke(array('varType', 'var', 'object'), $varType, $var, $object,
       self::$_nullObject,
       self::$_nullObject, self::$_nullObject,
       'civicrm_alterReportVar'
@@ -1620,7 +1663,7 @@ abstract class CRM_Utils_Hook {
    *   FALSE, if $op is 'check' and upgrades are not pending.
    */
   public static function upgrade($op, CRM_Queue_Queue $queue = NULL) {
-    return self::singleton()->invoke(2, $op, $queue,
+    return self::singleton()->invoke(array('op', 'queue'), $op, $queue,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject,
       'civicrm_upgrade'
@@ -1638,7 +1681,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function postEmailSend(&$params) {
-    return self::singleton()->invoke(1, $params,
+    return self::singleton()->invoke(array('params'), $params,
       self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_postEmailSend'
@@ -1654,7 +1697,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function postMailing($mailingId) {
-    return self::singleton()->invoke(1, $mailingId,
+    return self::singleton()->invoke(array('mailingId'), $mailingId,
       self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_postMailing'
@@ -1670,7 +1713,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterSettingsFolders(&$settingsFolders) {
-    return self::singleton()->invoke(1, $settingsFolders,
+    return self::singleton()->invoke(array('settingsFolders'), $settingsFolders,
       self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterSettingsFolders'
@@ -1689,7 +1732,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterSettingsMetaData(&$settingsMetaData, $domainID, $profile) {
-    return self::singleton()->invoke(3, $settingsMetaData,
+    return self::singleton()->invoke(array('settingsMetaData', 'domainID', 'profile'), $settingsMetaData,
       $domainID, $profile,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_alterSettingsMetaData'
@@ -1708,7 +1751,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function apiWrappers(&$wrappers, $apiRequest) {
     return self::singleton()
-      ->invoke(2, $wrappers, $apiRequest, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+      ->invoke(array('wrappers', 'apiRequest'), $wrappers, $apiRequest, self::$_nullObject, self::$_nullObject, self::$_nullObject,
         self::$_nullObject, 'civicrm_apiWrappers'
       );
   }
@@ -1722,7 +1765,7 @@ abstract class CRM_Utils_Hook {
    *   The return value is ignored.
    */
   public static function cron($jobManager) {
-    return self::singleton()->invoke(1,
+    return self::singleton()->invoke(array('jobManager'),
       $jobManager, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_cron'
     );
@@ -1740,33 +1783,47 @@ abstract class CRM_Utils_Hook {
    *   The return value is ignored
    */
   public static function permission(&$permissions) {
-    return self::singleton()->invoke(1, $permissions,
+    return self::singleton()->invoke(array('permissions'), $permissions,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_permission'
     );
   }
 
+  /**
+   * This hook is called when checking permissions; use this hook to dynamically
+   * escalate user permissions in certain use cases (cf. CRM-19256).
+   *
+   * @param string $permission
+   *   The name of an atomic permission, ie. 'access deleted contacts'
+   * @param bool $granted
+   *   Whether this permission is currently granted. The hook can change this value.
+   *
+   * @return null
+   *   The return value is ignored
+   */
+  public static function permission_check($permission, &$granted) {
+    return self::singleton()->invoke(array('permission', 'granted'), $permission, $granted,
+      self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+      'civicrm_permission_check'
+    );
+  }
+
   /**
    * @param CRM_Core_Exception Exception $exception
    * @param mixed $request
    *   Reserved for future use.
    */
   public static function unhandledException($exception, $request = NULL) {
-    self::singleton()
-      ->invoke(2, $exception, $request, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_unhandled_exception');
-    // == 4.4 ==
-    // $event = new stdClass();
-    // $event->exception = $exception;
-    // CRM_Core_LegacyErrorHandler::handleException($event);
-
-    // == 4.5+ ==
     $event = new \Civi\Core\Event\UnhandledExceptionEvent($exception, self::$_nullObject);
-    \Civi::service('dispatcher')->dispatch("hook_civicrm_unhandled_exception", $event);
+    \Civi::dispatcher()->dispatch('hook_civicrm_unhandled_exception', $event);
   }
 
   /**
    * This hook is called for declaring managed entities via API.
    *
+   * Note: This is a preboot hook. It will dispatch via the extension/module
+   * subsystem but *not* the Symfony EventDispatcher.
+   *
    * @param array[] $entityTypes
    *   List of entity types; each entity-type is an array with keys:
    *   - name: string, a unique short name (e.g. "ReportInstance")
@@ -1779,63 +1836,75 @@ abstract class CRM_Utils_Hook {
    *   The return value is ignored
    */
   public static function entityTypes(&$entityTypes) {
-    return self::singleton()->invoke(1, $entityTypes, self::$_nullObject, self::$_nullObject,
+    return self::singleton()->invoke(array('entityTypes'), $entityTypes, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_entityTypes'
     );
   }
 
+  /**
+   * Build a description of available hooks.
+   *
+   * @param \Civi\Core\CiviEventInspector $inspector
+   */
+  public static function eventDefs($inspector) {
+    $event = \Civi\Core\Event\GenericHookEvent::create(array(
+      'inspector' => $inspector,
+    ));
+    Civi::dispatcher()->dispatch('hook_civicrm_eventDefs', $event);
+  }
+
   /**
    * This hook is called while preparing a profile form.
    *
-   * @param string $name
+   * @param string $profileName
    * @return mixed
    */
-  public static function buildProfile($name) {
-    return self::singleton()->invoke(1, $name, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+  public static function buildProfile($profileName) {
+    return self::singleton()->invoke(array('profileName'), $profileName, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, 'civicrm_buildProfile');
   }
 
   /**
    * This hook is called while validating a profile form submission.
    *
-   * @param string $name
+   * @param string $profileName
    * @return mixed
    */
-  public static function validateProfile($name) {
-    return self::singleton()->invoke(1, $name, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+  public static function validateProfile($profileName) {
+    return self::singleton()->invoke(array('profileName'), $profileName, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, 'civicrm_validateProfile');
   }
 
   /**
    * This hook is called processing a valid profile form submission.
    *
-   * @param string $name
+   * @param string $profileName
    * @return mixed
    */
-  public static function processProfile($name) {
-    return self::singleton()->invoke(1, $name, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+  public static function processProfile($profileName) {
+    return self::singleton()->invoke(array('profileName'), $profileName, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, 'civicrm_processProfile');
   }
 
   /**
    * This hook is called while preparing a read-only profile screen
    *
-   * @param string $name
+   * @param string $profileName
    * @return mixed
    */
-  public static function viewProfile($name) {
-    return self::singleton()->invoke(1, $name, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+  public static function viewProfile($profileName) {
+    return self::singleton()->invoke(array('profileName'), $profileName, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, 'civicrm_viewProfile');
   }
 
   /**
    * This hook is called while preparing a list of contacts (based on a profile)
    *
-   * @param string $name
+   * @param string $profileName
    * @return mixed
    */
-  public static function searchProfile($name) {
-    return self::singleton()->invoke(1, $name, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+  public static function searchProfile($profileName) {
+    return self::singleton()->invoke(array('profileName'), $profileName, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, 'civicrm_searchProfile');
   }
 
@@ -1856,7 +1925,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function alterBadge($labelName, &$label, &$format, &$participant) {
     return self::singleton()
-      ->invoke(4, $labelName, $label, $format, $participant, self::$_nullObject, self::$_nullObject, 'civicrm_alterBadge');
+      ->invoke(array('labelName', 'label', 'format', 'participant'), $labelName, $label, $format, $participant, self::$_nullObject, self::$_nullObject, 'civicrm_alterBadge');
   }
 
 
@@ -1873,7 +1942,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterBarcode(&$data, $type = 'barcode', $context = 'name_badge') {
-    return self::singleton()->invoke(3, $data, $type, $context, self::$_nullObject,
+    return self::singleton()->invoke(array('data', 'type', 'context'), $data, $type, $context, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, 'civicrm_alterBarcode');
   }
 
@@ -1892,7 +1961,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function alterMailer(&$mailer, $driver, $params) {
     return self::singleton()
-      ->invoke(3, $mailer, $driver, $params, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_alterMailer');
+      ->invoke(array('mailer', 'driver', 'params'), $mailer, $driver, $params, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_alterMailer');
   }
 
   /**
@@ -1911,6 +1980,13 @@ abstract class CRM_Utils_Hook {
    * @deprecated
    */
   public static function alterMail(&$mailer, $driver, $params) {
+    // This has been deprecated on the premise it MIGHT be called externally for a long time.
+    // We don't have a clear policy on how much we support external extensions calling internal
+    // hooks (ie. in general we say 'don't call internal functions', but some hooks like pre hooks
+    // are expected to be called externally.
+    // It's really really unlikely anyone uses this - but let's add deprecations for a couple
+    // of releases first.
+    Civi::log()->warning('Deprecated function CRM_Utils_Hook::alterMail, use CRM_Utils_Hook::alterMailer', array('civi.tag' => 'deprecated'));
     return CRM_Utils_Hook::alterMailer($mailer, $driver, $params);
   }
 
@@ -1925,7 +2001,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function queryObjects(&$queryObjects, $type = 'Contact') {
     return self::singleton()
-      ->invoke(2, $queryObjects, $type, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_queryObjects');
+      ->invoke(array('queryObjects', 'type'), $queryObjects, $type, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_queryObjects');
   }
 
   /**
@@ -1940,7 +2016,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function dashboard_defaults($availableDashlets, &$defaultDashlets) {
     return self::singleton()
-      ->invoke(2, $availableDashlets, $defaultDashlets, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_dashboard_defaults');
+      ->invoke(array('availableDashlets', 'defaultDashlets'), $availableDashlets, $defaultDashlets, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_dashboard_defaults');
   }
 
   /**
@@ -1956,7 +2032,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function pre_case_merge($mainContactId, $mainCaseId = NULL, $otherContactId = NULL, $otherCaseId = NULL, $changeClient = FALSE) {
     return self::singleton()
-      ->invoke(5, $mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient, self::$_nullObject, 'civicrm_pre_case_merge');
+      ->invoke(array('mainContactId', 'mainCaseId', 'otherContactId', 'otherCaseId', 'changeClient'), $mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient, self::$_nullObject, 'civicrm_pre_case_merge');
   }
 
   /**
@@ -1972,7 +2048,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function post_case_merge($mainContactId, $mainCaseId = NULL, $otherContactId = NULL, $otherCaseId = NULL, $changeClient = FALSE) {
     return self::singleton()
-      ->invoke(5, $mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient, self::$_nullObject, 'civicrm_post_case_merge');
+      ->invoke(array('mainContactId', 'mainCaseId', 'otherContactId', 'otherCaseId', 'changeClient'), $mainContactId, $mainCaseId, $otherContactId, $otherCaseId, $changeClient, self::$_nullObject, 'civicrm_post_case_merge');
   }
 
   /**
@@ -1989,7 +2065,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterDisplayName(&$displayName, $contactId, $dao) {
-    return self::singleton()->invoke(3,
+    return self::singleton()->invoke(array('displayName', 'contactId', 'dao'),
       $displayName, $contactId, $dao, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, 'civicrm_contact_get_displayname'
     );
@@ -2019,7 +2095,7 @@ abstract class CRM_Utils_Hook {
    * @endcode
    */
   public static function angularModules(&$angularModules) {
-    return self::singleton()->invoke(1, $angularModules,
+    return self::singleton()->invoke(array('angularModules'), $angularModules,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_angularModules'
     );
@@ -2033,12 +2109,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function caseChange(\Civi\CCase\Analyzer $analyzer) {
     $event = new \Civi\CCase\Event\CaseChangeEvent($analyzer);
-    \Civi::service('dispatcher')->dispatch("hook_civicrm_caseChange", $event);
-
-    self::singleton()->invoke(1, $analyzer,
-      self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
-      'civicrm_caseChange'
-    );
+    \Civi::dispatcher()->dispatch('hook_civicrm_caseChange', $event);
   }
 
   /**
@@ -2060,7 +2131,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function crudLink($spec, $bao, &$link) {
-    return self::singleton()->invoke(3, $spec, $bao, $link,
+    return self::singleton()->invoke(array('spec', 'bao', 'link'), $spec, $bao, $link,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_crudLink'
     );
@@ -2084,11 +2155,14 @@ abstract class CRM_Utils_Hook {
    * flush the cache. Additionally, you should relax caching during development.
    * In `civicrm.settings.php`, set define('CIVICRM_CONTAINER_CACHE', 'auto').
    *
+   * Note: This is a preboot hook. It will dispatch via the extension/module
+   * subsystem but *not* the Symfony EventDispatcher.
+   *
    * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
    * @see http://symfony.com/doc/current/components/dependency_injection/index.html
    */
   public static function container(\Symfony\Component\DependencyInjection\ContainerBuilder $container) {
-    self::singleton()->invoke(1, $container, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_container');
+    self::singleton()->invoke(array('container'), $container, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_container');
   }
 
   /**
@@ -2096,7 +2170,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function fileSearches(&$fileSearches) {
-    return self::singleton()->invoke(1, $fileSearches,
+    return self::singleton()->invoke(array('fileSearches'), $fileSearches,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_fileSearches'
     );
@@ -2111,7 +2185,7 @@ abstract class CRM_Utils_Hook {
    */
   public static function check(&$messages) {
     return self::singleton()
-      ->invoke(1, $messages, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_check');
+      ->invoke(array('messages'), $messages, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_check');
   }
 
   /**
@@ -2122,7 +2196,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function batchQuery(&$query) {
-    return self::singleton()->invoke(1, $query, self::$_nullObject,
+    return self::singleton()->invoke(array('query'), $query, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_batchQuery'
     );
@@ -2143,7 +2217,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function alterDeferredRevenueItems(&$deferredRevenues, $contributionDetails, $update, $context) {
-    return self::singleton()->invoke(4, $deferredRevenues, $contributionDetails, $update, $context,
+    return self::singleton()->invoke(array('deferredRevenues', 'contributionDetails', 'update', 'context'), $deferredRevenues, $contributionDetails, $update, $context,
       self::$_nullObject, self::$_nullObject, 'civicrm_alterDeferredRevenueItems'
     );
   }
@@ -2157,7 +2231,7 @@ abstract class CRM_Utils_Hook {
    * @return mixed
    */
   public static function batchItems(&$results, &$items) {
-    return self::singleton()->invoke(2, $results, $items,
+    return self::singleton()->invoke(array('results', 'items'), $results, $items,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_batchItems'
     );
@@ -2175,7 +2249,7 @@ abstract class CRM_Utils_Hook {
     // First allow the cms integration to add to the list
     CRM_Core_Config::singleton()->userSystem->appendCoreResources($list);
 
-    self::singleton()->invoke(2, $list, $region,
+    self::singleton()->invoke(array('list', 'region'), $list, $region,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_coreResourceList'
     );
@@ -2189,18 +2263,21 @@ abstract class CRM_Utils_Hook {
    * @param array $filters
    */
   public static function entityRefFilters(&$filters) {
-    self::singleton()->invoke(1, $filters, self::$_nullObject, self::$_nullObject,
+    self::singleton()->invoke(array('filters'), $filters, self::$_nullObject, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_entityRefFilters'
     );
   }
 
   /**
-   * This hook is called for bypass a few civicrm urls from IDS check
-   * @param array $skip list of civicrm url;
+   * This hook is called for bypass a few civicrm urls from IDS check.
+   *
+   * @param array $skip list of civicrm urls
+   *
+   * @return mixed
    */
   public static function idsException(&$skip) {
-    return self::singleton()->invoke(1, $skip, self::$_nullObject,
+    return self::singleton()->invoke(array('skip'), $skip, self::$_nullObject,
       self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_idsException'
     );
@@ -2212,12 +2289,37 @@ abstract class CRM_Utils_Hook {
    * @param string $geoProvider
    * @param array $values
    * @param SimpleXMLElement $xml
+   *
+   * @return mixed
    */
   public static function geocoderFormat($geoProvider, &$values, $xml) {
-    return self::singleton()->invoke(3, $geoProvider, $values, $xml,
+    return self::singleton()->invoke(array('geoProvider', 'values', 'xml'), $geoProvider, $values, $xml,
       self::$_nullObject, self::$_nullObject, self::$_nullObject,
       'civicrm_geocoderFormat'
     );
   }
 
+  /**
+   * This hook is called before an inbound SMS is processed.
+   *
+   * @param string $from
+   *   The phone number the message is from, as set by SMS provider
+   * @param int $fromContactID
+   *   Set to override default matching
+   * @param string $to
+   *   The optional phone number the message is to, as set by SMS provider
+   * @param int $toContactID
+   *   Set to override default matching
+   * @param string $body
+   *   The body text of the message
+   * @param string $trackID
+   *   The tracking ID of the message
+   *
+   * @return mixed
+   */
+  public static function inboundSMS(&$from, &$fromContactID = NULL, &$to, &$toContactID = NULL, &$body, &$trackID) {
+    return self::singleton()
+      ->invoke(6, $from, $fromContactID, $to, $toContactID, $body, $trackID, 'civicrm_inboundSMS');
+  }
+
 }