PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.3.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.3.0
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / vendor / wpfluent / framework / src / WPFluent / Database / Concerns / ManagesTransactions.php

ManagesTransactions.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 2.3.0, at vendor/wpfluent/framework/src/WPFluent/Database/Concerns/ManagesTransactions.php

330 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\Framework\Database\Concerns;
4
5 use Closure;
6 use Throwable;
7 use RuntimeException;
8
9 trait ManagesTransactions
10 {
11 /**
12 * Execute a Closure within a transaction.
13 *
14 * @template TReturn
15 * @param \Closure(static): TReturn $callback
16 * @param int $attempts
17 * @return TReturn
18 *
19 * @throws \Throwable
20 */
21 public function transaction(Closure $callback, $attempts = 1)
22 {
23 for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
24
25 $this->beginTransaction();
26
27 try {
28 $result = $callback($this);
29 } catch (Throwable $e) {
30 $this->handleTransactionException($e, $currentAttempt, $attempts);
31 continue;
32 }
33
34 $levelBeingCommitted = $this->transactions;
35
36 try {
37 $this->commit();
38 } catch (Throwable $e) {
39 $this->handleCommitTransactionException($e, $currentAttempt, $attempts);
40 continue;
41 }
42
43 if ($this->transactionsManager) {
44 $this->transactionsManager->commit(
45 $this->getName(),
46 $levelBeingCommitted,
47 $this->transactions
48 );
49 }
50
51 return $result;
52 }
53 }
54
55 /**
56 * Begin transaction or create savepoint.
57 */
58 public function beginTransaction()
59 {
60 if ($this->inTransaction()) {
61
62 $this->transactions++;
63
64 if ($this->queryGrammar->supportsSavepoints()) {
65 $this->createSavepoint();
66 }
67
68 if ($this->transactionsManager) {
69 $this->transactionsManager->begin(
70 $this->getName(),
71 $this->transactions
72 );
73 }
74
75 $this->fireConnectionEvent('beganTransaction');
76
77 return;
78 }
79
80 // Run any registered pre-transaction callbacks before issuing
81 // the BEGIN — useful for setting session vars, acquiring locks,
82 // or other per-transaction connection setup.
83 foreach ($this->beforeStartingTransaction as $callback) {
84 $callback($this);
85 }
86
87 $this->transactions++;
88
89 $this->createTransaction();
90
91 if ($this->transactionsManager) {
92 $this->transactionsManager->begin(
93 $this->getName(),
94 $this->transactions
95 );
96 }
97
98 $this->fireConnectionEvent('beganTransaction');
99 }
100
101 /**
102 * Create root transaction.
103 */
104 protected function createTransaction()
105 {
106 // Defensive: only issue the top-level BEGIN if we just entered
107 // the first transaction level. Prevents duplicate BEGIN on
108 // mis-call from a subclass or refactor.
109 if ($this->transactions === 1) {
110 try {
111 if ($this->isSqlite()) {
112 $this->unprepared('BEGIN;');
113 } else {
114 $this->unprepared('START TRANSACTION;');
115 }
116 } catch (Throwable $e) {
117 $this->handleBeginTransactionException($e);
118 }
119 }
120 }
121
122 /**
123 * Create savepoint.
124 */
125 protected function createSavepoint()
126 {
127 $name = $this->getSavepointName($this->transactions);
128 $this->unprepared(
129 $this->queryGrammar->compileSavepoint($name) . ';'
130 );
131 }
132
133 /**
134 * Commit transaction or release savepoint.
135 */
136 public function commit()
137 {
138 if ($this->transactions === 0) {
139 return;
140 }
141
142 if ($this->transactions === 1) {
143
144 $this->fireConnectionEvent('committing');
145 $this->unprepared('COMMIT;');
146
147 } elseif ($this->queryGrammar->supportsSavepoints()) {
148
149 $name = $this->getSavepointName($this->transactions);
150 $this->unprepared(
151 $this->queryGrammar->compileSavepointRelease($name) . ';'
152 );
153 }
154
155 [$levelBeingCommitted, $this->transactions] = [
156 $this->transactions,
157 max(0, $this->transactions - 1),
158 ];
159
160 if ($this->transactionsManager) {
161 $this->transactionsManager->commit(
162 $this->getName(),
163 $levelBeingCommitted,
164 $this->transactions
165 );
166 }
167
168 $this->fireConnectionEvent('committed');
169 }
170
171 /**
172 * Rollback transaction.
173 */
174 public function rollBack($toLevel = null)
175 {
176 $toLevel = is_null($toLevel)
177 ? $this->transactions - 1
178 : $toLevel;
179
180 if ($toLevel < 0 || $toLevel >= $this->transactions) {
181 return;
182 }
183
184 try {
185 $this->performRollBack($toLevel);
186 } catch (Throwable $e) {
187 $this->handleRollBackException($e);
188 }
189
190 $this->transactions = $toLevel;
191
192 if ($this->transactionsManager) {
193 $this->transactionsManager->rollback(
194 $this->getName(),
195 $this->transactions
196 );
197 }
198
199 $this->fireConnectionEvent('rollingBack');
200 }
201
202 /**
203 * Perform rollback.
204 */
205 protected function performRollBack($toLevel)
206 {
207 if ($toLevel === 0) {
208
209 // Defensive: only ROLLBACK if we actually have an active
210 // transaction, and only decrement the counter if the SQL
211 // succeeded — protects against counter drift if the
212 // ROLLBACK silently fails (e.g., connection drop).
213 if ($this->inTransaction()) {
214 $transaction = $this->unprepared('ROLLBACK;');
215
216 if ($transaction !== false) {
217 $this->transactions--;
218 }
219 }
220
221 } elseif ($this->queryGrammar->supportsSavepoints()) {
222
223 $name = $this->getSavepointName($toLevel + 1);
224 $this->unprepared(
225 $this->queryGrammar->compileSavepointRollBack($name) . ';'
226 );
227 }
228 }
229
230 /**
231 * Handle transaction exception.
232 */
233 protected function handleTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
234 {
235 $this->rollBack();
236 throw $e;
237 }
238
239 /**
240 * Handle commit exception.
241 */
242 protected function handleCommitTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
243 {
244 $this->transactions = max(0, $this->transactions - 1);
245
246 if ($this->causedByLostConnection($e)) {
247 $this->transactions = 0;
248 }
249
250 throw $e;
251 }
252
253 /**
254 * Handle begin exception.
255 */
256 protected function handleBeginTransactionException(Throwable $e)
257 {
258 if ($this->causedByLostConnection($e)) {
259 $this->reconnect();
260 } else {
261 throw $e;
262 }
263 }
264
265 /**
266 * Handle rollback exception.
267 */
268 protected function handleRollBackException(Throwable $e)
269 {
270 if ($this->causedByLostConnection($e)) {
271 $this->transactions = 0;
272 }
273
274 throw $e;
275 }
276
277 /**
278 * Get savepoint name.
279 */
280 protected function getSavepointName($level)
281 {
282 return 'trans' . $level;
283 }
284
285 /**
286 * Transaction nesting level.
287 */
288 public function transactionLevel()
289 {
290 return $this->transactions;
291 }
292
293 /**
294 * After commit callback.
295 */
296 public function afterCommit($callback)
297 {
298 if ($this->transactionsManager) {
299 return $this->transactionsManager->addCallback($callback);
300 }
301
302 throw new RuntimeException('Transactions Manager has not been set.');
303 }
304
305 /**
306 * In transaction?
307 */
308 public function inTransaction()
309 {
310 return $this->transactions > 0;
311 }
312
313 /**
314 * Set transaction manager.
315 */
316 public function setTransactionManager($manager)
317 {
318 $this->transactionsManager = $manager;
319 return $this;
320 }
321
322 /**
323 * Unset transaction manager.
324 */
325 public function unsetTransactionManager()
326 {
327 $this->transactionsManager = null;
328 }
329 }
330