diff --git a/CRM/Core/Transaction.php b/CRM/Core/Transaction.php index e23e9f4315ee..d42197f9de76 100644 --- a/CRM/Core/Transaction.php +++ b/CRM/Core/Transaction.php @@ -252,4 +252,24 @@ public static function addCallback($phase, $callback, $params = NULL, $id = NULL $frame->addCallback($phase, $callback, $params, $id); } + /** + * Whenever hook_civicrm_post fires, schedule an equivalent + * call to hook_civicrm_postCommit. + * + * @param \Civi\Core\Event\PostEvent $e + * @see CRM_Utils_Hook::post + */ + public static function addPostCommit($e) { + // Do we want to dedupe post-commit hooks for the same txn? Setting an ID + // would allow this. + // $id = $e->entity . chr(0) . $e->action . chr(0) . $e->id; + $frame = \Civi\Core\Transaction\Manager::singleton()->getBaseFrame(); + if ($frame) { + $frame->addCallback(self::PHASE_POST_COMMIT, ['CRM_Utils_Hook', 'postCommit'], [$e->action, $e->entity, $e->id, $e->object]); + } + else { + \CRM_Utils_Hook::postCommit($e->action, $e->entity, $e->id, $e->object); + } + } + } diff --git a/CRM/Utils/Hook.php b/CRM/Utils/Hook.php index 76887f2bc3cb..e0e7a9eec13b 100644 --- a/CRM/Utils/Hook.php +++ b/CRM/Utils/Hook.php @@ -393,6 +393,36 @@ public static function post($op, $objectName, $objectId, &$objectRef = NULL) { return $event->getReturnValues(); } + /** + * This hook is equivalent to post(), except that it is guaranteed to run + * outside of any SQL transaction. The objectRef is not modifiable. + * + * This hook is defined for two cases: + * + * 1. If the original action runs within a transaction, then the hook fires + * after the transaction commits. + * 2. If the original action runs outside a transaction, then the data was + * committed immediately, and we can run the hook immediately. + * + * @param string $op + * The type of operation being performed. + * @param string $objectName + * The name of the object. + * @param int $objectId + * The unique identifier for the object. + * @param object $objectRef + * The reference to the object if available. + * + * @return mixed + * based on op. pre-hooks return a boolean or + * an error message which aborts the operation + */ + public static function postCommit($op, $objectName, $objectId, $objectRef = NULL) { + $event = new \Civi\Core\Event\PostEvent($op, $objectName, $objectId, $objectRef); + \Civi::dispatcher()->dispatch('hook_civicrm_postCommit', $event); + return $event->getReturnValues(); + } + /** * This hook retrieves links from other modules and injects it into. * the view contact tabs diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index 0dfebb73ed95..c9232136f0a4 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -335,6 +335,7 @@ public function createEventDispatcher($container) { $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, ['\Civi\Core\InstallationCanary', 'check']); $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, ['\Civi\Core\DatabaseInitializer', 'initialize']); $dispatcher->addListener(SystemInstallEvent::EVENT_NAME, ['\Civi\Core\LocalizationInitializer', 'initialize']); + $dispatcher->addListener('hook_civicrm_post', ['\CRM_Core_Transaction', 'addPostCommit'], -1000); $dispatcher->addListener('hook_civicrm_pre', ['\Civi\Core\Event\PreEvent', 'dispatchSubevent'], 100); $dispatcher->addListener('hook_civicrm_post', ['\Civi\Core\Event\PostEvent', 'dispatchSubevent'], 100); $dispatcher->addListener('hook_civicrm_post::Activity', ['\Civi\CCase\Events', 'fireCaseChange']);