Merge pull request #17480 from tunbola/email-template-perms
[civicrm-core.git] / Civi / Core / Transaction / Frame.php
CommitLineData
5bb8417a
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
41498ac5 4 | Copyright CiviCRM LLC. All rights reserved. |
5bb8417a 5 | |
41498ac5
TO
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 |
5bb8417a 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
5bb8417a
TO
11
12namespace Civi\Core\Transaction;
13
14/**
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).
18 *
19 * @package Civi
ca5cec67 20 * @copyright CiviCRM LLC https://civicrm.org/licensing
5bb8417a
TO
21 */
22class Frame {
23
24 const F_NEW = 0, F_ACTIVE = 1, F_DONE = 2, F_FORCED = 3;
25
26 /**
27 * @var \CRM_Core_DAO
28 */
29 private $dao;
30
31 /**
8d0a3eb6
TO
32 * The statement used to start this transaction - e.g. "BEGIN" or "SAVEPOINT foo"
33 *
34 * @var string|null
5bb8417a
TO
35 */
36 private $beginStmt;
37
38 /**
8d0a3eb6
TO
39 * The statement used to commit this transaction - e.g. "COMMIT"
40 *
41 * @var string|null
5bb8417a
TO
42 */
43 private $commitStmt;
44
45 /**
8d0a3eb6
TO
46 * The statement used to rollback this transaction - e.g. "ROLLBACK" or "ROLLBACK TO SAVEPOINT foo"
47 *
48 * @var string|null
5bb8417a
TO
49 */
50 private $rollbackStmt;
51
52 /**
53 * @var int
54 */
55 private $refCount = 0;
56 private $callbacks;
57 private $doCommit = TRUE;
58
59 /**
60 * @var int
61 */
62 private $state = self::F_NEW;
63
64 /**
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"
69 */
70 public function __construct($dao, $beginStmt, $commitStmt, $rollbackStmt) {
71 $this->dao = $dao;
72 $this->beginStmt = $beginStmt;
73 $this->commitStmt = $commitStmt;
74 $this->rollbackStmt = $rollbackStmt;
75
c64f69d9
CW
76 $this->callbacks = [
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 => [],
81 ];
5bb8417a
TO
82 }
83
84 public function inc() {
85 $this->refCount++;
86 }
87
88 public function dec() {
89 $this->refCount--;
90 }
91
7fe37828
EM
92 /**
93 * @return bool
94 */
5bb8417a
TO
95 public function isEmpty() {
96 return ($this->refCount == 0);
97 }
98
7fe37828
EM
99 /**
100 * @return bool
101 */
5bb8417a
TO
102 public function isRollbackOnly() {
103 return !$this->doCommit;
104 }
105
106 public function setRollbackOnly() {
46bcf597 107 $this->doCommit = FALSE;
5bb8417a
TO
108 }
109
0dbbb8e7 110 /**
111 * Begin frame processing.
112 *
113 * @throws \CRM_Core_Exception
114 */
5bb8417a 115 public function begin() {
0dbbb8e7 116 if ($this->state !== self::F_NEW) {
117 throw new \CRM_Core_Exception('State is not F_NEW');
118 };
119
5bb8417a
TO
120 $this->state = self::F_ACTIVE;
121 if ($this->beginStmt) {
122 $this->dao->query($this->beginStmt);
123 }
124 }
125
126 /**
0dbbb8e7 127 * Finish frame processing.
128 *
2241036a 129 * @param int $newState
0dbbb8e7 130 *
131 * @throws \CRM_Core_Exception
5bb8417a
TO
132 */
133 public function finish($newState = self::F_DONE) {
134 if ($this->state == self::F_FORCED) {
135 return;
136 }
0dbbb8e7 137 if ($this->state !== self::F_ACTIVE) {
138 throw new \CRM_Core_Exception('State is not F_ACTIVE');
139 };
140
5bb8417a
TO
141 $this->state = $newState;
142
143 if ($this->doCommit) {
144 $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_PRE_COMMIT);
145 if ($this->commitStmt) {
146 $this->dao->query($this->commitStmt);
147 }
148 $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_POST_COMMIT);
149 }
150 else {
151 $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_PRE_ROLLBACK);
152 if ($this->rollbackStmt) {
153 $this->dao->query($this->rollbackStmt);
154 }
155 $this->invokeCallbacks(\CRM_Core_Transaction::PHASE_POST_ROLLBACK);
156 }
157 }
158
159 public function forceRollback() {
160 $this->setRollbackOnly();
161 $this->finish(self::F_FORCED);
162 }
163
164 /**
fe482240 165 * Add a transaction callback.
5bb8417a
TO
166 *
167 * Pre-condition: isActive()
168 *
04855556
TO
169 * @param int $phase
170 * A constant; one of: self::PHASE_{PRE,POST}_{COMMIT,ROLLBACK}.
171 * @param mixed $callback
172 * A PHP callback.
5bb8417a
TO
173 * @param array|NULL $params Optional values to pass to callback.
174 * See php manual call_user_func_array for details.
257e7666 175 * @param null $id
5bb8417a 176 */
46bcf597 177 public function addCallback($phase, $callback, $params = NULL, $id = NULL) {
5bb8417a 178 if ($id) {
c64f69d9 179 $this->callbacks[$phase][$id] = [
5bb8417a 180 'callback' => $callback,
c64f69d9
CW
181 'parameters' => (is_array($params) ? $params : [$params]),
182 ];
5bb8417a
TO
183 }
184 else {
c64f69d9 185 $this->callbacks[$phase][] = [
5bb8417a 186 'callback' => $callback,
c64f69d9
CW
187 'parameters' => (is_array($params) ? $params : [$params]),
188 ];
5bb8417a
TO
189 }
190 }
191
192 /**
193 * @param int $phase
194 */
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']);
199 }
200 }
201 }
202
203}