Total Complexity | 62 |
Total Lines | 428 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like Memcached often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Memcached, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class Memcached extends BaseHandler |
||
29 | { |
||
30 | /** |
||
31 | * Wrapper Memcached. |
||
32 | * |
||
33 | * @var BaseMemcached |
||
34 | */ |
||
35 | protected $_Memcached; |
||
36 | |||
37 | /** |
||
38 | * La configuration par défaut utilisée sauf si elle est remplacée par la configuration d'exécution |
||
39 | * |
||
40 | * - `compress` Indique s'il faut compresser les données |
||
41 | * - `duration` Spécifiez combien de temps durent les éléments de cette configuration de cache. |
||
42 | * - `groups` Liste des groupes ou 'tags' associés à chaque clé stockée dans cette configuration. |
||
43 | * pratique pour supprimer un groupe complet du cache. |
||
44 | * - `nom d'utilisateur` Connectez-vous pour accéder au serveur Memcache |
||
45 | * - `password` Mot de passe pour accéder au serveur Memcache |
||
46 | * - `persistent` Le nom de la connexion persistante. Toutes les configurations utilisant |
||
47 | * la même valeur persistante partagera une seule connexion sous-jacente. |
||
48 | * - `prefix` Préfixé à toutes les entrées. Bon pour quand vous avez besoin de partager un keyspace |
||
49 | * avec une autre configuration de cache ou une autre application. |
||
50 | * - `serialize` Le moteur de sérialisation utilisé pour sérialiser les données. Les moteurs disponibles sont 'php', |
||
51 | * 'igbinaire' et 'json'. A côté de 'php', l'extension memcached doit être compilée avec le |
||
52 | * Prise en charge appropriée du sérialiseur. |
||
53 | * - `servers` Chaîne ou tableau de serveurs memcached. Si un tableau MemcacheEngine utilisera |
||
54 | * eux comme une piscine. |
||
55 | * - `options` - Options supplémentaires pour le client memcached. Doit être un tableau d'option => valeur. |
||
56 | * Utilisez les constantes \Memcached::OPT_* comme clés. |
||
57 | */ |
||
58 | protected array $_defaultConfig = [ |
||
59 | 'compress' => false, |
||
60 | 'duration' => 3600, |
||
61 | 'groups' => [], |
||
62 | 'host' => null, |
||
63 | 'username' => null, |
||
64 | 'password' => null, |
||
65 | 'persistent' => null, |
||
66 | 'port' => null, |
||
67 | 'prefix' => 'blitz_', |
||
68 | 'serialize' => 'php', |
||
69 | 'servers' => ['127.0.0.1'], |
||
70 | 'options' => [], |
||
71 | ]; |
||
72 | |||
73 | /** |
||
74 | * Liste des moteurs de sérialisation disponibles |
||
75 | * |
||
76 | * Memcached doit être compilé avec JSON et le support igbinary pour utiliser ces moteurs |
||
77 | */ |
||
78 | protected array $_serializers = []; |
||
79 | |||
80 | /** |
||
81 | * @var string[] |
||
82 | */ |
||
83 | protected array $_compiledGroupNames = []; |
||
84 | |||
85 | /** |
||
86 | * {@inheritDoc} |
||
87 | */ |
||
88 | public function init(array $config = []): bool |
||
89 | { |
||
90 | if (! extension_loaded('memcached')) { |
||
91 | throw new RuntimeException('L\'extension `memcached` doit être activée pour utiliser MemcachedHandler.'); |
||
92 | } |
||
93 | |||
94 | $this->_serializers = [ |
||
95 | 'igbinary' => BaseMemcached::SERIALIZER_IGBINARY, |
||
96 | 'json' => BaseMemcached::SERIALIZER_JSON, |
||
97 | 'php' => BaseMemcached::SERIALIZER_PHP, |
||
98 | ]; |
||
99 | if (defined('Memcached::HAVE_MSGPACK')) { |
||
100 | $this->_serializers['msgpack'] = BaseMemcached::SERIALIZER_MSGPACK; |
||
101 | } |
||
102 | |||
103 | parent::init($config); |
||
104 | |||
105 | if (! empty($config['host'])) { |
||
106 | if (empty($config['port'])) { |
||
107 | $config['servers'] = [$config['host']]; |
||
108 | } else { |
||
109 | $config['servers'] = [sprintf('%s:%d', $config['host'], $config['port'])]; |
||
110 | } |
||
111 | } |
||
112 | |||
113 | if (isset($config['servers'])) { |
||
114 | $this->setConfig('servers', $config['servers'], false); |
||
115 | } |
||
116 | |||
117 | if (! is_array($this->_config['servers'])) { |
||
118 | $this->_config['servers'] = [$this->_config['servers']]; |
||
119 | } |
||
120 | |||
121 | /** @psalm-suppress RedundantPropertyInitializationCheck */ |
||
122 | if (isset($this->_Memcached)) { |
||
123 | return true; |
||
124 | } |
||
125 | |||
126 | if ($this->_config['persistent']) { |
||
127 | $this->_Memcached = new BaseMemcached($this->_config['persistent']); |
||
128 | } else { |
||
129 | $this->_Memcached = new BaseMemcached(); |
||
130 | } |
||
131 | $this->_setOptions(); |
||
132 | |||
133 | $serverList = $this->_Memcached->getServerList(); |
||
134 | if ($serverList) { |
||
|
|||
135 | if ($this->_Memcached->isPersistent()) { |
||
136 | foreach ($serverList as $server) { |
||
137 | if (! in_array($server['host'] . ':' . $server['port'], $this->_config['servers'], true)) { |
||
138 | throw new InvalidArgumentException( |
||
139 | 'Configuration du cache invalide. Plusieurs configurations de cache persistant sont détectées' . |
||
140 | ' avec des valeurs `servers` différentes. `valeurs` des serveurs pour les configurations de cache persistant' . |
||
141 | ' doit être le même lors de l\'utilisation du même identifiant de persistance.' |
||
142 | ); |
||
143 | } |
||
144 | } |
||
145 | } |
||
146 | |||
147 | return true; |
||
148 | } |
||
149 | |||
150 | $servers = []; |
||
151 | |||
152 | foreach ($this->_config['servers'] as $server) { |
||
153 | $servers[] = $this->parseServerString($server); |
||
154 | } |
||
155 | |||
156 | if (! $this->_Memcached->addServers($servers)) { |
||
157 | return false; |
||
158 | } |
||
159 | |||
160 | if (is_array($this->_config['options'])) { |
||
161 | foreach ($this->_config['options'] as $opt => $value) { |
||
162 | $this->_Memcached->setOption($opt, $value); |
||
163 | } |
||
164 | } |
||
165 | |||
166 | if (empty($this->_config['username']) && ! empty($this->_config['login'])) { |
||
167 | throw new InvalidArgumentException( |
||
168 | 'Veuillez passer "nom d\'utilisateur" au lieu de "login" pour vous connecter à Memcached' |
||
169 | ); |
||
170 | } |
||
171 | |||
172 | if ($this->_config['username'] !== null && $this->_config['password'] !== null) { |
||
173 | if (! method_exists($this->_Memcached, 'setSaslAuthData')) { |
||
174 | throw new InvalidArgumentException( |
||
175 | "L'extension Memcached n'est pas construite avec le support SASL" |
||
176 | ); |
||
177 | } |
||
178 | $this->_Memcached->setOption(BaseMemcached::OPT_BINARY_PROTOCOL, true); |
||
179 | $this->_Memcached->setSaslAuthData( |
||
180 | $this->_config['username'], |
||
181 | $this->_config['password'] |
||
182 | ); |
||
183 | } |
||
184 | |||
185 | return true; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Paramétrage de l'instance memcached |
||
190 | * |
||
191 | * @throws InvalidArgumentException Lorsque l'extension Memcached n'est pas construite avec le moteur de sérialisation souhaité. |
||
192 | */ |
||
193 | protected function _setOptions(): void |
||
194 | { |
||
195 | $this->_Memcached->setOption(BaseMemcached::OPT_LIBKETAMA_COMPATIBLE, true); |
||
196 | |||
197 | $serializer = strtolower($this->_config['serialize']); |
||
198 | if (! isset($this->_serializers[$serializer])) { |
||
199 | throw new InvalidArgumentException( |
||
200 | sprintf('%s n\'est pas un moteur de sérialisation valide pour Memcached', $serializer) |
||
201 | ); |
||
202 | } |
||
203 | |||
204 | if ( |
||
205 | $serializer !== 'php' |
||
206 | && ! constant('Memcached::HAVE_' . strtoupper($serializer)) |
||
207 | ) { |
||
208 | throw new InvalidArgumentException( |
||
209 | sprintf('L\'extension Memcached n\'est pas compilée avec la prise en charge de %s', $serializer) |
||
210 | ); |
||
211 | } |
||
212 | |||
213 | $this->_Memcached->setOption( |
||
214 | BaseMemcached::OPT_SERIALIZER, |
||
215 | $this->_serializers[$serializer] |
||
216 | ); |
||
217 | |||
218 | // Check for Amazon ElastiCache instance |
||
219 | if ( |
||
220 | defined('Memcached::OPT_CLIENT_MODE') |
||
221 | && defined('Memcached::DYNAMIC_CLIENT_MODE') |
||
222 | ) { |
||
223 | $this->_Memcached->setOption( |
||
224 | BaseMemcached::OPT_CLIENT_MODE, |
||
225 | BaseMemcached::DYNAMIC_CLIENT_MODE |
||
226 | ); |
||
227 | } |
||
228 | |||
229 | $this->_Memcached->setOption( |
||
230 | BaseMemcached::OPT_COMPRESSION, |
||
231 | (bool) $this->_config['compress'] |
||
232 | ); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Analyse l'adresse du serveur dans l'hôte/port. Gère à la fois les adresses IPv6 et IPv4 et sockets Unix |
||
237 | * |
||
238 | * @param string $server La chaîne d'adresse du serveur. |
||
239 | * |
||
240 | * @return array Tableau contenant l'hôte, le port |
||
241 | */ |
||
242 | public function parseServerString(string $server): array |
||
243 | { |
||
244 | $socketTransport = 'unix://'; |
||
245 | if (str_starts_with($server, $socketTransport)) { |
||
246 | return [substr($server, strlen($socketTransport)), 0]; |
||
247 | } |
||
248 | if (substr($server, 0, 1) === '[') { |
||
249 | $position = strpos($server, ']:'); |
||
250 | if ($position !== false) { |
||
251 | $position++; |
||
252 | } |
||
253 | } else { |
||
254 | $position = strpos($server, ':'); |
||
255 | } |
||
256 | $port = 11211; |
||
257 | $host = $server; |
||
258 | if ($position !== false) { |
||
259 | $host = substr($server, 0, $position); |
||
260 | $port = substr($server, $position + 1); |
||
261 | } |
||
262 | |||
263 | return [$host, (int) $port]; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Lire une valeur d'option à partir de la connexion memcached. |
||
268 | * |
||
269 | * @return bool|int|string|null |
||
270 | * |
||
271 | * @see https://secure.php.net/manual/en/memcached.getoption.php |
||
272 | */ |
||
273 | public function getOption(int $name) |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * {@inheritDoc} |
||
280 | * |
||
281 | * @see https://www.php.net/manual/en/memcached.set.php |
||
282 | */ |
||
283 | public function set(string $key, mixed $value, null|DateInterval|int $ttl = null): bool |
||
284 | { |
||
285 | $duration = $this->duration($ttl); |
||
286 | |||
287 | return $this->_Memcached->set($this->_key($key), $value, $duration); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * {@inheritDoc} |
||
292 | */ |
||
293 | public function setMultiple(iterable $values, null|DateInterval|int $ttl = null): bool |
||
294 | { |
||
295 | $cacheData = []; |
||
296 | |||
297 | foreach ($values as $key => $value) { |
||
298 | $cacheData[$this->_key($key)] = $value; |
||
299 | } |
||
300 | $duration = $this->duration($ttl); |
||
301 | |||
302 | return $this->_Memcached->setMulti($cacheData, $duration); |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * {@inheritDoc} |
||
307 | */ |
||
308 | public function get(string $key, mixed $default = null): mixed |
||
309 | { |
||
310 | $key = $this->_key($key); |
||
311 | $value = $this->_Memcached->get($key); |
||
312 | if ($this->_Memcached->getResultCode() === BaseMemcached::RES_NOTFOUND) { |
||
313 | return $default; |
||
314 | } |
||
315 | |||
316 | return $value; |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * {@inheritDoc} |
||
321 | * |
||
322 | * @return array Un tableau contenant, pour chacune des $keys données, les données mises en cache ou false si les données mises en cache n'ont pas pu être récupérées. |
||
323 | */ |
||
324 | public function getMultiple(iterable $keys, mixed $default = null): iterable |
||
325 | { |
||
326 | $cacheKeys = []; |
||
327 | |||
328 | foreach ($keys as $key) { |
||
329 | $cacheKeys[$key] = $this->_key($key); |
||
330 | } |
||
331 | |||
332 | $values = $this->_Memcached->getMulti($cacheKeys); |
||
333 | $return = []; |
||
334 | |||
335 | foreach ($cacheKeys as $original => $prefixed) { |
||
336 | $return[$original] = $values[$prefixed] ?? $default; |
||
337 | } |
||
338 | |||
339 | return $return; |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * {@inheritDoc} |
||
344 | */ |
||
345 | public function increment(string $key, int $offset = 1) |
||
346 | { |
||
347 | return $this->_Memcached->increment($this->_key($key), $offset); |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * {@inheritDoc} |
||
352 | */ |
||
353 | public function decrement(string $key, int $offset = 1) |
||
354 | { |
||
355 | return $this->_Memcached->decrement($this->_key($key), $offset); |
||
356 | } |
||
357 | |||
358 | /** |
||
359 | * {@inheritDoc} |
||
360 | */ |
||
361 | public function delete(string $key): bool |
||
364 | } |
||
365 | |||
366 | /** |
||
367 | * {@inheritDoc} |
||
368 | */ |
||
369 | public function deleteMultiple(iterable $keys): bool |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * {@inheritDoc} |
||
382 | */ |
||
383 | public function clear(): bool |
||
384 | { |
||
385 | $keys = $this->_Memcached->getAllKeys(); |
||
386 | if ($keys === false) { |
||
387 | return false; |
||
388 | } |
||
389 | |||
390 | foreach ($keys as $key) { |
||
391 | if (str_starts_with($key, $this->_config['prefix'])) { |
||
392 | $this->_Memcached->delete($key); |
||
393 | } |
||
394 | } |
||
395 | |||
396 | return true; |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * {@inheritDoc} |
||
401 | */ |
||
402 | public function add(string $key, mixed $value): bool |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * {@inheritDoc} |
||
412 | */ |
||
413 | public function info() |
||
414 | { |
||
415 | return $this->_Memcached->getStats(); |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * {@inheritDoc} |
||
420 | */ |
||
421 | public function groups(): array |
||
448 | } |
||
449 | |||
450 | /** |
||
451 | * {@inheritDoc} |
||
452 | */ |
||
453 | public function clearGroup(string $group): bool |
||
458 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.