From 654fe85fad7f12b5c4019dccf8967d6a6236ee70 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Tue, 5 May 2026 19:07:34 +0530 Subject: [PATCH 01/11] feat: Added support for callable for TTL in Cache Handlers --- system/Cache/CacheInterface.php | 8 ++--- system/Cache/Handlers/ApcuHandler.php | 6 +++- system/Cache/Handlers/BaseHandler.php | 13 +++++-- system/Cache/Handlers/DummyHandler.php | 2 +- system/Test/Mock/MockCache.php | 13 +++++-- .../system/Cache/Handlers/ApcuHandlerTest.php | 34 +++++++++++++++++++ .../Cache/Handlers/DummyHandlerTest.php | 14 ++++++++ .../system/Cache/Handlers/FileHandlerTest.php | 34 +++++++++++++++++++ .../Cache/Handlers/MemcachedHandlerTest.php | 34 +++++++++++++++++++ .../Cache/Handlers/PredisHandlerTest.php | 34 +++++++++++++++++++ .../Cache/Handlers/RedisHandlerTest.php | 34 +++++++++++++++++++ user_guide_src/source/changelogs/v4.8.0.rst | 2 ++ user_guide_src/source/libraries/caching.rst | 29 ++++++++++++++-- .../source/libraries/caching/015.php | 11 ++++++ 14 files changed, 255 insertions(+), 13 deletions(-) create mode 100644 user_guide_src/source/libraries/caching/015.php diff --git a/system/Cache/CacheInterface.php b/system/Cache/CacheInterface.php index 4cedb67e9538..464b1e5f03b9 100644 --- a/system/Cache/CacheInterface.php +++ b/system/Cache/CacheInterface.php @@ -44,11 +44,11 @@ public function save(string $key, mixed $value, int $ttl = 60): bool; * Attempts to get an item from the cache, or executes the callback * and stores the result on cache miss. * - * @param string $key Cache item name - * @param int $ttl Time To Live, in seconds - * @param Closure(): mixed $callback Callback executed on cache miss + * @param string $key Cache item name + * @param callable(): int|callable(mixed $value): int|int $ttl Time To Live, in seconds + * @param Closure(): mixed $callback Callback executed on cache miss */ - public function remember(string $key, int $ttl, Closure $callback): mixed; + public function remember(string $key, callable|int $ttl, Closure $callback): mixed; /** * Deletes a specific item from the cache store. diff --git a/system/Cache/Handlers/ApcuHandler.php b/system/Cache/Handlers/ApcuHandler.php index ef0f51c50dc7..60fffb64145a 100644 --- a/system/Cache/Handlers/ApcuHandler.php +++ b/system/Cache/Handlers/ApcuHandler.php @@ -55,10 +55,14 @@ public function save(string $key, $value, int $ttl = 60): bool return apcu_store($key, $value, $ttl); } - public function remember(string $key, int $ttl, Closure $callback): mixed + public function remember(string $key, callable|int $ttl, Closure $callback): mixed { $key = static::validateKey($key, $this->prefix); + if (is_callable($ttl)) { + return parent::remember($key, $ttl, $callback); + } + return apcu_entry($key, $callback, $ttl); } diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index 2217e98e35f9..5531c72ba771 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -17,6 +17,7 @@ use CodeIgniter\Cache\CacheInterface; use CodeIgniter\Exceptions\InvalidArgumentException; use Config\Cache; +use ReflectionFunction; /** * Base class for cache handling @@ -64,7 +65,7 @@ public static function validateKey($key, $prefix = ''): string return strlen($prefix . $key) > static::MAX_KEY_LENGTH ? $prefix . md5($key) : $prefix . $key; } - public function remember(string $key, int $ttl, Closure $callback): mixed + public function remember(string $key, callable|int $ttl, Closure $callback): mixed { $value = $this->get($key); @@ -72,7 +73,15 @@ public function remember(string $key, int $ttl, Closure $callback): mixed return $value; } - $this->save($key, $value = $callback(), $ttl); + $value = $callback(); + + if (is_callable($ttl)) { + $ttl = (new ReflectionFunction($ttl(...)))->getNumberOfParameters() > 0 + ? $ttl($value) + : $ttl(); + } + + $this->save($key, $value, $ttl); return $value; } diff --git a/system/Cache/Handlers/DummyHandler.php b/system/Cache/Handlers/DummyHandler.php index a30475965d6f..891f8ad31d3f 100644 --- a/system/Cache/Handlers/DummyHandler.php +++ b/system/Cache/Handlers/DummyHandler.php @@ -31,7 +31,7 @@ public function get(string $key): mixed return null; } - public function remember(string $key, int $ttl, Closure $callback): mixed + public function remember(string $key, callable|int $ttl, Closure $callback): mixed { return null; } diff --git a/system/Test/Mock/MockCache.php b/system/Test/Mock/MockCache.php index 393246a8d0a9..d3cd3dea7d60 100644 --- a/system/Test/Mock/MockCache.php +++ b/system/Test/Mock/MockCache.php @@ -20,6 +20,7 @@ use CodeIgniter\Cache\LockStoreProviderInterface; use CodeIgniter\I18n\Time; use PHPUnit\Framework\Assert; +use ReflectionFunction; class MockCache extends BaseHandler implements CacheInterface, LockStoreProviderInterface { @@ -72,7 +73,7 @@ public function get(string $key): mixed * * @return bool|null */ - public function remember(string $key, int $ttl, Closure $callback): mixed + public function remember(string $key, callable|int $ttl, Closure $callback): mixed { $value = $this->get($key); @@ -80,7 +81,15 @@ public function remember(string $key, int $ttl, Closure $callback): mixed return $value; } - $this->save($key, $value = $callback(), $ttl); + $value = $callback(); + + if (is_callable($ttl)) { + $ttl = (new ReflectionFunction($ttl(...)))->getNumberOfParameters() > 0 + ? $ttl($value) + : $ttl(); + } + + $this->save($key, $value, $ttl); return $value; } diff --git a/tests/system/Cache/Handlers/ApcuHandlerTest.php b/tests/system/Cache/Handlers/ApcuHandlerTest.php index 5d5d5f7b2f6f..7b89c3a3c26c 100644 --- a/tests/system/Cache/Handlers/ApcuHandlerTest.php +++ b/tests/system/Cache/Handlers/ApcuHandlerTest.php @@ -92,6 +92,40 @@ public function testRemember(): void $this->assertNull($this->handler->get(self::$key1)); } + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallable(): void + { + $this->handler->remember(self::$key1, static fn (): int => 2, static fn (): string => 'value'); + + $this->assertSame('value', $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallableAndValuePassed(): void + { + $this->handler->remember(self::$key1, static fn ($value): int => $value[0], static fn (): array => [2, 3]); + + $this->assertSame([2, 3], $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); diff --git a/tests/system/Cache/Handlers/DummyHandlerTest.php b/tests/system/Cache/Handlers/DummyHandlerTest.php index ab3b3ccd643d..2bdc42b5bd5d 100644 --- a/tests/system/Cache/Handlers/DummyHandlerTest.php +++ b/tests/system/Cache/Handlers/DummyHandlerTest.php @@ -48,6 +48,20 @@ public function testRemember(): void $this->assertNull($dummyHandler); } + public function testRememberWithTTLCallable(): void + { + $dummyHandler = $this->handler->remember('key', static fn (): int => 2, static fn (): string => 'value'); + + $this->assertNull($dummyHandler); + } + + public function testRememberWithTTLCallableAndValuePassed(): void + { + $dummyHandler = $this->handler->remember('key', static fn ($value): int => $value[0], static fn (): array => [2, 3]); + + $this->assertNull($dummyHandler); + } + public function testSave(): void { $this->assertTrue($this->handler->save('key', 'value')); diff --git a/tests/system/Cache/Handlers/FileHandlerTest.php b/tests/system/Cache/Handlers/FileHandlerTest.php index 16c6042be124..02f04892f996 100644 --- a/tests/system/Cache/Handlers/FileHandlerTest.php +++ b/tests/system/Cache/Handlers/FileHandlerTest.php @@ -144,6 +144,40 @@ public function testRemember(): void $this->assertNull($this->handler->get(self::$key1)); } + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallable(): void + { + $this->handler->remember(self::$key1, static fn (): int => 2, static fn (): string => 'value'); + + $this->assertSame('value', $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallableAndValuePassed(): void + { + $this->handler->remember(self::$key1, static fn ($value): int => $value[0], static fn (): array => [2, 3]); + + $this->assertSame([2, 3], $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + /** * chmod('path', 0444) does not work on Windows */ diff --git a/tests/system/Cache/Handlers/MemcachedHandlerTest.php b/tests/system/Cache/Handlers/MemcachedHandlerTest.php index 4597e95ccd12..00a8fcd88ffe 100644 --- a/tests/system/Cache/Handlers/MemcachedHandlerTest.php +++ b/tests/system/Cache/Handlers/MemcachedHandlerTest.php @@ -101,6 +101,40 @@ public function testRemember(): void $this->assertNull($this->handler->get(self::$key1)); } + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallable(): void + { + $this->handler->remember(self::$key1, static fn (): int => 2, static fn (): string => 'value'); + + $this->assertSame('value', $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallableAndValuePassed(): void + { + $this->handler->remember(self::$key1, static fn ($value): int => $value[0], static fn (): array => [2, 3]); + + $this->assertSame([2, 3], $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); diff --git a/tests/system/Cache/Handlers/PredisHandlerTest.php b/tests/system/Cache/Handlers/PredisHandlerTest.php index 3b317df2299e..f09bd3ff8495 100644 --- a/tests/system/Cache/Handlers/PredisHandlerTest.php +++ b/tests/system/Cache/Handlers/PredisHandlerTest.php @@ -109,6 +109,40 @@ public function testRemember(): void $this->assertNull($this->handler->get(self::$key1)); } + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallable(): void + { + $this->handler->remember(self::$key1, static fn (): int => 2, static fn (): string => 'value'); + + $this->assertSame('value', $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallableAndValuePassed(): void + { + $this->handler->remember(self::$key1, static fn ($value): int => $value[0], static fn (): array => [2, 3]); + + $this->assertSame([2, 3], $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); diff --git a/tests/system/Cache/Handlers/RedisHandlerTest.php b/tests/system/Cache/Handlers/RedisHandlerTest.php index 72a66f04df1b..f4ecd4d5cc4b 100644 --- a/tests/system/Cache/Handlers/RedisHandlerTest.php +++ b/tests/system/Cache/Handlers/RedisHandlerTest.php @@ -110,6 +110,40 @@ public function testRemember(): void $this->assertNull($this->handler->get(self::$key1)); } + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallable(): void + { + $this->handler->remember(self::$key1, static fn (): int => 2, static fn (): string => 'value'); + + $this->assertSame('value', $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + + /** + * This test waits for 3 seconds before last assertion so this + * is naturally a "slow" test on the perspective of the default limit. + * + * @timeLimit 3.5 + */ + public function testRememberWithTTLCallableAndValuePassed(): void + { + $this->handler->remember(self::$key1, static fn ($value): int => $value[0], static fn (): array => [2, 3]); + + $this->assertSame([2, 3], $this->handler->get(self::$key1)); + $this->assertNull($this->handler->get(self::$dummy)); + + CLI::wait(3); + $this->assertNull($this->handler->get(self::$key1)); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 861b0f3d7a31..37d4d35e4adb 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -46,6 +46,7 @@ update your implementations to include the new methods or method changes to ensu - **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``. +- **Cache:** ``CodeIgniter\Cache\CacheInterface::remember()`` now accepts a TTL callable. All built-in cache handlers inherit this method via ``BaseHandler``, so no changes are required for them. Method Signature Changes ======================== @@ -230,6 +231,7 @@ Model Libraries ========= +- **Cache:** Added support for TTL callables in the ``remember()`` method of cache handlers. This allows you to specify a callable that returns a TTL value, which can be useful for dynamic TTL. See :ref:`cache-ttl-callables` for details. - **Context**: This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See :ref:`Context ` for details. - **Images:**: Added support for the AVIF file format. - **Locks:** Added :doc:`Atomic Locks ` for owner-aware, cross-process mutual exclusion backed by supported cache handlers: **File**, **Redis**, **Predis**, and **Memcached**. Memcached support has driver-specific release limitations because Memcached has no atomic compare-and-delete command. diff --git a/user_guide_src/source/libraries/caching.rst b/user_guide_src/source/libraries/caching.rst index b208d63499df..cb898c22d67c 100644 --- a/user_guide_src/source/libraries/caching.rst +++ b/user_guide_src/source/libraries/caching.rst @@ -128,10 +128,10 @@ Class Reference .. literalinclude:: caching/003.php - .. php:method:: remember(string $key, int $ttl, Closure $callback) + .. php:method:: remember(string $key, callable|int $ttl, Closure $callback) :param string $key: Cache item name - :param int $ttl: Time to live in seconds + :param callable|int $ttl: Time to live in seconds :param Closure $callback: Callback to invoke when the cache item returns null :returns: The value of the cache item :rtype: mixed @@ -139,6 +139,29 @@ Class Reference Gets an item from the cache. If ``null`` was returned, this will invoke the callback and save the result. Either way, this will return the value. + The ``$ttl`` parameter may also be a callable, allowing the TTL to be + determined dynamically at runtime. This is especially useful when the + expiration time depends on the computed value or requires an expensive + calculation. + + When a callable is provided, it will only be executed on a cache miss, + after the callback has been invoked. The callable may optionally accept + the computed value as its first argument: + + .. literalinclude:: caching/015.php + + This ensures that TTL computation is deferred until necessary and avoids + unnecessary overhead when the cache item already exists. + + .. note:: Prior to v4.8.0, the second parameter only accepted an integer TTL value. The ability to pass a callable was added in v4.8.0. + + .. note:: When using the APCu cache handler, providing a callable TTL disables + the use of ``apcu_entry()`` and falls back to a manual cache retrieval + and storage process. As a result, the operation is no longer atomic + and may be subject to race conditions under high concurrency. + + If atomic behavior is required, use an integer TTL value. + .. php:method:: save(string $key, $data[, int $ttl = 60]) :param string $key: Cache item name @@ -276,7 +299,7 @@ Drivers APCu Caching ============ -APCu is an in-memory key-value store for PHP. +APCu is an in-memory key-value store for PHP. To use it, you need the `APCu PHP extension `_. diff --git a/user_guide_src/source/libraries/caching/015.php b/user_guide_src/source/libraries/caching/015.php new file mode 100644 index 000000000000..cb7f6ccb36f5 --- /dev/null +++ b/user_guide_src/source/libraries/caching/015.php @@ -0,0 +1,11 @@ +remember('key', static fn () => 60, static fn () => fetchData()); + +// Value-aware TTL +$cache->remember( + 'key', + static fn ($value) => $value->expires_at - time(), + static fn () => fetchData(), +); From 01679b860180ebbe765289c61d0f9949002f9910 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Tue, 5 May 2026 20:06:05 +0530 Subject: [PATCH 02/11] remove redundant reference in changelog --- user_guide_src/source/changelogs/v4.8.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 37d4d35e4adb..4d98b2dc6961 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -231,7 +231,7 @@ Model Libraries ========= -- **Cache:** Added support for TTL callables in the ``remember()`` method of cache handlers. This allows you to specify a callable that returns a TTL value, which can be useful for dynamic TTL. See :ref:`cache-ttl-callables` for details. +- **Cache:** Added support for TTL callables in the ``remember()`` method of cache handlers. This allows you to specify a callable that returns a TTL value, which can be useful for dynamic TTL. - **Context**: This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See :ref:`Context ` for details. - **Images:**: Added support for the AVIF file format. - **Locks:** Added :doc:`Atomic Locks ` for owner-aware, cross-process mutual exclusion backed by supported cache handlers: **File**, **Redis**, **Predis**, and **Memcached**. Memcached support has driver-specific release limitations because Memcached has no atomic compare-and-delete command. From c9467e4f0e1b192f86040a8651c1bfd04e1aecad Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 6 May 2026 09:35:34 +0530 Subject: [PATCH 03/11] fix Psalm error --- system/Cache/Handlers/BaseHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index 5531c72ba771..121fbcc8f81f 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -76,7 +76,7 @@ public function remember(string $key, callable|int $ttl, Closure $callback): mix $value = $callback(); if (is_callable($ttl)) { - $ttl = (new ReflectionFunction($ttl(...)))->getNumberOfParameters() > 0 + $ttl = (new ReflectionFunction(Closure::fromCallable($ttl)))->getNumberOfParameters() > 0 ? $ttl($value) : $ttl(); } From 452fd201512be0c01bd32583103ef5163d1f17d4 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 6 May 2026 10:14:25 +0530 Subject: [PATCH 04/11] Add explicit types and throw exception if number of required args > 1 --- system/Cache/CacheInterface.php | 6 +++--- system/Cache/Handlers/BaseHandler.php | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/system/Cache/CacheInterface.php b/system/Cache/CacheInterface.php index 464b1e5f03b9..2b61b7d8512f 100644 --- a/system/Cache/CacheInterface.php +++ b/system/Cache/CacheInterface.php @@ -44,9 +44,9 @@ public function save(string $key, mixed $value, int $ttl = 60): bool; * Attempts to get an item from the cache, or executes the callback * and stores the result on cache miss. * - * @param string $key Cache item name - * @param callable(): int|callable(mixed $value): int|int $ttl Time To Live, in seconds - * @param Closure(): mixed $callback Callback executed on cache miss + * @param string $key Cache item name + * @param callable(): int|callable(mixed): int|int $ttl Time To Live, in seconds + * @param Closure(): mixed $callback Callback executed on cache miss */ public function remember(string $key, callable|int $ttl, Closure $callback): mixed; diff --git a/system/Cache/Handlers/BaseHandler.php b/system/Cache/Handlers/BaseHandler.php index 121fbcc8f81f..597ac5b0d31e 100644 --- a/system/Cache/Handlers/BaseHandler.php +++ b/system/Cache/Handlers/BaseHandler.php @@ -76,9 +76,22 @@ public function remember(string $key, callable|int $ttl, Closure $callback): mix $value = $callback(); if (is_callable($ttl)) { - $ttl = (new ReflectionFunction(Closure::fromCallable($ttl)))->getNumberOfParameters() > 0 - ? $ttl($value) - : $ttl(); + $ttlClosure = Closure::fromCallable($ttl); + $rf = new ReflectionFunction($ttlClosure); + $params = $rf->getNumberOfRequiredParameters(); + + if ($params === 0) { + /** @var Closure(): int $ttlClosure */ + $ttl = $ttlClosure(); + } elseif ($params === 1) { + /** @var Closure(mixed): int $ttlClosure */ + $ttl = $ttlClosure($value); + } else { + throw new InvalidArgumentException(sprintf( + 'Argument #2 ($ttl) must accept 0 or 1 parameter, %d given.', + $params, + )); + } } $this->save($key, $value, $ttl); From 21a08787d4762f6e533562fb93ec838b85c93af5 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 6 May 2026 10:21:33 +0530 Subject: [PATCH 05/11] rebase --- system/Test/Mock/MockCache.php | 20 +++++++++++++++++--- user_guide_src/source/changelogs/v4.8.0.rst | 12 +++--------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/system/Test/Mock/MockCache.php b/system/Test/Mock/MockCache.php index d3cd3dea7d60..dbeebe0e6faf 100644 --- a/system/Test/Mock/MockCache.php +++ b/system/Test/Mock/MockCache.php @@ -18,6 +18,7 @@ use CodeIgniter\Cache\Handlers\BaseHandler; use CodeIgniter\Cache\LockStoreInterface; use CodeIgniter\Cache\LockStoreProviderInterface; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\I18n\Time; use PHPUnit\Framework\Assert; use ReflectionFunction; @@ -84,9 +85,22 @@ public function remember(string $key, callable|int $ttl, Closure $callback): mix $value = $callback(); if (is_callable($ttl)) { - $ttl = (new ReflectionFunction($ttl(...)))->getNumberOfParameters() > 0 - ? $ttl($value) - : $ttl(); + $ttlClosure = Closure::fromCallable($ttl); + $rf = new ReflectionFunction($ttlClosure); + $params = $rf->getNumberOfRequiredParameters(); + + if ($params === 0) { + /** @var Closure(): int $ttlClosure */ + $ttl = $ttlClosure(); + } elseif ($params === 1) { + /** @var Closure(mixed): int $ttlClosure */ + $ttl = $ttlClosure($value); + } else { + throw new InvalidArgumentException(sprintf( + 'Argument #2 ($ttl) must accept 0 or 1 parameter, %d given.', + $params, + )); + } } $this->save($key, $value, $ttl); diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 4d98b2dc6961..fc36f12e7bd1 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -43,10 +43,10 @@ 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()``, ``inTransaction()``, and ``transaction()`` methods. +- **Cache:** ``CodeIgniter\Cache\CacheInterface::remember()`` now accepts a TTL callable. All built-in cache handlers inherit this method via ``BaseHandler``, so no changes are required for them. +- **Database:** ``CodeIgniter\Database\ConnectionInterface`` now requires the ``afterCommit()``, ``afterRollback()``, 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``. -- **Cache:** ``CodeIgniter\Cache\CacheInterface::remember()`` now accepts a TTL callable. All built-in cache handlers inherit this method via ``BaseHandler``, so no changes are required for them. Method Signature Changes ======================== @@ -203,14 +203,11 @@ 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 ------------- -- Added ``whereColumn()`` and ``orWhereColumn()`` to compare one column to another column while protecting identifiers by default. See :ref:`query-builder-where-column`. - Forge ----- @@ -234,7 +231,7 @@ Libraries - **Cache:** Added support for TTL callables in the ``remember()`` method of cache handlers. This allows you to specify a callable that returns a TTL value, which can be useful for dynamic TTL. - **Context**: This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See :ref:`Context ` for details. - **Images:**: Added support for the AVIF file format. -- **Locks:** Added :doc:`Atomic Locks ` for owner-aware, cross-process mutual exclusion backed by supported cache handlers: **File**, **Redis**, **Predis**, and **Memcached**. Memcached support has driver-specific release limitations because Memcached has no atomic compare-and-delete command. +- **Locks:** Added :doc:`Atomic Locks ` for owner-aware, cross-process mutual exclusion backed by supported cache handlers. - **Logging:** Log handlers now receive the full context array as a third argument to ``handle()``. When ``$logGlobalContext`` is enabled, the CI global context is available under the ``HandlerInterface::GLOBAL_CONTEXT_KEY`` key. Built-in handlers append it to the log output; custom handlers can use it for structured logging. - **Logging:** Added :ref:`per-call context logging ` with three new ``Config\Logger`` options (``$logContext``, ``$logContextTrace``, ``$logContextUsedKeys``). Per PSR-3, a ``Throwable`` in the ``exception`` context key is automatically normalized to a meaningful array. All options default to ``false``. @@ -284,9 +281,6 @@ Others Message Changes *************** -- Added new language key: - - ``Cache.unsupportedLockStore`` (``CacheException::forUnsupportedLockStore()``) - - Removed deprecated language keys tied to removed exception constructors: - ``Core.missingExtension`` (``FrameworkException::forMissingExtension()``) - ``HTTP.cannotSetCache`` (``DownloadException::forCannotSetCache()``) From fd7bb767bb7487772dc6a08777e7c3b8e4d9049a Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 6 May 2026 10:26:49 +0530 Subject: [PATCH 06/11] Added tests to check expection thrown --- tests/system/Cache/Handlers/ApcuHandlerTest.php | 9 +++++++++ tests/system/Cache/Handlers/DummyHandlerTest.php | 1 + tests/system/Cache/Handlers/FileHandlerTest.php | 9 +++++++++ tests/system/Cache/Handlers/MemcachedHandlerTest.php | 9 +++++++++ tests/system/Cache/Handlers/PredisHandlerTest.php | 9 +++++++++ tests/system/Cache/Handlers/RedisHandlerTest.php | 9 +++++++++ 6 files changed, 46 insertions(+) diff --git a/tests/system/Cache/Handlers/ApcuHandlerTest.php b/tests/system/Cache/Handlers/ApcuHandlerTest.php index 7b89c3a3c26c..34b61d94bd7a 100644 --- a/tests/system/Cache/Handlers/ApcuHandlerTest.php +++ b/tests/system/Cache/Handlers/ApcuHandlerTest.php @@ -15,6 +15,7 @@ use CodeIgniter\Cache\CacheFactory; use CodeIgniter\CLI\CLI; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\I18n\Time; use Config\Cache; use PHPUnit\Framework\Attributes\Group; @@ -126,6 +127,14 @@ public function testRememberWithTTLCallableAndValuePassed(): void $this->assertNull($this->handler->get(self::$key1)); } + public function testRememberWithTTLCallableAndMultipleParameters(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + + $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); diff --git a/tests/system/Cache/Handlers/DummyHandlerTest.php b/tests/system/Cache/Handlers/DummyHandlerTest.php index 2bdc42b5bd5d..df95c06c8636 100644 --- a/tests/system/Cache/Handlers/DummyHandlerTest.php +++ b/tests/system/Cache/Handlers/DummyHandlerTest.php @@ -14,6 +14,7 @@ namespace CodeIgniter\Cache\Handlers; use CodeIgniter\Cache\CacheFactory; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Test\CIUnitTestCase; use Config\Cache; use PHPUnit\Framework\Attributes\Group; diff --git a/tests/system/Cache/Handlers/FileHandlerTest.php b/tests/system/Cache/Handlers/FileHandlerTest.php index 02f04892f996..8e548a1f8d9d 100644 --- a/tests/system/Cache/Handlers/FileHandlerTest.php +++ b/tests/system/Cache/Handlers/FileHandlerTest.php @@ -16,6 +16,7 @@ use CodeIgniter\Cache\CacheFactory; use CodeIgniter\Cache\Exceptions\CacheException; use CodeIgniter\CLI\CLI; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\I18n\Time; use Config\Cache; use PHPUnit\Framework\Attributes\DataProvider; @@ -178,6 +179,14 @@ public function testRememberWithTTLCallableAndValuePassed(): void $this->assertNull($this->handler->get(self::$key1)); } + public function testRememberWithTTLCallableAndMultipleParameters(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + + $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); + } + /** * chmod('path', 0444) does not work on Windows */ diff --git a/tests/system/Cache/Handlers/MemcachedHandlerTest.php b/tests/system/Cache/Handlers/MemcachedHandlerTest.php index 00a8fcd88ffe..3b68fc1ef467 100644 --- a/tests/system/Cache/Handlers/MemcachedHandlerTest.php +++ b/tests/system/Cache/Handlers/MemcachedHandlerTest.php @@ -18,6 +18,7 @@ use CodeIgniter\Cache\LockStoreProviderInterface; use CodeIgniter\CLI\CLI; use CodeIgniter\Exceptions\BadMethodCallException; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\I18n\Time; use Config\Cache; use PHPUnit\Framework\Attributes\Group; @@ -135,6 +136,14 @@ public function testRememberWithTTLCallableAndValuePassed(): void $this->assertNull($this->handler->get(self::$key1)); } + public function testRememberWithTTLCallableAndMultipleParameters(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + + $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); diff --git a/tests/system/Cache/Handlers/PredisHandlerTest.php b/tests/system/Cache/Handlers/PredisHandlerTest.php index f09bd3ff8495..ffd895b56b4e 100644 --- a/tests/system/Cache/Handlers/PredisHandlerTest.php +++ b/tests/system/Cache/Handlers/PredisHandlerTest.php @@ -17,6 +17,7 @@ use CodeIgniter\Cache\LockStoreInterface; use CodeIgniter\Cache\LockStoreProviderInterface; use CodeIgniter\CLI\CLI; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\I18n\Time; use Config\Cache; use PHPUnit\Framework\Attributes\Group; @@ -143,6 +144,14 @@ public function testRememberWithTTLCallableAndValuePassed(): void $this->assertNull($this->handler->get(self::$key1)); } + public function testRememberWithTTLCallableAndMultipleParameters(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + + $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); diff --git a/tests/system/Cache/Handlers/RedisHandlerTest.php b/tests/system/Cache/Handlers/RedisHandlerTest.php index f4ecd4d5cc4b..7735bde2d7dc 100644 --- a/tests/system/Cache/Handlers/RedisHandlerTest.php +++ b/tests/system/Cache/Handlers/RedisHandlerTest.php @@ -17,6 +17,7 @@ use CodeIgniter\Cache\LockStoreInterface; use CodeIgniter\Cache\LockStoreProviderInterface; use CodeIgniter\CLI\CLI; +use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\I18n\Time; use Config\Cache; use PHPUnit\Framework\Attributes\DataProvider; @@ -144,6 +145,14 @@ public function testRememberWithTTLCallableAndValuePassed(): void $this->assertNull($this->handler->get(self::$key1)); } + public function testRememberWithTTLCallableAndMultipleParameters(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + + $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); + } + public function testSave(): void { $this->assertTrue($this->handler->save(self::$key1, 'value')); From 361da70a61a7fdd0433ceac475a83e98d78a74bf Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 6 May 2026 10:27:19 +0530 Subject: [PATCH 07/11] cs-fix --- tests/system/Cache/Handlers/DummyHandlerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/system/Cache/Handlers/DummyHandlerTest.php b/tests/system/Cache/Handlers/DummyHandlerTest.php index df95c06c8636..2bdc42b5bd5d 100644 --- a/tests/system/Cache/Handlers/DummyHandlerTest.php +++ b/tests/system/Cache/Handlers/DummyHandlerTest.php @@ -14,7 +14,6 @@ namespace CodeIgniter\Cache\Handlers; use CodeIgniter\Cache\CacheFactory; -use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Test\CIUnitTestCase; use Config\Cache; use PHPUnit\Framework\Attributes\Group; From fea901b2117603b6a658e76ae552eeb25dd1151c Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 6 May 2026 10:30:20 +0530 Subject: [PATCH 08/11] Added phpstan-ignore to explicitly check for exception --- tests/system/Cache/Handlers/ApcuHandlerTest.php | 1 + tests/system/Cache/Handlers/FileHandlerTest.php | 1 + tests/system/Cache/Handlers/MemcachedHandlerTest.php | 1 + tests/system/Cache/Handlers/PredisHandlerTest.php | 1 + tests/system/Cache/Handlers/RedisHandlerTest.php | 1 + 5 files changed, 5 insertions(+) diff --git a/tests/system/Cache/Handlers/ApcuHandlerTest.php b/tests/system/Cache/Handlers/ApcuHandlerTest.php index 34b61d94bd7a..be4bbaa3b1cb 100644 --- a/tests/system/Cache/Handlers/ApcuHandlerTest.php +++ b/tests/system/Cache/Handlers/ApcuHandlerTest.php @@ -132,6 +132,7 @@ public function testRememberWithTTLCallableAndMultipleParameters(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + /** @phpstan-ignore argument.type */ $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); } diff --git a/tests/system/Cache/Handlers/FileHandlerTest.php b/tests/system/Cache/Handlers/FileHandlerTest.php index 8e548a1f8d9d..1b27c4b0e834 100644 --- a/tests/system/Cache/Handlers/FileHandlerTest.php +++ b/tests/system/Cache/Handlers/FileHandlerTest.php @@ -184,6 +184,7 @@ public function testRememberWithTTLCallableAndMultipleParameters(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + /** @phpstan-ignore argument.type */ $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); } diff --git a/tests/system/Cache/Handlers/MemcachedHandlerTest.php b/tests/system/Cache/Handlers/MemcachedHandlerTest.php index 3b68fc1ef467..bb99e77197c3 100644 --- a/tests/system/Cache/Handlers/MemcachedHandlerTest.php +++ b/tests/system/Cache/Handlers/MemcachedHandlerTest.php @@ -141,6 +141,7 @@ public function testRememberWithTTLCallableAndMultipleParameters(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + /** @phpstan-ignore argument.type */ $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); } diff --git a/tests/system/Cache/Handlers/PredisHandlerTest.php b/tests/system/Cache/Handlers/PredisHandlerTest.php index ffd895b56b4e..d81fb5d02243 100644 --- a/tests/system/Cache/Handlers/PredisHandlerTest.php +++ b/tests/system/Cache/Handlers/PredisHandlerTest.php @@ -149,6 +149,7 @@ public function testRememberWithTTLCallableAndMultipleParameters(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + /** @phpstan-ignore argument.type */ $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); } diff --git a/tests/system/Cache/Handlers/RedisHandlerTest.php b/tests/system/Cache/Handlers/RedisHandlerTest.php index 7735bde2d7dc..883cd6644957 100644 --- a/tests/system/Cache/Handlers/RedisHandlerTest.php +++ b/tests/system/Cache/Handlers/RedisHandlerTest.php @@ -150,6 +150,7 @@ public function testRememberWithTTLCallableAndMultipleParameters(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Argument #2 ($ttl) must accept 0 or 1 parameter, 2 given.'); + /** @phpstan-ignore argument.type */ $this->handler->remember(self::$key1, static fn ($a, $b): int => 2, static fn (): string => 'value'); } From 287ea0462ab7c4990cf63c35922329df41135027 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Thu, 7 May 2026 10:38:41 +0530 Subject: [PATCH 09/11] fix rebase problems --- user_guide_src/source/changelogs/v4.8.0.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index fc36f12e7bd1..90349e0fe744 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -44,7 +44,7 @@ Interface Changes update your implementations to include the new methods or method changes to ensure compatibility. - **Cache:** ``CodeIgniter\Cache\CacheInterface::remember()`` now accepts a TTL callable. All built-in cache handlers inherit this method via ``BaseHandler``, so no changes are required for them. -- **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``. @@ -203,11 +203,14 @@ 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 ------------- +- Added ``whereColumn()`` and ``orWhereColumn()`` to compare one column to another column while protecting identifiers by default. See :ref:`query-builder-where-column`. + Forge ----- @@ -231,7 +234,7 @@ Libraries - **Cache:** Added support for TTL callables in the ``remember()`` method of cache handlers. This allows you to specify a callable that returns a TTL value, which can be useful for dynamic TTL. - **Context**: This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See :ref:`Context ` for details. - **Images:**: Added support for the AVIF file format. -- **Locks:** Added :doc:`Atomic Locks ` for owner-aware, cross-process mutual exclusion backed by supported cache handlers. +- **Locks:** Added :doc:`Atomic Locks ` for owner-aware, cross-process mutual exclusion backed by supported cache handlers: **File**, **Redis**, **Predis**, and **Memcached**. Memcached support has driver-specific release limitations because Memcached has no atomic compare-and-delete command. - **Logging:** Log handlers now receive the full context array as a third argument to ``handle()``. When ``$logGlobalContext`` is enabled, the CI global context is available under the ``HandlerInterface::GLOBAL_CONTEXT_KEY`` key. Built-in handlers append it to the log output; custom handlers can use it for structured logging. - **Logging:** Added :ref:`per-call context logging ` with three new ``Config\Logger`` options (``$logContext``, ``$logContextTrace``, ``$logContextUsedKeys``). Per PSR-3, a ``Throwable`` in the ``exception`` context key is automatically normalized to a meaningful array. All options default to ``false``. @@ -281,6 +284,9 @@ Others Message Changes *************** +- Added new language key: + - ``Cache.unsupportedLockStore`` (``CacheException::forUnsupportedLockStore()``) + - Removed deprecated language keys tied to removed exception constructors: - ``Core.missingExtension`` (``FrameworkException::forMissingExtension()``) - ``HTTP.cannotSetCache`` (``DownloadException::forCannotSetCache()``) From 46b0fa74074ec411dd45971354ffba9c7ee31e05 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Thu, 7 May 2026 10:41:47 +0530 Subject: [PATCH 10/11] Fix double prefixing in APCu Handler --- system/Cache/Handlers/ApcuHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/Cache/Handlers/ApcuHandler.php b/system/Cache/Handlers/ApcuHandler.php index 60fffb64145a..b8ef4f284332 100644 --- a/system/Cache/Handlers/ApcuHandler.php +++ b/system/Cache/Handlers/ApcuHandler.php @@ -57,12 +57,12 @@ public function save(string $key, $value, int $ttl = 60): bool public function remember(string $key, callable|int $ttl, Closure $callback): mixed { - $key = static::validateKey($key, $this->prefix); - if (is_callable($ttl)) { return parent::remember($key, $ttl, $callback); } + $key = static::validateKey($key, $this->prefix); + return apcu_entry($key, $callback, $ttl); } From f1912e24a51978b5afae08c423a0973fca228734 Mon Sep 17 00:00:00 2001 From: Vansh Patel Date: Thu, 7 May 2026 18:20:13 +0530 Subject: [PATCH 11/11] Make the changelog more explicit regarding custom implementation Co-authored-by: Michal Sniatala --- user_guide_src/source/changelogs/v4.8.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index 90349e0fe744..c1b1116cc890 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -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. -- **Cache:** ``CodeIgniter\Cache\CacheInterface::remember()`` now accepts a TTL callable. All built-in cache handlers inherit this method via ``BaseHandler``, so no changes are required for them. +- **Cache:** ``CodeIgniter\Cache\CacheInterface::remember()`` now accepts a TTL callable. Custom implementations of ``CacheInterface`` must update the ``$ttl`` parameter type from ``int`` to `callable|int``. - **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``.