Passed
Push — main ( eecacd...dafd8a )
by Dimitri
11:37
created

Cache   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 445
Duplicated Lines 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 89
dl 0
loc 445
rs 8.96
c 3
b 2
f 0
wmc 43

25 Methods

Rating   Name   Duplication   Size   Complexity  
A setConfig() 0 6 1
A __construct() 0 3 1
A writeMany() 0 3 1
A deleteMultiple() 0 3 1
A increment() 0 7 2
A decrement() 0 7 2
A deleteMany() 0 3 1
A clearGroup() 0 3 1
A getMultiple() 0 3 1
C factory() 0 51 12
A delete() 0 3 1
A get() 0 3 1
A setMultiple() 0 3 1
A set() 0 3 1
A write() 0 20 4
A clear() 0 3 1
A readMany() 0 3 1
A read() 0 3 1
A has() 0 3 1
A enable() 0 3 1
A remember() 0 10 2
A disable() 0 3 1
A info() 0 3 1
A add() 0 7 2
A enabled() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Cache 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 Cache, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Cache;
13
14
use BlitzPHP\Cache\Handlers\BaseHandler;
15
use BlitzPHP\Cache\Handlers\Dummy;
16
use DateInterval;
17
use RuntimeException;
18
19
/**
20
 * Cache fournit une interface cohérente à la mise en cache dans votre application. Il vous permet
21
 * d'utiliser plusieurs moteurs de Cache différents, sans coupler votre application à un moteur spécifique
22
 * la mise en oeuvre. Il vous permet également de modifier le stockage ou la configuration du cache sans affecter
23
 * le reste de votre candidature.
24
 *
25
 * Cela configurerait un moteur de cache APCu sur l'alias "shared". Vous pourrez alors lire et écrire
26
 * à cet alias de cache en l'utilisant pour le paramètre `$config` dans les différentes méthodes Cache.
27
 *
28
 * En général, toutes les opérations de cache sont prises en charge par tous les moteurs de cache.
29
 * Cependant, Cache::increment() et Cache::decrement() ne sont pas pris en charge par la mise en cache des fichiers.
30
 *
31
 * Il existe 7 moteurs de mise en cache intégrés :
32
 *
33
 * - `Apcu` - Utilise le cache d'objets APCu, l'un des moteurs de mise en cache les plus rapides.
34
 * - `Array` - Utilise uniquement la mémoire pour stocker toutes les données, pas réellement un moteur persistant.
35
 * 			Peut être utile dans un environnement de test ou CLI.
36
 * - `File` - Utilise des fichiers simples pour stocker le contenu. Mauvaises performances, mais bonnes pour
37
 * 			stocker de gros objets ou des choses qui ne sont pas sensibles aux E/S. Bien adapté au développement
38
 * 			car il s'agit d'un cache facile à inspecter et à vider manuellement.
39
 * - `Memcache` - Utilise l'extension PECL::Memcache et Memcached pour le stockage.
40
 * 			Lectures/écritures rapides et avantages de la distribution de Memcache.
41
 * - `Redis` - Utilise l'extension redis et php-redis pour stocker les données de cache.
42
 * - `Wincache` - Utilise l'extension de cache Windows pour PHP. Prend en charge Wincache 1.1.0 et supérieur.
43
 * 			Ce moteur est recommandé aux personnes déployant sur Windows avec IIS.
44
 * - `Xcache` - Utilise l'extension Xcache, une alternative à APCu.
45
 *
46
 * Voir la documentation du moteur de cache pour les clés de configuration attendues.
47
 */
