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) { |
|
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
|
|||
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) { |
|
183 | 1 | return $this->server->deleteMulti(array_keys($packed)); |
|
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
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. ![]() |
|||
213 | } |
||
214 | |||
215 | /* 2592000 seconds = 30 days */ |
||
216 | 2 | return $seconds <= 2592000 ? $seconds : $this->ttlToTimestamp($ttl); |
|
217 | } |
||
218 | } |
||
219 |
In the issue above, the returned value is violating the contract defined by the mentioned interface.
Let's take a look at an example: