Commit | Line | Data |
---|---|---|
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 | |
12 | namespace 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 | */ |
22 | class 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 | } |