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 * @var string|null e.g. "BEGIN" or "SAVEPOINT foo"
37 * @var string|null e.g. "COMMIT"
42 * @var string|null e.g. "ROLLBACK" or "ROLLBACK TO SAVEPOINT foo"
44 private $rollbackStmt;
49 private $refCount = 0;
51 private $doCommit = TRUE;
56 private $state = self
::F_NEW
;
59 * @param \CRM_Core_DAO $dao
60 * @param string|null $beginStmt e.g. "BEGIN" or "SAVEPOINT foo"
61 * @param string|null $commitStmt e.g. "COMMIT"
62 * @param string|null $rollbackStmt e.g. "ROLLBACK" or "ROLLBACK TO SAVEPOINT foo"
64 public function __construct($dao, $beginStmt, $commitStmt, $rollbackStmt) {
66 $this->beginStmt
= $beginStmt;
67 $this->commitStmt
= $commitStmt;
68 $this->rollbackStmt
= $rollbackStmt;
71 \CRM_Core_Transaction
::PHASE_PRE_COMMIT
=> [],
72 \CRM_Core_Transaction
::PHASE_POST_COMMIT
=> [],
73 \CRM_Core_Transaction
::PHASE_PRE_ROLLBACK
=> [],
74 \CRM_Core_Transaction
::PHASE_POST_ROLLBACK
=> [],
78 public function inc() {
82 public function dec() {
89 public function isEmpty() {
90 return ($this->refCount
== 0);
96 public function isRollbackOnly() {
97 return !$this->doCommit
;
100 public function setRollbackOnly() {
101 $this->doCommit
= FALSE;
105 * Begin frame processing.
107 * @throws \CRM_Core_Exception
109 public function begin() {
110 if ($this->state
!== self
::F_NEW
) {
111 throw new \
CRM_Core_Exception('State is not F_NEW');
114 $this->state
= self
::F_ACTIVE
;
115 if ($this->beginStmt
) {
116 $this->dao
->query($this->beginStmt
);
121 * Finish frame processing.
123 * @param int $newState
125 * @throws \CRM_Core_Exception
127 public function finish($newState = self
::F_DONE
) {
128 if ($this->state
== self
::F_FORCED
) {
131 if ($this->state
!== self
::F_ACTIVE
) {
132 throw new \
CRM_Core_Exception('State is not F_ACTIVE');
135 $this->state
= $newState;
137 if ($this->doCommit
) {
138 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_PRE_COMMIT
);
139 if ($this->commitStmt
) {
140 $this->dao
->query($this->commitStmt
);
142 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_POST_COMMIT
);
145 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_PRE_ROLLBACK
);
146 if ($this->rollbackStmt
) {
147 $this->dao
->query($this->rollbackStmt
);
149 $this->invokeCallbacks(\CRM_Core_Transaction
::PHASE_POST_ROLLBACK
);
153 public function forceRollback() {
154 $this->setRollbackOnly();
155 $this->finish(self
::F_FORCED
);
159 * Add a transaction callback.
161 * Pre-condition: isActive()
164 * A constant; one of: self::PHASE_{PRE,POST}_{COMMIT,ROLLBACK}.
165 * @param mixed $callback
167 * @param array|NULL $params Optional values to pass to callback.
168 * See php manual call_user_func_array for details.
171 public function addCallback($phase, $callback, $params = NULL, $id = NULL) {
173 $this->callbacks
[$phase][$id] = [
174 'callback' => $callback,
175 'parameters' => (is_array($params) ?
$params : [$params]),
179 $this->callbacks
[$phase][] = [
180 'callback' => $callback,
181 'parameters' => (is_array($params) ?
$params : [$params]),
189 public function invokeCallbacks($phase) {
190 if (is_array($this->callbacks
[$phase])) {
191 foreach ($this->callbacks
[$phase] as $cb) {
192 call_user_func_array($cb['callback'], $cb['parameters']);