Skip to content

Commit 856c1d1

Browse files
committed
feat: add database transaction state check
Signed-off-by: memleakd <[email protected]>
1 parent 2cc937a commit 856c1d1

5 files changed

Lines changed: 106 additions & 1 deletion

File tree

system/Database/BaseConnection.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,14 @@ public function transStatus(): bool
10241024
return $this->transStatus;
10251025
}
10261026

1027+
/**
1028+
* Checks whether this connection is inside an active transaction.
1029+
*/
1030+
public function inTransaction(): bool
1031+
{
1032+
return $this->transDepth > 0;
1033+
}
1034+
10271035
/**
10281036
* Register a callback to run after the outermost transaction commits.
10291037
*

system/Database/ConnectionInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ public function query(string $sql, $binds = null);
115115
*/
116116
public function simpleQuery(string $sql);
117117

118+
/**
119+
* Checks whether this connection is inside an active CodeIgniter-managed transaction.
120+
*/
121+
public function inTransaction(): bool;
122+
118123
/**
119124
* Register a callback to run after the outermost transaction commits.
120125
*

tests/system/Database/BaseConnectionTest.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,72 @@ public function testGetSessionTimezoneWithoutTimezoneKey(): void
558558
$this->assertNull($result);
559559
}
560560

561+
public function testInTransactionReflectsManagedTransactionState(): void
562+
{
563+
$db = new MockConnection($this->options);
564+
565+
$this->assertFalse($db->inTransaction());
566+
567+
$this->assertTrue($db->transBegin());
568+
$this->assertTrue($db->inTransaction());
569+
570+
$this->assertTrue($db->transBegin());
571+
$this->assertTrue($db->inTransaction());
572+
573+
$this->assertTrue($db->transCommit());
574+
$this->assertTrue($db->inTransaction());
575+
576+
$this->assertTrue($db->transCommit());
577+
$this->assertFalse($db->inTransaction());
578+
}
579+
580+
public function testInTransactionReturnsFalseWhenTransactionsAreDisabled(): void
581+
{
582+
$db = new MockConnection($this->options);
583+
584+
$db->transOff();
585+
586+
$this->assertFalse($db->transBegin());
587+
$this->assertFalse($db->inTransaction());
588+
}
589+
590+
public function testInTransactionReturnsTrueInsideTransactionCallback(): void
591+
{
592+
$db = new MockConnection($this->options);
593+
$state = null;
594+
595+
$result = $db->transaction(static function (BaseConnection $connection) use (&$state): string {
596+
$state = $connection->inTransaction();
597+
598+
return 'done';
599+
});
600+
601+
$this->assertSame('done', $result);
602+
$this->assertTrue($state);
603+
$this->assertFalse($db->inTransaction());
604+
}
605+
606+
public function testInTransactionReturnsFalseInsideTransactionCallbacks(): void
607+
{
608+
$db = new MockConnection($this->options);
609+
$commitState = null;
610+
$rollbackState = null;
611+
612+
$this->assertTrue($db->transBegin());
613+
$db->afterCommit(static function () use ($db, &$commitState): void {
614+
$commitState = $db->inTransaction();
615+
});
616+
$this->assertTrue($db->transCommit());
617+
$this->assertFalse($commitState);
618+
619+
$this->assertTrue($db->transBegin());
620+
$db->afterRollback(static function () use ($db, &$rollbackState): void {
621+
$rollbackState = $db->inTransaction();
622+
});
623+
$this->assertTrue($db->transRollback());
624+
$this->assertFalse($rollbackState);
625+
}
626+
561627
public function testAfterCommitCallbacksRemainQueuedWhenDriverCommitFails(): void
562628
{
563629
$callbacks = [];
@@ -581,10 +647,12 @@ protected function _transCommit(): bool
581647
$this->assertFalse($db->transCommit());
582648
$this->assertSame([], $callbacks);
583649
$this->assertSame(1, $db->transDepth);
650+
$this->assertTrue($db->inTransaction());
584651

585652
$this->assertTrue($db->transCommit());
586653
$this->assertSame(['committed'], $callbacks);
587654
$this->assertSame(0, $db->transDepth);
655+
$this->assertFalse($db->inTransaction());
588656

589657
$this->assertTrue($db->transBegin());
590658
$this->assertTrue($db->transCommit());
@@ -614,10 +682,12 @@ protected function _transRollback(): bool
614682
$this->assertFalse($db->transRollback());
615683
$this->assertSame([], $callbacks);
616684
$this->assertSame(1, $db->transDepth);
685+
$this->assertTrue($db->inTransaction());
617686

618687
$this->assertTrue($db->transRollback());
619688
$this->assertSame(['rolled back'], $callbacks);
620689
$this->assertSame(0, $db->transDepth);
690+
$this->assertFalse($db->inTransaction());
621691

622692
$this->assertTrue($db->transBegin());
623693
$this->assertTrue($db->transRollback());

user_guide_src/source/changelogs/v4.8.0.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Interface Changes
4343
**NOTE:** If you've implemented your own classes that implement these interfaces from scratch, you will need to
4444
update your implementations to include the new methods or method changes to ensure compatibility.
4545

46-
- **Database:** ``CodeIgniter\Database\ConnectionInterface`` now requires the ``afterCommit()``, ``afterRollback()``, and ``transaction()`` methods.
46+
- **Database:** ``CodeIgniter\Database\ConnectionInterface`` now requires the ``afterCommit()``, ``afterRollback()``, ``inTransaction()``, and ``transaction()`` methods.
4747
- **Logging:** ``CodeIgniter\Log\Handlers\HandlerInterface::handle()`` now requires a third parameter ``array $context = []``. Any custom log handler that overrides ``handle()`` - whether implementing ``HandlerInterface`` directly or extending a built-in handler class - must add the parameter to its ``handle()`` method signature.
4848
- **Security:** The ``SecurityInterface``'s ``verify()`` method now has a native return type of ``static``.
4949

@@ -198,6 +198,7 @@ Database
198198

199199
- Added ``afterCommit()`` and ``afterRollback()`` transaction callbacks to database connections. These callbacks run after the outermost transaction commits or rolls back. See :ref:`transactions-transaction-callbacks`.
200200
- Added the ``transaction()`` method to database connections to run a callback inside a transaction. See :ref:`transactions-closure`.
201+
- Added ``inTransaction()`` to database connections to check whether the connection is inside an active CodeIgniter-managed transaction. See :ref:`transactions-checking-transaction-state`.
201202
- Added ``trustServerCertificate`` option to ``SQLSRV`` database connections in ``Config\Database``. Set it to ``true`` to trust the server certificate without CA validation when using encrypted connections.
202203

203204
Query Builder

user_guide_src/source/database/transactions.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,27 @@ Rollback callbacks also run when CodeIgniter automatically rolls back an active
192192
transaction while handling a transaction failure or cleaning up an unfinished
193193
transaction.
194194

195+
.. _transactions-checking-transaction-state:
196+
197+
Checking Transaction State
198+
==========================
199+
200+
.. versionadded:: 4.8.0
201+
202+
You may use ``inTransaction()`` to check whether the connection is currently
203+
inside an active CodeIgniter-managed transaction.
204+
205+
It returns ``false`` when no CodeIgniter-managed transaction is active,
206+
including when transactions are disabled.
207+
208+
This is useful for services or libraries that need to adapt their behavior when
209+
they are called from inside an existing transaction.
210+
211+
.. note:: ``inTransaction()`` reflects transactions started through
212+
CodeIgniter's transaction methods. If you start or end transactions through
213+
raw SQL or driver-specific APIs, CodeIgniter may not know about that
214+
transaction state.
215+
195216
Disabling Transactions
196217
======================
197218

0 commit comments

Comments
 (0)