• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 25417376385

06 May 2026 05:00AM UTC coverage: 88.26% (-0.01%) from 88.274%
25417376385

Pull #10159

github

web-flow
Merge 250df3558 into 3cdb4c5e1
Pull Request #10159: feat: Add support for callables for TTL in Cache Handlers

16 of 30 new or added lines in 3 files covered. (53.33%)

33 existing lines in 2 files now uncovered.

23508 of 26635 relevant lines covered (88.26%)

218.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/system/Cache/Handlers/BaseHandler.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Cache\Handlers;
15

16
use Closure;
17
use CodeIgniter\Cache\CacheInterface;
18
use CodeIgniter\Exceptions\InvalidArgumentException;
19
use Config\Cache;
20
use ReflectionFunction;
21

22
/**
23
 * Base class for cache handling
24
 *
25
 * @see \CodeIgniter\Cache\Handlers\BaseHandlerTest
26
 */
27
abstract class BaseHandler implements CacheInterface
28
{
29
    public const MAX_KEY_LENGTH = PHP_INT_MAX;
30

31
    /**
32
     * Prefix to apply to cache keys.
33
     * May not be used by all handlers.
34
     *
35
     * @var string
36
     */
37
    protected $prefix;
38

39
    /**
40
     * Validates a cache key according to PSR-6.
41
     * Keys that exceed MAX_KEY_LENGTH are hashed.
42
     * From https://github.com/symfony/cache/blob/7b024c6726af21fd4984ac8d1eae2b9f3d90de88/CacheItem.php#L158
43
     *
44
     * @param mixed  $key    The key to validate
45
     * @param string $prefix Optional prefix to include in length calculations
46
     *
47
     * @throws InvalidArgumentException When $key is not valid
48
     */
49
    public static function validateKey($key, $prefix = ''): string
50
    {
51
        if (! is_string($key)) {
305✔
52
            throw new InvalidArgumentException('Cache key must be a string');
5✔
53
        }
54
        if ($key === '') {
300✔
UNCOV
55
            throw new InvalidArgumentException('Cache key cannot be empty.');
×
56
        }
57

58
        $reserved = config(Cache::class)->reservedCharacters;
300✔
59

60
        if ($reserved !== '' && strpbrk($key, $reserved) !== false) {
300✔
61
            throw new InvalidArgumentException('Cache key contains reserved characters ' . $reserved);
1✔
62
        }
63

64
        // If the key with prefix exceeds the length then return the hashed version
65
        return strlen($prefix . $key) > static::MAX_KEY_LENGTH ? $prefix . md5($key) : $prefix . $key;
299✔
66
    }
67

68
    public function remember(string $key, callable|int $ttl, Closure $callback): mixed
69
    {
70
        $value = $this->get($key);
19✔
71

72
        if ($value !== null) {
19✔
UNCOV
73
            return $value;
×
74
        }
75

76
        $value = $callback();
19✔
77

78
        if (is_callable($ttl)) {
19✔
79
            $ttlClosure = Closure::fromCallable($ttl);
15✔
80
            $rf         = new ReflectionFunction($ttlClosure);
15✔
81
            $params     = $rf->getNumberOfRequiredParameters();
15✔
82

83
            if ($params === 0) {
15✔
84
                /** @var Closure(): int $ttlClosure */
85
                $ttl = $ttlClosure();
5✔
86
            } elseif ($params === 1) {
10✔
87
                /** @var Closure(mixed): int $ttlClosure */
88
                $ttl = $ttlClosure($value);
5✔
89
            } else {
90
                throw new InvalidArgumentException(sprintf(
5✔
91
                    'Argument #2 ($ttl) must accept 0 or 1 parameter, %d given.',
5✔
92
                    $params,
5✔
93
                ));
5✔
94
            }
95
        }
96

97
        $this->save($key, $value, $ttl);
14✔
98

99
        return $value;
14✔
100
    }
101

102
    /**
103
     * Check if connection is alive.
104
     *
105
     * Default implementation for handlers that don't require connection management.
106
     * Handlers with persistent connections (Redis, Predis, Memcached) should override this.
107
     */
108
    public function ping(): bool
109
    {
110
        return true;
2✔
111
    }
112

113
    /**
114
     * Reconnect to the cache server.
115
     *
116
     * Default implementation for handlers that don't require connection management.
117
     * Handlers with persistent connections (Redis, Predis, Memcached) should override this.
118
     */
119
    public function reconnect(): bool
120
    {
121
        return true;
1✔
122
    }
123
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc