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

aplus-framework / cache / 16306742406

15 Jul 2025 11:44PM UTC coverage: 95.194%. Remained the same
16306742406

push

github

natanfelles
Add since tag

515 of 541 relevant lines covered (95.19%)

55.5 hits per line

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

97.56
/src/MemcachedCache.php
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of Aplus Framework Cache Library.
4
 *
5
 * (c) Natan Felles <natanfelles@gmail.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Framework\Cache;
11

12
use Framework\Log\Logger;
13
use Framework\Log\LogLevel;
14
use Memcached;
15
use OutOfBoundsException;
16
use Override;
17
use RuntimeException;
18
use SensitiveParameter;
19

20
/**
21
 * Class MemcachedCache.
22
 *
23
 * @package cache
24
 */
25
class MemcachedCache extends Cache
26
{
27
    protected Memcached $memcached;
28
    /**
29
     * Memcached Cache handler configurations.
30
     *
31
     * @var array<string,mixed>
32
     */
33
    protected array $configs = [
34
        'servers' => [
35
            [
36
                'host' => '127.0.0.1',
37
                'port' => 11211,
38
                'weight' => 0,
39
            ],
40
        ],
41
        'options' => [
42
            Memcached::OPT_BINARY_PROTOCOL => true,
43
        ],
44
    ];
45

46
    /**
47
     * MemcachedCache constructor.
48
     *
49
     * @param Memcached|array<string,mixed>|null $configs Driver specific
50
     * configurations. Set null to not initialize or a custom Memcached object.
51
     * @param string|null $prefix Keys prefix
52
     * @param Serializer|string $serializer Data serializer
53
     * @param Logger|null $logger Logger instance
54
     */
55
    public function __construct(
56
        #[SensitiveParameter]
57
        Memcached | array | null $configs = [],
58
        ?string $prefix = null,
59
        Serializer | string $serializer = Serializer::PHP,
60
        ?Logger $logger = null
61
    ) {
62
        parent::__construct($configs, $prefix, $serializer, $logger);
111✔
63
        if ($configs instanceof Memcached) {
111✔
64
            $this->setMemcached($configs);
6✔
65
            $this->setAutoClose(false);
6✔
66
        }
67
    }
68

69
    protected function initialize() : void
70
    {
71
        $this->validateConfigs();
111✔
72
        $this->connect();
111✔
73
    }
74

75
    protected function validateConfigs() : void
76
    {
77
        foreach ($this->configs['servers'] as $index => $config) {
111✔
78
            if (empty($config['host'])) {
111✔
79
                throw new OutOfBoundsException(
1✔
80
                    "Memcached host config empty on server '{$index}'"
1✔
81
                );
1✔
82
            }
83
        }
84
    }
85

86
    /**
87
     * Set custom Memcached instance.
88
     *
89
     * @since 3.2
90
     *
91
     * @param Memcached $memcached
92
     *
93
     * @return static
94
     */
95
    public function setMemcached(Memcached $memcached) : static
96
    {
97
        $this->memcached = $memcached;
12✔
98
        return $this;
12✔
99
    }
100

101
    /**
102
     * Get Memcached instance or null.
103
     *
104
     * @since 3.2
105
     *
106
     * @return Memcached|null
107
     */
108
    public function getMemcached() : ?Memcached
109
    {
110
        return $this->memcached ?? null;
12✔
111
    }
112

113
    public function get(string $key) : mixed
114
    {
115
        if (isset($this->debugCollector)) {
66✔
116
            $start = \microtime(true);
12✔
117
            return $this->addDebugGet(
12✔
118
                $key,
12✔
119
                $start,
12✔
120
                $this->getValue($key)
12✔
121
            );
12✔
122
        }
123
        return $this->getValue($key);
54✔
124
    }
125

126
    protected function getValue(string $key) : mixed
127
    {
128
        $key = $this->memcached->get($this->renderKey($key));
66✔
129
        return $key === false && $this->memcached->getResultCode() === Memcached::RES_NOTFOUND
66✔
130
            ? null
66✔
131
            : $key;
66✔
132
    }
133

134
    public function set(string $key, mixed $value, ?int $ttl = null) : bool
135
    {
136
        if (isset($this->debugCollector)) {
60✔
137
            $start = \microtime(true);
6✔
138
            return $this->addDebugSet(
6✔
139
                $key,
6✔
140
                $ttl,
6✔
141
                $start,
6✔
142
                $value,
6✔
143
                $this->memcached->set($this->renderKey($key), $value, $this->makeTtl($ttl))
6✔
144
            );
6✔
145
        }
146
        return $this->memcached->set($this->renderKey($key), $value, $this->makeTtl($ttl));
54✔
147
    }
148

149
    public function delete(string $key) : bool
150
    {
151
        if (isset($this->debugCollector)) {
18✔
152
            $start = \microtime(true);
6✔
153
            return $this->addDebugDelete(
6✔
154
                $key,
6✔
155
                $start,
6✔
156
                $this->memcached->delete($this->renderKey($key))
6✔
157
            );
6✔
158
        }
159
        return $this->memcached->delete($this->renderKey($key));
12✔
160
    }
161

162
    public function flush() : bool
163
    {
164
        if (isset($this->debugCollector)) {
111✔
165
            $start = \microtime(true);
24✔
166
            return $this->addDebugFlush(
24✔
167
                $start,
24✔
168
                $this->memcached->flush()
24✔
169
            );
24✔
170
        }
171
        return $this->memcached->flush();
87✔
172
    }
173

174
    #[Override]
175
    public function close() : bool
176
    {
177
        return $this->memcached->quit();
111✔
178
    }
179

180
    protected function connect() : void
181
    {
182
        $this->configs['options'][Memcached::OPT_SERIALIZER] = match ($this->serializer) {
111✔
183
            Serializer::IGBINARY => Memcached::SERIALIZER_IGBINARY,
111✔
184
            Serializer::JSON => Memcached::SERIALIZER_JSON,
93✔
185
            Serializer::JSON_ARRAY => Memcached::SERIALIZER_JSON_ARRAY,
75✔
186
            Serializer::MSGPACK => Memcached::SERIALIZER_MSGPACK,
57✔
187
            default => Memcached::SERIALIZER_PHP,
39✔
188
        };
111✔
189
        $this->memcached = new Memcached();
111✔
190
        $pool = [];
111✔
191
        foreach ($this->configs['servers'] as $server) {
111✔
192
            $host = $server['host'] . ':' . ($server['port'] ?? 11211);
111✔
193
            if (\in_array($host, $pool, true)) {
111✔
194
                $this->log(
1✔
195
                    'Cache (memcached): Server pool already has ' . $host,
1✔
196
                    LogLevel::DEBUG
1✔
197
                );
1✔
198
                continue;
1✔
199
            }
200
            $result = $this->memcached->addServer(
111✔
201
                $server['host'],
111✔
202
                $server['port'] ?? 11211,
111✔
203
                $server['weight'] ?? 0
111✔
204
            );
111✔
205
            if ($result === false) {
111✔
206
                $this->log("Cache (memcached): Could not add {$host} to server pool");
×
207
            }
208
            $pool[] = $host;
111✔
209
        }
210
        $result = $this->memcached->setOptions($this->configs['options']);
111✔
211
        if ($result === false) {
111✔
212
            $this->log('Cache (memcached): ' . $this->memcached->getLastErrorMessage());
×
213
        }
214
        if (!$this->memcached->getStats()) {
111✔
215
            throw new RuntimeException('Cache (memcached): Could not connect to any server');
1✔
216
        }
217
    }
218
}
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