Memcached   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Test Coverage

Coverage 96.77%

Importance

Changes 0
Metric Value
eloc 52
dl 0
loc 190
ccs 60
cts 62
cp 0.9677
rs 10
c 0
b 0
f 0
wmc 25

12 Methods

Rating   Name   Duplication   Size   Complexity  
A has() 0 8 1
A packValues() 0 10 3
A delete() 0 7 2
A assertKey() 0 6 2
A get() 0 11 2
A getMultiple() 0 19 4
A createDefaultPacker() 0 3 1
A set() 0 14 2
A ttlToMemcachedTime() 0 10 4
A setMultiple() 0 12 2
A __construct() 0 3 1
A clear() 0 3 1
1
<?php
2
3
/*
4
 * This file is part of the Cache package.
5
 *
6
 * Copyright (c) Daniel González
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @author Daniel González <[email protected]>
12
 * @author Arnold Daniels <[email protected]>
13
 */
14
15
declare(strict_types=1);
16
17
namespace Desarrolla2\Cache;
18
19
use Desarrolla2\Cache\Exception\InvalidArgumentException;
20
use Desarrolla2\Cache\Packer\PackerInterface;
21
use Desarrolla2\Cache\Packer\NopPacker;
22
use Memcached as MemcachedServer;
23
24
/**
25
 * Memcached
26
 */
27
class Memcached extends AbstractCache
28
{
29
    /**
30
     * @var MemcachedServer
31
     */
32
    protected $server;
33
34
    /**
35
     * @param MemcachedServer $server
36
     */
37 197
    public function __construct(MemcachedServer $server)
38
    {
39 197
        $this->server = $server;
40
    }
41
42
43
    /**
44
     * Create the default packer for this cache implementation
45
     *
46
     * @return PackerInterface
47
     */
48 80
    protected static function createDefaultPacker(): PackerInterface
49
    {
50 80
        return new NopPacker();
51
    }
52
53
    /**
54
     * Validate the key
55
     *
56
     * @param string $key
57
     * @return void
58
     * @throws InvalidArgumentException
59
     */
60 189
    protected function assertKey($key): void
61
    {
62 189
        parent::assertKey($key);
63
64 117
        if (strlen($key) > 250) {
65
            throw new InvalidArgumentException("Key to long, max 250 characters");
66
        }
67
    }
68
69
    /**
70
     * Pack all values and turn keys into ids
71
     *
72
     * @param iterable $values
73
     * @return array
74
     */
75 41
    protected function packValues(iterable $values): array
76
    {
77 41
        $packed = [];
78
79 41
        foreach ($values as $key => $value) {
80 41
            $this->assertKey(is_int($key) ? (string)$key : $key);
81 41
            $packed[$key] = $this->pack($value);
82
        }
83
84 24
        return $packed;
85
    }
86
87
88
    /**
89
     * {@inheritdoc}
90
     */
91 51
    public function get($key, $default = null)
92
    {
93 51
        $this->assertKey($key);
94
95 33
        $data = $this->server->get($key);
96
97 33
        if ($this->server->getResultCode() !== MemcachedServer::RES_SUCCESS) {
98 11
            return $default;
99
        }
100
101 26
        return $this->unpack($data);
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 21
    public function has($key)
108
    {
109 21
        $this->assertKey($key);
110 3
        $this->server->get($key);
111
112 3
        $result = $this->server->getResultCode();
113
114 3
        return $result === MemcachedServer::RES_SUCCESS;
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 57
    public function set($key, $value, $ttl = null)
121
    {
122 57
        $this->assertKey($key);
123
124 39
        $packed = $this->pack($value);
125 39
        $ttlTime = $this->ttlToMemcachedTime($ttl);
126
127 29
        if ($ttlTime === false) {
0 ignored issues
show
introduced by
The condition $ttlTime === false is always false.
Loading history...
128 1
            return $this->delete($key);
129
        }
130
131 29
        $success = $this->server->set($key, $packed, $ttlTime);
132
133 29
        return $success;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 40
    public function delete($key)
140
    {
141 40
        $this->server->delete($this->keyToId($key));
142
143 22
        $result = $this->server->getResultCode();
144
145 22
        return $result === MemcachedServer::RES_SUCCESS || $result === MemcachedServer::RES_NOTFOUND;
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151 30
    public function getMultiple($keys, $default = null)
152
    {
153 30
        $this->assertIterable($keys, 'keys not iterable');
154 29
        $keysArr = is_array($keys) ? $keys : iterator_to_array($keys, false);
155 29
        array_walk($keysArr, [$this, 'assertKey']);
156
157 11
        $result = $this->server->getMulti($keysArr);
158
159 11
        if ($result === false) {
160
            return false;
0 ignored issues
show
introduced by
Method Desarrolla2\Cache\Memcached::getMultiple() should return iterable but returns false.
Loading history...
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the return type mandated by Psr\SimpleCache\CacheInterface::getMultiple() of iterable.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
161
        }
162
163 11
        $items = array_fill_keys($keysArr, $default);
164
165 11
        foreach ($result as $key => $value) {
166 11
            $items[$key] = $this->unpack($value);
167
        }
168
169 11
        return $items;
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175 42
    public function setMultiple($values, $ttl = null)
176
    {
177 42
        $this->assertIterable($values, 'values not iterable');
178
179 41
        $packed = $this->packValues($values);
180 24
        $ttlTime = $this->ttlToMemcachedTime($ttl);
181
182 14
        if ($ttlTime === false) {
0 ignored issues
show
introduced by
The condition $ttlTime === false is always false.
Loading history...
183 1
            return $this->server->deleteMulti(array_keys($packed));
0 ignored issues
show
introduced by
Method Desarrolla2\Cache\Memcached::setMultiple() should return bool but returns array.
Loading history...
184
        }
185
186 13
        return $this->server->setMulti($packed, $ttlTime);
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192 197
    public function clear()
193
    {
194 197
       return $this->server->flush();
195
    }
196
197
198
    /**
199
     * Convert ttl to timestamp or seconds.
200
     *
201
     * @see http://php.net/manual/en/memcached.expiration.php
202
     *
203
     * @param null|int|\DateInterval $ttl
204
     * @return int|null
205
     * @throws InvalidArgumentException
206
     */
207 63
    protected function ttlToMemcachedTime($ttl)
208
    {
209 63
        $seconds = $this->ttlToSeconds($ttl);
210
211 43
        if ($seconds <= 0) {
212 41
            return isset($seconds) ? false : 0;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? false : 0 could also return false which is incompatible with the documented return type integer|null. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
213
        }
214
215
        /* 2592000 seconds = 30 days */
216 2
        return $seconds <= 2592000 ? $seconds : $this->ttlToTimestamp($ttl);
217
    }
218
}
219