Skip to content

Commit e1b5cb4

Browse files
committed
feat: add transaction lifecycle callbacks
- Add afterCommit() and afterRollback() callbacks to database connections - Run callbacks only after the outermost transaction commits or rolls back - Discard callbacks registered for the opposite transaction outcome - Document callback exception behavior and automatic rollback handling - Add live database coverage for callback ordering, nesting, and failures Signed-off-by: memleakd <[email protected]>
1 parent 2db0ed7 commit e1b5cb4

5 files changed

Lines changed: 411 additions & 7 deletions

File tree

system/Database/BaseConnection.php

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,20 @@ abstract class BaseConnection implements ConnectionInterface
358358
*/
359359
protected bool $transException = false;
360360

361+
/**
362+
* Callbacks to run after the outermost transaction commits.
363+
*
364+
* @var list<callable(): void>
365+
*/
366+
protected array $transCommitCallbacks = [];
367+
368+
/**
369+
* Callbacks to run after the outermost transaction rolls back.
370+
*
371+
* @var list<callable(): void>
372+
*/
373+
protected array $transRollbackCallbacks = [];
374+
361375
/**
362376
* Array of table aliases.
363377
*
@@ -985,13 +999,15 @@ public function transComplete(): bool
985999

9861000
// The query() function will set this flag to FALSE in the event that a query failed
9871001
if ($this->transStatus === false || $this->transFailure === true) {
988-
$this->transRollback();
989-
990-
// If we are NOT running in strict mode, we will reset
991-
// the _trans_status flag so that subsequent groups of
992-
// transactions will be permitted.
993-
if ($this->transStrict === false) {
994-
$this->transStatus = true;
1002+
try {
1003+
$this->transRollback();
1004+
} finally {
1005+
// If we are NOT running in strict mode, we will reset
1006+
// the _trans_status flag so that subsequent groups of
1007+
// transactions will be permitted.
1008+
if ($this->transStrict === false) {
1009+
$this->transStatus = true;
1010+
}
9951011
}
9961012

9971013
return false;
@@ -1008,6 +1024,48 @@ public function transStatus(): bool
10081024
return $this->transStatus;
10091025
}
10101026

1027+
/**
1028+
* Register a callback to run after the outermost transaction commits.
1029+
*
1030+
* If no transaction is active, the callback runs immediately.
1031+
*
1032+
* @param callable(): void $callback
1033+
*
1034+
* @return $this
1035+
*/
1036+
public function afterCommit(callable $callback): static
1037+
{
1038+
if ($this->transDepth === 0) {
1039+
$callback();
1040+
1041+
return $this;
1042+
}
1043+
1044+
$this->transCommitCallbacks[] = $callback;
1045+
1046+
return $this;
1047+
}
1048+
1049+
/**
1050+
* Register a callback to run after the outermost transaction rolls back.
1051+
*
1052+
* If no transaction is active, the callback is not run.
1053+
*
1054+
* @param callable(): void $callback
1055+
*
1056+
* @return $this
1057+
*/
1058+
public function afterRollback(callable $callback): static
1059+
{
1060+
if ($this->transDepth === 0) {
1061+
return $this;
1062+
}
1063+
1064+
$this->transRollbackCallbacks[] = $callback;
1065+
1066+
return $this;
1067+
}
1068+
10111069
/**
10121070
* Begin Transaction
10131071
*/
@@ -1055,6 +1113,11 @@ public function transCommit(): bool
10551113
if ($this->transDepth > 1 || $this->_transCommit()) {
10561114
$this->transDepth--;
10571115

1116+
if ($this->transDepth === 0) {
1117+
$this->transRollbackCallbacks = [];
1118+
$this->runTransCommitCallbacks();
1119+
}
1120+
10581121
return true;
10591122
}
10601123

@@ -1074,6 +1137,11 @@ public function transRollback(): bool
10741137
if ($this->transDepth > 1 || $this->_transRollback()) {
10751138
$this->transDepth--;
10761139

1140+
if ($this->transDepth === 0) {
1141+
$this->transCommitCallbacks = [];
1142+
$this->runTransRollbackCallbacks();
1143+
}
1144+
10771145
return true;
10781146
}
10791147

@@ -1102,6 +1170,32 @@ public function handleTransStatus(): void
11021170
}
11031171
}
11041172

1173+
/**
1174+
* Run and clear callbacks registered for a successful transaction commit.
1175+
*/
1176+
protected function runTransCommitCallbacks(): void
1177+
{
1178+
$callbacks = $this->transCommitCallbacks;
1179+
$this->transCommitCallbacks = [];
1180+
1181+
foreach ($callbacks as $callback) {
1182+
$callback();
1183+
}
1184+
}
1185+
1186+
/**
1187+
* Run and clear callbacks registered for a transaction rollback.
1188+
*/
1189+
protected function runTransRollbackCallbacks(): void
1190+
{
1191+
$callbacks = $this->transRollbackCallbacks;
1192+
$this->transRollbackCallbacks = [];
1193+
1194+
foreach ($callbacks as $callback) {
1195+
$callback();
1196+
}
1197+
}
1198+
11051199
/**
11061200
* Begin Transaction
11071201
*/

0 commit comments

Comments
 (0)