48
class Cache implements CacheInterface
49
{
50
    /**
51
     * Un tableau mappant les schémas d'URL aux noms de classe de moteur de mise en cache complets.
52
     *
53
     * @var array<string, string>
54
     * @psalm-var array<string, class-string>
55
     */
56
    protected static array $validHandlers = [
57
        'apcu'      => Handlers\Apcu::class,
58
        'array'     => Handlers\ArrayHandler::class,
59
        'dummy'     => Handlers\Dummy::class,
60
        'file'      => Handlers\File::class,
61
        'memcached' => Handlers\Memcached::class,
62
        'redis'     => Handlers\RedisHandler::class,
63
        'wincache'  => Handlers\Wincache::class,
64
    ];
65
66
    /**
67
     * Drapeau pour verifier si la mise en cache est activr ou pas.
68
     */
69
    protected static bool $_enabled = true;
70
71
    /**
72
     * Configuration des caches
73
     */
74
    protected array $config = [];
75
76
    /**
77
     * Adapter a utiliser pour la mise en cache
78
     */
79
    private ?CacheInterface $adapter;
80
81
    /**
82
     * Constructeur
83
     */
84
    public function __construct(array $config = [])
85
    {
86
        $this->setConfig($config);
87
    }
88
89
    /**
90
     * Modifie les configuration du cache pour la fabrique actuelle
91
     */
92
    public function setConfig(array $config): self
93
    {
94
        $this->config  = $config;
95
        $this->adapter = null;
96
97
        return $this;
98
    }
99
100
    /**
101
     * Tente de créer le gestionnaire de cache souhaité
102
     */
103
    protected function factory(): CacheInterface
104
    {
105
        if (! static::$_enabled) {
106
            return new Dummy();
107
        }
108
        if (! empty($this->adapter)) {
109
            return $this->adapter;
110
        }
111
112
        $validHandlers = $this->config['valid_handlers'] ?? self::$validHandlers;
113
114
        if (empty($validHandlers) || ! is_array($validHandlers)) {
115
            throw new InvalidArgumentException('La configuration du cache doit avoir un tableau de $valid_handlers.');
116
        }
117
118
        $handler  = $this->config['handler'] ?? null;
119
        $fallback = $this->config['fallback_handler'] ?? null;
120
121
        if (empty($handler)) {
122
            throw new InvalidArgumentException('La configuration du cache doit avoir un ensemble de gestionnaires.');
123
        }
124
125
        if (! array_key_exists($handler, $validHandlers)) {
126
            throw new InvalidArgumentException('La configuration du cache a un gestionnaire non valide spécifié.');
127
        }
128
129
        $adapter = new $validHandlers[$handler]();
130
        if (! ($adapter instanceof BaseHandler)) {
131
            if (empty($fallback)) {
132
                $adapter = new Dummy();
133
            } elseif (! array_key_exists($fallback, $validHandlers)) {
134
                throw new InvalidArgumentException('La configuration du cache a un gestionnaire de secours non valide spécifié.');
135
            } else {
136
                $adapter = new $validHandlers[$fallback]();
137
            }
138
        }
139
140
        if (! ($adapter instanceof BaseHandler)) {
141
            throw new InvalidArgumentException('Le gestionnaire de cache doit utiliser BlitzPHP\Cache\Handlers\BaseHandler comme classe de base.');
142
        }
143
144
        if (! $adapter->init($this->config)) {
145
            throw new RuntimeException(
146
                sprintf(
147
                    'Le moteur de cache %s n\'est pas correctement configuré. Consultez le journal des erreurs pour plus d\'informations.',
148
                    get_class($adapter)
149
                )
150
            );
151
        }
152
153
        return $this->adapter = $adapter;
154
    }
155
156
    /**
157
     * Écrivez les données de la clé dans le cache.
158
     *
159
     * ### Utilisation :
160
     *
161
     * Écriture dans la configuration de cache active :
162
     *
163
     * ```
164
     * $cache->write('cached_data', $data);
165
     * ```
166
     *
167
     * @param mixed                 $value Données à mettre en cache - tout sauf une ressource
168
     * @param DateInterval|int|null $ttl   Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
169
     *                                     le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
170
     *                                     pour cela ou laissez le conducteur s'en occuper.
171
     *
172
     * @return bool Vrai si les données ont été mises en cache avec succès, faux en cas d'échec
173
     */
174
    public function write(string $key, mixed $value, DateInterval|int|null $ttl = null): bool
175
    {
176
        if (is_resource($value)) {
177
            return false;
178
        }
179
180
        $backend = $this->factory();
181
        $success = $backend->set($key, $value, $ttl);
182
        if ($success === false && $value !== '') {
183
            trigger_error(
184
                sprintf(
185
                    "Unable to write '%s' to %s cache",
186
                    $key,
187
                    get_class($backend)
188
                ),
189
                E_USER_WARNING
190
            );
191
        }
192
193
        return $success;
194
    }
195
196
    /**
197
     * {@inheritDoc}
198
     */
199
    public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool
200
    {
201
        return $this->write($key, $value, $ttl);
202
    }
203
204
    /**
205
     * Écrire des données pour de nombreuses clés dans le cache.
206
     *
207
     * ### Utilisation :
208
     *
209
     * Écriture dans la configuration de cache active :
210
     *
211
     * ```
212
     * $cache->writeMany(['cached_data_1' => 'data 1', 'cached_data_2' => 'data 2']);
213
     * ```
214
     *
215
     * @param iterable              $data Un tableau ou Traversable de données à stocker dans le cache
216
     * @param DateInterval|int|null $ttl  Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
217
     *                                    le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
218
     *                                    pour cela ou laissez le conducteur s'en occuper.
219
     *
220
     * @return bool Vrai en cas de succès, faux en cas d'échec
221
     *
222
     * @throws InvalidArgumentException
223
     */
224
    public function writeMany(iterable $data, DateInterval|int|null $ttl = null): bool
225
    {
226
        return $this->factory()->setMultiple($data, $ttl);
227
    }
228
229
    /**
230
     * {@inheritDoc}
231
     */
232
    public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool
233
    {
234
        return $this->writeMany($values, $ttl);
235
    }
236
237
    /**
238
     * Lire une clé du cache.
239
     *
240
     * ### Utilisation :
241
     *
242
     * Lecture à partir de la configuration du cache actif.
243
     *
244
     * ```
245
     * $cache->read('my_data');
246
     */
247
    public function read(string $key, mixed $default = null): mixed
248
    {
249
        return $this->factory()->get($key, $default);
250
    }
251
252
    /**
253
     * {@inheritDoc}
254
     */
255
    public function get(string $key, mixed $default = null): mixed
256
    {
257
        return $this->read($key, $default);
258
    }
259
260
    /**
261
     * Lire plusieurs clés du cache.
262
     *
263
     * ### Utilisation :
264
     *
265
     * Lecture de plusieurs clés à partir de la configuration de cache active.
266
     *
267
     * ```
268
     * $cache->readMany(['my_data_1', 'my_data_2]);
269
     */
270
    public function readMany(iterable $keys, mixed $default = null): iterable
271
    {
272
        return $this->factory()->getMultiple($keys, $default);
273
    }
274
275
    /**
276
     * {@inheritDoc}
277
     */
278
    public function getMultiple(iterable $keys, mixed $default = null): iterable
279
    {
280
        return $this->readMany($keys, $default);
281
    }
282
283
    /**
284
     * Incrémente un nombre sous la clé et renvoie la valeur incrémentée.
285
     *
286
     * @param int $offset Combien ajouter
287
     *
288
     * @return false|int Nouvelle valeur, ou false si la donnée n'existe pas, n'est pas un entier,
289
     *                   ou si une erreur s'est produite lors de sa récupération.
290
     *
291
     * @throws InvalidArgumentException Lorsque décalage < 0
292
     */
293
    public function increment(string $key, int $offset = 1)
294
    {
295
        if ($offset < 0) {
296
            throw new InvalidArgumentException('Le décalage ne peut pas être inférieur à 0.');
297
        }
298
299
        return $this->factory()->increment($key, $offset);
300
    }
301
302
    /**
303
     * Décrémenter un nombre sous la clé et renvoyer la valeur décrémentée.
304
     *
305
     * @param int $offset Combien soustraire
306
     *
307
     * @return false|int Nouvelle valeur, ou false si la donnée n'existe pas, n'est pas un entier,
308
     *                   ou s'il y a eu une erreur lors de sa récupération
309
     *
310
     * @throws InvalidArgumentException lorsque décalage < 0
311
     */
312
    public function decrement(string $key, int $offset = 1)
313
    {
314
        if ($offset < 0) {
315
            throw new InvalidArgumentException('Le décalage ne peut pas être inférieur à 0.');
316
        }
317
318
        return $this->factory()->decrement($key, $offset);
319
    }
320
321
    /**
322
     * Supprimer une clé du cache.
323
     *
324
     * ### Utilisation :
325
     *
326
     * Suppression de la configuration du cache actif.
327
     *
328
     * ```
329
     * $cache->delete('my_data');
330
     * ```
331
     */
332
    public function delete(string $key): bool
333
    {
334
        return $this->factory()->delete($key);
335
    }
336
337
    /**
338
     * Supprimez de nombreuses clés du cache.
339
     *
340
     * ### Utilisation :
341
     *
342
     * Suppression de plusieurs clés de la configuration du cache actif.
343
     *
344
     * ```
345
     * $cache->deleteMany(['my_data_1', 'my_data_2']);
346
     * ```
347
     *
348
     * @param iterable $keys Array ou Traversable de clés de cache à supprimer
349
     *
350
     * @throws InvalidArgumentException
351
     */
352
    public function deleteMany(iterable $keys): bool
353
    {
354
        return $this->factory()->deleteMultiple($keys);
355
    }
356
357
    /**
358
     * {@inheritDoc}
359
     */
360
    public function deleteMultiple(iterable $keys): bool
361
    {
362
        return $this->deleteMany($keys);
363
    }
364
365
    /**
366
     * Supprimez toutes les clés du cache.
367
     */
368
    public function clear(): bool
369
    {
370
        return $this->factory()->clear();
371
    }
372
373
    /**
374
     * Supprimez toutes les clés du cache appartenant au même groupe.
375
     */
376
    public function clearGroup(string $group): bool
377
    {
378
        return $this->factory()->clearGroup($group);
379
    }
380
381
    /**
382
     * Renvoie des informations sur l'ensemble du cache.
383
     *
384
     * Les informations retournées et la structure des données
385
     * varie selon le gestionnaire.
386
     *
387
     * @return array|false|object|null
388
     */
389
    public function info()
390
    {
391
        return $this->factory()->info();
392
    }
393
394
    /**
395
     * Réactivez la mise en cache.
396
     *
397
     * Si la mise en cache a été désactivée avec Cache::disable() cette méthode inversera cet effet.
398
     */
399
    public static function enable(): void
400
    {
401
        static::$_enabled = true;
402
    }
403
404
    /**
405
     * Désactivez la mise en cache.
406
     *
407
     * Lorsqu'il est désactivé, toutes les opérations de cache renverront null.
408
     */
409
    public static function disable(): void
410
    {
411
        static::$_enabled = false;
412
    }
413
414
    /**
415
     * Vérifiez si la mise en cache est activée.
416
     */
417
    public static function enabled(): bool
418
    {
419
        return static::$_enabled;
420
    }
421
422
    /**
423
     * Fournit la possibilité de faire facilement la mise en cache de lecture.
424
     *
425
     * Lorsqu'elle est appelée si la clé $ n'est pas définie dans $config, la fonction $callable
426
     * sera invoqué. Les résultats seront ensuite stockés dans la configuration du cache
427
     * à la clé.
428
     *
429
     * Exemples:
430
     *
431
     * En utilisant une Closure pour fournir des données, supposez que `$this` est un objet Table :
432
     *
433
     * ```
434
     * $resultats = $cache->remember('all_articles', function() {
435
     * 		return $this->find('all')->toArray();
436
     * });
437
     * ```
438
     *
439
     * @param string   $key      La clé de cache sur laquelle lire/stocker les données.
440
     * @param callable $callable Le callback qui fournit des données dans le cas où
441
     *                           la clé de cache est vide. Peut être n'importe quel type appelable pris en charge par votre PHP.
442
     *
443
     * @return mixed Si la clé est trouvée : les données en cache.
444
     *               Si la clé n'est pas trouvée, la valeur renvoyée par le callable.
445
     */
446
    public function remember(string $key, callable $callable): mixed
447
    {
448
        $existing = $this->read($key);
449
        if ($existing !== null) {
450
            return $existing;
451
        }
452
        $results = $callable();
453
        $this->write($key, $results);
454
455
        return $results;
456
    }
457
458
    /**
459
     * Écrivez les données de la clé dans un moteur de cache si elles n'existent pas déjà.
460
     *
461
     * ### Utilisation :
462
     *
463
     * Écriture dans la configuration de cache active :
464
     *
465
     * ```
466
     * $cache->add('cached_data', $data);
467
     * ```
468
     *
469
     * @param mixed $value Données à mettre en cache - tout sauf une ressource.
470
     */
471
    public function add(string $key, mixed $value): bool
472
    {
473
        if (is_resource($value)) {
474
            return false;
475
        }
476
477
        return $this->factory()->add($key, $value);
478
    }
479
480
    /**
481
     * Détermine si un élément est présent dans le cache.
482
     *
483
     * REMARQUE : Il est recommandé que has() ne soit utilisé qu'à des fins de type réchauffement du cache
484
     * et à ne pas utiliser dans vos opérations d'applications en direct pour get/set, car cette méthode
485
     * est soumis à une condition de concurrence où votre has() renverra vrai et immédiatement après,
486
     * un autre script peut le supprimer, rendant l'état de votre application obsolète.
487
     *
488
     * @throws InvalidArgumentException DOIT être lancé si la chaîne $key n'est pas une valeur légale.
489
     */
490
    public function has(string $key): bool
491
    {
492
        return $this->factory()->has($key);
493
    }
494
}
495