Completed
Push — master ( b10f59...6e4655 )
by Alexander
02:32
created

MemCached   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Test Coverage

Coverage 65.22%

Importance

Changes 0
Metric Value
wmc 33
eloc 53
dl 0
loc 209
ccs 45
cts 69
cp 0.6522
rs 9.76
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
B getMemcached() 0 18 7
A setValues() 0 7 2
A setPersistentId() 0 3 1
A getValues() 0 10 2
A setPassword() 0 3 1
A setValue() 0 7 2
A deleteValue() 0 3 1
A setUsername() 0 3 1
A addServers() 0 12 6
A hasValue() 0 4 1
A getServers() 0 3 1
A setServers() 0 4 2
A getValue() 0 9 2
A setOptions() 0 3 1
A clear() 0 3 1
1
<?php
2
namespace Yiisoft\Cache;
3
4
use Yiisoft\Cache\Serializer\SerializerInterface;
5
use Yiisoft\Cache\Exceptions\InvalidConfigException;
6
7
/**
8
 * MemCached implements a cache application component based on [memcached](http://pecl.php.net/package/memcached) PECL
9
 * extension.
10
 *
11
 * MemCached can be configured with a list of memcached servers by settings its [[servers]] property.
12
 * By default, MemCached assumes there is a memcached server running on localhost at port 11211.
13
 *
14
 * See [[\Psr\SimpleCache\CacheInterface]] for common cache operations that MemCached supports.
15
 *
16
 * Note, there is no security measure to protected data in memcached.
17
 * All data in memcached can be accessed by any process running in the system.
18
 *
19
 * To use MemCached as the cache application component, configure the application as follows,
20
 *
21
 * ```php
22
 * [
23
 *     'components' => [
24
 *         'cache' => [
25
 *             '__class' => \Yiisoft\Cache\Cache::class,
26
 *             'handler' => [
27
 *                 '__class' => \Yiisoft\Cache\MemCached::class,
28
 *                 'servers' => [
29
 *                     [
30
 *                         'host' => 'server1',
31
 *                         'port' => 11211,
32
 *                         'weight' => 60,
33
 *                     ],
34
 *                     [
35
 *                         'host' => 'server2',
36
 *                         'port' => 11211,
37
 *                         'weight' => 40,
38
 *                     ],
39
 *                 ],
40
 *             ],
41
 *         ],
42
 *     ],
43
 * ]
44
 * ```
45
 *
46
 * In the above, two memcached servers are used: server1 and server2. You can configure more properties of
47
 * each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options.
48
 *
49
 * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).
50
 */
51
final class MemCached extends SimpleCache
52
{
53
    /**
54
     * @var string an ID that identifies a Memcached instance.
55
     * By default the Memcached instances are destroyed at the end of the request. To create an instance that
56
     * persists between requests, you may specify a unique ID for the instance. All instances created with the
57
     * same ID will share the same connection.
58
     * @see http://ca2.php.net/manual/en/memcached.construct.php
59
     */
60
    private $persistentId;
61
    /**
62
     * @var array options for Memcached.
63
     * @see http://ca2.php.net/manual/en/memcached.setoptions.php
64
     */
65
    private $options;
66
    /**
67
     * @var string memcached sasl username.
68
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
69
     */
70
    private $username;
71
    /**
72
     * @var string memcached sasl password.
73
     * @see http://php.net/manual/en/memcached.setsaslauthdata.php
74
     */
75
    private $password;
76
77
    /**
78
     * @var \Memcached the Memcached instance
79
     */
80
    private $cache;
81
    /**
82
     * @var array list of memcached server configurations
83
     */
84
    private $servers;
85
86
    /**
87
     * @param SerializerInterface|null $serializer
88
     * @param array $servers
89
     * @throws InvalidConfigException
90
     * @see setSerializer
91
     */
92 14
    public function __construct(?SerializerInterface $serializer = null, array $servers = [])
93
    {
94 14
        parent::__construct($serializer);
95
96 14
        if (empty($servers)) {
97 14
            $servers = [new MemCachedServer('127.0.0.1')];
98
        }
99
100 14
        $this->servers = $servers;
101
102 14
        $this->addServers($this->getMemcached(), $this->servers);
103 14
    }
104
105
    /**
106
     * Add servers to the server pool of the cache specified
107
     *
108
     * @param \Memcached $cache
109
     * @param MemCachedServer[] $servers
110
     */
111 14
    private function addServers(\Memcached $cache, array $servers): void
112
    {
113 14
        $existingServers = [];
114 14
        if ($this->persistentId !== null) {
115
            foreach ($cache->getServerList() as $s) {
116
                $existingServers[$s['host'] . ':' . $s['port']] = true;
117
            }
118
        }
119 14
        foreach ($servers as $server) {
120 14
            $serverAddress = $server->getHost() . ':' . $server->getPort();
121 14
            if (empty($existingServers) || !isset($existingServers[$serverAddress])) {
122 14
                $cache->addServer($server->getHost(), $server->getPort(), $server->getWeight());
123
            }
124
        }
125 14
    }
126
127
    /**
128
     * Returns the underlying memcached object.
129
     * @return \Memcached the memcached object used by this cache component.
130
     * @throws InvalidConfigException if memcached extension is not loaded
131
     */
132 14
    public function getMemcached(): \Memcached
133
    {
134 14
        if ($this->cache === null) {
135 14
            if (!\extension_loaded('memcached')) {
136
                throw new InvalidConfigException('MemCached requires PHP memcached extension to be loaded.');
137
            }
138
139 14
            $this->cache = $this->persistentId !== null ? new \Memcached($this->persistentId) : new \Memcached;
140 14
            if ($this->username !== null || $this->password !== null) {
141
                $this->cache->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
142
                $this->cache->setSaslAuthData($this->username, $this->password);
143
            }
144 14
            if (!empty($this->options)) {
145
                $this->cache->setOptions($this->options);
146
            }
147
        }
148
149 14
        return $this->cache;
150
    }
151
152
    /**
153
     * Returns the memcached server configurations.
154
     * @return MemCachedServer[] list of memcached server configurations.
155
     */
156
    public function getServers(): array
157
    {
158
        return $this->servers;
159
    }
160
161
    /**
162
     * @param array $config list of memcached server configurations. Each element must be an array
163
     * with the following keys: host, port, persistent, weight, timeout, retryInterval, status.
164
     * @see http://php.net/manual/en/memcached.addserver.php
165
     */
166
    public function setServers(array $config): void
167
    {
168
        foreach ($config as $c) {
169
            $this->servers[] = new MemCachedServer($c);
170
        }
171
    }
172
173
    /**
174
     * @param string $persistentId
175
     */
176
    public function setPersistentId(string $persistentId): void
177
    {
178
        $this->persistentId = $persistentId;
179
    }
180
181
    /**
182
     * @param array $options
183
     */
184
    public function setOptions(array $options): void
185
    {
186
        $this->options = $options;
187
    }
188
189
    /**
190
     * @param string $username
191
     */
192
    public function setUsername(string $username): void
193
    {
194
        $this->username = $username;
195
    }
196
197
    /**
198
     * @param string $password
199
     */
200
    public function setPassword(string $password): void
201
    {
202
        $this->password = $password;
203
    }
204
205 12
    protected function getValue(string $key, $default = null)
206
    {
207 12
        $value = $this->cache->get($key);
208
209 12
        if ($this->cache->getResultCode() === \Memcached::RES_SUCCESS) {
210 10
            return $value;
211
        }
212
213 7
        return $default;
214
    }
215
216 3
    protected function getValues(array $keys, $default = null): array
217
    {
218 3
        $values = $this->cache->getMulti($keys);
219
220 3
        if ($this->cache->getResultCode() === \Memcached::RES_SUCCESS) {
221
            // TODO: test that all fields are returned
222 3
            return $values;
223
        }
224
225 1
        return array_fill_keys($keys, $default);
226
    }
227
228 11
    protected function setValue(string $key, $value, int $ttl): bool
229
    {
230
        // Use UNIX timestamp since it doesn't have any limitation
231
        // @see http://php.net/manual/en/memcached.expiration.php
232 11
        $expire = $ttl > 0 ? $ttl + time() : 0;
233
234 11
        return $this->cache->set($key, $value, $expire);
235
    }
236
237 4
    protected function setValues(array $values, int $ttl): bool
238
    {
239
        // Use UNIX timestamp since it doesn't have any limitation
240
        // @see http://php.net/manual/en/memcached.expiration.php
241 4
        $expire = $ttl > 0 ? $ttl + time() : 0;
242
243 4
        return $this->cache->setMulti($values, $expire);
244
    }
245
246 1
    protected function deleteValue(string $key): bool
247
    {
248 1
        return $this->cache->delete($key);
249
    }
250
251 11
    public function clear(): bool
252
    {
253 11
        return $this->cache->flush();
254
    }
255
256 2
    protected function hasValue(string $key): bool
257
    {
258 2
        $this->cache->get($key);
259 2
        return $this->cache->getResultCode() === \Memcached::RES_SUCCESS;
260
    }
261
}
262