3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
12 namespace Civi\Core\Transaction
;
15 * A "frame" is a layer in a series of nested transactions. Generally,
16 * the outermost frame is a normal SQL transaction (BEGIN/ROLLBACK/COMMIT)
17 * and any nested frames are SQL savepoints (SAVEPOINT foo/ROLLBACK TO SAVEPOINT).
20 * @copyright CiviCRM LLC https://civicrm.org/licensing
24 const F_NEW
= 0, F_ACTIVE
= 1, F_DONE
= 2, F_FORCED
= 3;
32 * The statement used to start this transaction - e.g. "BEGIN" or "SAVEPOINT foo"
39 * The statement used to commit this transaction - e.g. "COMMIT"
46 * The statement used to rollback this transaction - e.g. "ROLLBACK" or "ROLLBACK TO SAVEPOINT foo"
50 private $rollbackStmt;
55 private $refCount = 0;
57 private $doCommit = TRUE;
62 private $state = self
::F_NEW
;
65 * @param \CRM_Core_DAO $dao
66 * @param string|null $beginStmt e.g. "BEGIN" or "SAVEPOINT foo"
67 * @param string|null $commitStmt e.g. "COMMIT"
68 * @param string|null $rollbackStmt e.g. "ROLLBACK" or "ROLLBACK TO SAVEPOINT foo"
70 public function __construct($dao, $beginStmt, $commitStmt, $rollbackStmt) {
72 $this->beginStmt
= $beginStmt;
73 $this->commitStmt
= $commitStmt;
74 $this->rollbackStmt
= $rollbackStmt;
77 \CRM_Core_Transaction
::PHASE_PRE_COMMIT
=> [],
78 \CRM_Core_Transaction
::PHASE_POST_COMMIT
=> [],
79 \CRM_Core_Transaction
::PHASE_PRE_ROLLBACK
=> [],
80 \CRM_Core_Transaction
::PHASE_POST_ROLLBACK
=> [],
84 public function inc() {
88 public function dec() {
95 public function isEmpty() {
96 return ($this->refCount
== 0);
102 public function isRollbackOnly() {
103 return !$this->doCommit
;
106 public function setRollbackOnly() {
107 $this->doCommit
= FALSE;
111 * Begin frame processing.
113 * @throws \CRM_Core_Exception
115 public function begin() {
116 if ($this->state
!== self
::F_NEW
) {
117 throw new \
CRM_Core_Exception('State is not F_NEW');
120 $this->state
= self
::F_ACTIVE
;
121 if ($this->beginStmt
) {
122 $this->dao
->query($this->beginStmt
);
127 * Finish frame processing.
129 * @param int $newState
131 * @throws \CRM_Core_Exception
133 public function finish($newState = self
::F_DONE
) {
134 if ($this->state
== self
::F_FORCED
) {
137 if ($this->state
!== self
::F_ACTIVE
) {
138 throw new \
CRM_Core_Exception('State is not F_ACTIVE');
141 $this->state
= $newState;
143 if ($this->doCommit
) {
144 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_PRE_COMMIT
);
145 if ($this->commitStmt
) {
146 $this->dao
->query($this->commitStmt
);
148 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_POST_COMMIT
);
151 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_PRE_ROLLBACK
);
152 if ($this->rollbackStmt
) {
153 $this->dao
->query($this->rollbackStmt
);
155 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_POST_ROLLBACK
);
159 public function forceRollback() {
160 $this->setRollbackOnly();
161 $this->finish(self
::F_FORCED
);
165 * Add a transaction callback.
167 * Pre-condition: isActive()
170 * A constant; one of: self::PHASE_{PRE,POST}_{COMMIT,ROLLBACK}.
171 * @param mixed $callback
173 * @param array|NULL $params Optional values to pass to callback.
174 * See php manual call_user_func_array for details.
177 public function addCallback($phase, $callback, $params = NULL, $id = NULL) {
179 $this->callbacks
[$phase][$id] = [
180 'callback' => $callback,
181 'parameters' => (is_array($params) ?
$params : [$params]),
185 $this->callbacks
[$phase][] = [
186 'callback' => $callback,
187 'parameters' => (is_array($params) ?
$params : [$params]),
195 public function invokeCallbacks($phase) {
196 if (is_array($this->callbacks
[$phase])) {
197 foreach ($this->callbacks
[$phase] as $cb) {
198 call_user_func_array($cb['callback'], $cb['parameters']);