Passed
Pull Request — master (#30)
by Alexander
02:06
created

Memcached   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 229
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 64
c 1
b 0
f 0
dl 0
loc 229
rs 9.52
wmc 36

17 Methods

Rating   Name   Duplication   Size   Complexity  
A deleteValue() 0 3 1
A setOptions() 0 3 1
A clear() 0 3 1
A setValue() 0 8 2
A getServers() 0 3 1
A getValue() 0 9 2
A setValues() 0 8 2
A hasValue() 0 4 1
A setUsername() 0 3 1
A setPersistentId() 0 3 1
A deleteValues() 0 8 3
A __construct() 0 11 2
A setPassword() 0 3 1
A setServers() 0 4 2
A getValues() 0 9 2
B getMemcached() 0 18 7
A addServers() 0 12 6
1
<?php
2
3
namespace Yiisoft\CacheOld;
4
5
use Yiisoft\CacheOld\Exception\InvalidConfigException;
6
use Yiisoft\CacheOld\Serializer\SerializerInterface;
7
8
/**
9
 * Memcached implements a cache application component based on [memcached](http://pecl.php.net/package/memcached) PECL
10
 * extension.
11
 *
12
 * Memcached can be configured with a list of memcached servers by settings its {@see Memcached::$servers} property.
13
 * By default, MemCached assumes there is a memcached server running on localhost at port 11211.
14
 *
15
 * See {@see \Psr\SimpleCache\CacheInterface} for common cache operations that MemCached supports.
16
 *
17
 * Note, there is no security measure to protected data in memcached.
18
 * All data in memcached can be accessed by any process running in the system.
19
 *
20
 * You can configure more properties of each server, such as `persistent`, `weight`, `timeout`.
21
 * Please see {@see MemcachedServer} for available options.
22
 */
23
final class Memcached extends SimpleCache
24
{
25
    private const TTL_INFINITY = 0;
26
27
    /**
28
     * @var string an ID that identifies a Memcached instance.
29
     * By default the Memcached instances are destroyed at the end of the request. To create an instance that
30
     * persists between requests, you may specify a unique ID for the instance. All instances created with the
31
     * same ID will share the same connection.
32
     * @see http://ca2.php.net/manual/en/memcached.construct.php
33
     */
34
    private $persistentId;
35
    /**
36
     * @var array options for Memcached.
37
     * @see http://ca2.php.net/manual/en/memcached.setoptions.php
38
     */
39
    private $options;
40
    /**
41
     * @var string memcached sasl username.
42
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
43
     */
44
    private $username;
45
    /**
46
     * @var string memcached sasl password.
47
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
48
     */
49
    private $password;
50
51
    /**
52
     * @var \Memcached the Memcached instance
53
     */
54
    private $cache;
55
    /**
56
     * @var array list of memcached server configurations
57
     */
58
    private $servers;
59
60
    /**
61
     * @param SerializerInterface|null $serializer
62
     * @param MemcachedServer[] $servers list of memcached server configurations
63
     * @throws InvalidConfigException
64
     * @see setSerializer
65
     */
66
    public function __construct(?SerializerInterface $serializer = null, array $servers = [])
67
    {
68
        parent::__construct($serializer);
69
70
        if (empty($servers)) {
71
            $servers = [new MemcachedServer('127.0.0.1')];
72
        }
73
74
        $this->servers = $servers;
75
76
        $this->addServers($this->getMemcached(), $this->servers);
77
    }
78
79
    /**
80
     * Add servers to the server pool of the cache specified
81
     *
82
     * @param \Memcached $cache
83
     * @param MemcachedServer[] $servers
84
     */
85
    private function addServers(\Memcached $cache, array $servers): void
86
    {
87
        $existingServers = [];
88
        if ($this->persistentId !== null) {
89
            foreach ($cache->getServerList() as $s) {
90
                $existingServers[$s['host'] . ':' . $s['port']] = true;
91
            }
92
        }
93
        foreach ($servers as $server) {
94
            $serverAddress = $server->getHost() . ':' . $server->getPort();
95
            if (empty($existingServers) || !isset($existingServers[$serverAddress])) {
96
                $cache->addServer($server->getHost(), $server->getPort(), $server->getWeight());
97
            }
98
        }
99
    }
100
101
    /**
102
     * Returns the underlying memcached object.
103
     * @return \Memcached the memcached object used by this cache component.
104
     * @throws InvalidConfigException if memcached extension is not loaded
105
     */
106
    public function getMemcached(): \Memcached
107
    {
108
        if ($this->cache === null) {
109
            if (!\extension_loaded('memcached')) {
110
                throw new InvalidConfigException('MemCached requires PHP memcached extension to be loaded.');
111
            }
112
113
            $this->cache = $this->persistentId !== null ? new \Memcached($this->persistentId) : new \Memcached();
114
            if ($this->username !== null || $this->password !== null) {
115
                $this->cache->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
116
                $this->cache->setSaslAuthData($this->username, $this->password);
117
            }
118
            if (!empty($this->options)) {
119
                $this->cache->setOptions($this->options);
120
            }
121
        }
122
123
        return $this->cache;
124
    }
125
126
    /**
127
     * Returns the memcached server configurations.
128
     * @return MemcachedServer[] list of memcached server configurations.
129
     */
130
    public function getServers(): array
131
    {
132
        return $this->servers;
133
    }
134
135
    /**
136
     * @param array $configs list of memcached server configurations. Each element must be an array
137
     * with the following keys: host, port, weight.
138
     * @see http://php.net/manual/en/memcached.addserver.php
139
     */
140
    public function setServers(array $configs): void
141
    {
142
        foreach ($configs as $config) {
143
            $this->servers[] = new MemcachedServer($config['host'], $config['port'], $config['weight']);
144
        }
145
    }
146
147
    /**
148
     * @param string $persistentId an ID that identifies a Memcached instance.
149
     * By default the Memcached instances are destroyed at the end of the request. To create an instance that
150
     * persists between requests, you may specify a unique ID for the instance. All instances created with the
151
     * same ID will share the same connection.
152
     * @see http://ca2.php.net/manual/en/memcached.construct.php
153
     */
154
    public function setPersistentId(string $persistentId): void
155
    {
156
        $this->persistentId = $persistentId;
157
    }
158
159
    /**
160
     * @param array $options options for Memcached.
161
     * @see http://ca2.php.net/manual/en/memcached.setoptions.php
162
     */
163
    public function setOptions(array $options): void
164
    {
165
        $this->options = $options;
166
    }
167
168
    /**
169
     * @param string $username memcached sasl username.
170
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
171
     */
172
    public function setUsername(string $username): void
173
    {
174
        $this->username = $username;
175
    }
176
177
    /**
178
     * @param string $password memcached sasl password.
179
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
180
     */
181
    public function setPassword(string $password): void
182
    {
183
        $this->password = $password;
184
    }
185
186
    protected function getValue(string $key, $default = null)
187
    {
188
        $value = $this->cache->get($key);
189
190
        if ($this->cache->getResultCode() === \Memcached::RES_SUCCESS) {
191
            return $value;
192
        }
193
194
        return $default;
195
    }
196
197
    protected function getValues(iterable $keys, $default = null): iterable
198
    {
199
        $values = $this->cache->getMulti($keys);
0 ignored issues
show
Bug introduced by
$keys of type iterable is incompatible with the type array expected by parameter $keys of Memcached::getMulti(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

199
        $values = $this->cache->getMulti(/** @scrutinizer ignore-type */ $keys);
Loading history...
200
201
        if ($this->cache->getResultCode() === \Memcached::RES_SUCCESS) {
202
            return $values;
203
        }
204
205
        return array_fill_keys($keys, $default);
0 ignored issues
show
Bug introduced by
$keys of type iterable is incompatible with the type array expected by parameter $keys of array_fill_keys(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
        return array_fill_keys(/** @scrutinizer ignore-type */ $keys, $default);
Loading history...
206
    }
207
208
    protected function setValue(string $key, $value, ?int $ttl): bool
209
    {
210
        if ($ttl === null) {
211
            $ttl = self::TTL_INFINITY;
212
        } else {
213
            $ttl += time();
214
        }
215
        return $this->cache->set($key, $value, $ttl);
216
    }
217
218
    protected function setValues(iterable $values, ?int $ttl): bool
219
    {
220
        if ($ttl === null) {
221
            $ttl = self::TTL_INFINITY;
222
        } else {
223
            $ttl += time();
224
        }
225
        return $this->cache->setMulti($values, $ttl);
0 ignored issues
show
Bug introduced by
$values of type iterable is incompatible with the type array expected by parameter $items of Memcached::setMulti(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

225
        return $this->cache->setMulti(/** @scrutinizer ignore-type */ $values, $ttl);
Loading history...
226
    }
227
228
    protected function deleteValue(string $key): bool
229
    {
230
        return $this->cache->delete($key);
231
    }
232
233
    public function clear(): bool
234
    {
235
        return $this->cache->flush();
236
    }
237
238
    protected function hasValue(string $key): bool
239
    {
240
        $this->cache->get($key);
241
        return $this->cache->getResultCode() === \Memcached::RES_SUCCESS;
242
    }
243
244
    public function deleteValues(iterable $keys): bool
245
    {
246
        foreach ($this->cache->deleteMulti($keys) as $result) {
0 ignored issues
show
Bug introduced by
$keys of type iterable is incompatible with the type array expected by parameter $keys of Memcached::deleteMulti(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

246
        foreach ($this->cache->deleteMulti(/** @scrutinizer ignore-type */ $keys) as $result) {
Loading history...
247
            if ($result === false) {
248
                return false;
249
            }
250
        }
251
        return true;
252
    }
253
}
254