Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions system/Database/BaseConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,14 @@ public function transStatus(): bool
return $this->transStatus;
}

/**
* Checks whether this connection is inside an active transaction.
*/
public function inTransaction(): bool
{
return $this->transDepth > 0;
}

/**
* Register a callback to run after the outermost transaction commits.
*
Expand Down
5 changes: 5 additions & 0 deletions system/Database/ConnectionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ public function query(string $sql, $binds = null);
*/
public function simpleQuery(string $sql);

/**
* Checks whether this connection is inside an active CodeIgniter-managed transaction.
*/
public function inTransaction(): bool;

/**
* Register a callback to run after the outermost transaction commits.
*
Expand Down
70 changes: 70 additions & 0 deletions tests/system/Database/BaseConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,72 @@ public function testGetSessionTimezoneWithoutTimezoneKey(): void
$this->assertNull($result);
}

public function testInTransactionReflectsManagedTransactionState(): void
{
$db = new MockConnection($this->options);

$this->assertFalse($db->inTransaction());

$this->assertTrue($db->transBegin());
$this->assertTrue($db->inTransaction());

$this->assertTrue($db->transBegin());
$this->assertTrue($db->inTransaction());

$this->assertTrue($db->transCommit());
$this->assertTrue($db->inTransaction());

$this->assertTrue($db->transCommit());
$this->assertFalse($db->inTransaction());
}

public function testInTransactionReturnsFalseWhenTransactionsAreDisabled(): void
{
$db = new MockConnection($this->options);

$db->transOff();

$this->assertFalse($db->transBegin());
$this->assertFalse($db->inTransaction());
}

public function testInTransactionReturnsTrueInsideTransactionCallback(): void
{
$db = new MockConnection($this->options);
$state = null;

$result = $db->transaction(static function (BaseConnection $connection) use (&$state): string {
$state = $connection->inTransaction();

return 'done';
});

$this->assertSame('done', $result);
$this->assertTrue($state);
$this->assertFalse($db->inTransaction());
}

public function testInTransactionReturnsFalseInsideTransactionCallbacks(): void
{
$db = new MockConnection($this->options);
$commitState = null;
$rollbackState = null;

$this->assertTrue($db->transBegin());
$db->afterCommit(static function () use ($db, &$commitState): void {
$commitState = $db->inTransaction();
});
$this->assertTrue($db->transCommit());
$this->assertFalse($commitState);

$this->assertTrue($db->transBegin());
$db->afterRollback(static function () use ($db, &$rollbackState): void {
$rollbackState = $db->inTransaction();
});
$this->assertTrue($db->transRollback());
$this->assertFalse($rollbackState);
}

public function testAfterCommitCallbacksRemainQueuedWhenDriverCommitFails(): void
{
$callbacks = [];
Expand All @@ -581,10 +647,12 @@ protected function _transCommit(): bool
$this->assertFalse($db->transCommit());
$this->assertSame([], $callbacks);
$this->assertSame(1, $db->transDepth);
$this->assertTrue($db->inTransaction());

$this->assertTrue($db->transCommit());
$this->assertSame(['committed'], $callbacks);
$this->assertSame(0, $db->transDepth);
$this->assertFalse($db->inTransaction());

$this->assertTrue($db->transBegin());
$this->assertTrue($db->transCommit());
Expand Down Expand Up @@ -614,10 +682,12 @@ protected function _transRollback(): bool
$this->assertFalse($db->transRollback());
$this->assertSame([], $callbacks);
$this->assertSame(1, $db->transDepth);
$this->assertTrue($db->inTransaction());

$this->assertTrue($db->transRollback());
$this->assertSame(['rolled back'], $callbacks);
$this->assertSame(0, $db->transDepth);
$this->assertFalse($db->inTransaction());

$this->assertTrue($db->transBegin());
$this->assertTrue($db->transRollback());
Expand Down
3 changes: 2 additions & 1 deletion user_guide_src/source/changelogs/v4.8.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Interface Changes
**NOTE:** If you've implemented your own classes that implement these interfaces from scratch, you will need to
update your implementations to include the new methods or method changes to ensure compatibility.

- **Database:** ``CodeIgniter\Database\ConnectionInterface`` now requires the ``afterCommit()``, ``afterRollback()``, and ``transaction()`` methods.
- **Database:** ``CodeIgniter\Database\ConnectionInterface`` now requires the ``afterCommit()``, ``afterRollback()``, ``inTransaction()``, and ``transaction()`` methods.
- **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.
- **Security:** The ``SecurityInterface``'s ``verify()`` method now has a native return type of ``static``.

Expand Down Expand Up @@ -198,6 +198,7 @@ Database

- 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`.
- Added the ``transaction()`` method to database connections to run a callback inside a transaction. See :ref:`transactions-closure`.
- Added ``inTransaction()`` to database connections to check whether the connection is inside an active CodeIgniter-managed transaction. See :ref:`transactions-checking-transaction-state`.
- 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.

Query Builder
Expand Down
21 changes: 21 additions & 0 deletions user_guide_src/source/database/transactions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,27 @@ Rollback callbacks also run when CodeIgniter automatically rolls back an active
transaction while handling a transaction failure or cleaning up an unfinished
transaction.

.. _transactions-checking-transaction-state:

Checking Transaction State
==========================

.. versionadded:: 4.8.0

You may use ``inTransaction()`` to check whether the connection is currently
inside an active CodeIgniter-managed transaction.

It returns ``false`` when no CodeIgniter-managed transaction is active,
including when transactions are disabled.

This is useful for services or libraries that need to adapt their behavior when
they are called from inside an existing transaction.

.. note:: ``inTransaction()`` reflects transactions started through
CodeIgniter's transaction methods. If you start or end transactions through
raw SQL or driver-specific APIs, CodeIgniter may not know about that
transaction state.

Disabling Transactions
======================

Expand Down
Loading