Passed
Push — main ( 8f6c6e...4fdfef )
by Dimitri
11:17
created
Labels
Severity
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
	 * @return BaseHandler
104
     */
105
    protected function factory(): CacheInterface
106
    {
107
        if (! static::$_enabled) {
108
            return new Dummy();
109
        }
110
        if (! empty($this->adapter)) {
111
            return $this->adapter;
112
        }
113
114
        $validHandlers = $this->config['valid_handlers'] ?? self::$validHandlers;
115
116
        if (empty($validHandlers) || ! is_array($validHandlers)) {
117
            throw new InvalidArgumentException('La configuration du cache doit avoir un tableau de $valid_handlers.');
118
        }
119
120
        $handler  = $this->config['handler'] ?? null;
121
        $fallback = $this->config['fallback_handler'] ?? null;
122
123
        if (empty($handler)) {
124
            throw new InvalidArgumentException('La configuration du cache doit avoir un ensemble de gestionnaires.');
125
        }
126
127
        if (! array_key_exists($handler, $validHandlers)) {
128
            throw new InvalidArgumentException('La configuration du cache a un gestionnaire non valide spécifié.');
129
        }
130
131
        $adapter = new $validHandlers[$handler]();
132
        if (! ($adapter instanceof BaseHandler)) {
133
            if (empty($fallback)) {
134
                $adapter = new Dummy();
135
            } elseif (! array_key_exists($fallback, $validHandlers)) {
136
                throw new InvalidArgumentException('La configuration du cache a un gestionnaire de secours non valide spécifié.');
137
            } else {
138
                $adapter = new $validHandlers[$fallback]();
139
            }
140
        }
141
142
        if (! ($adapter instanceof BaseHandler)) {
143
            throw new InvalidArgumentException('Le gestionnaire de cache doit utiliser BlitzPHP\Cache\Handlers\BaseHandler comme classe de base.');
144
        }
145
146
        if (isset($this->config[$handler]) && is_array($this->config[$handler])) {
147
            $this->config = array_merge($this->config, $this->config[$handler]);
148
            unset($this->config[$handler]);
149
        }
150
151
        if (! $adapter->init($this->config)) {
152
            throw new RuntimeException(
153
                sprintf(
154
                    'Le moteur de cache %s n\'est pas correctement configuré. Consultez le journal des erreurs pour plus d\'informations.',
155
                    get_class($adapter)
156
                )
157
            );
158
        }
159
160
        return $this->adapter = $adapter;
161
    }
162
163
    /**
164
     * Persiste les données dans le cache, référencées de manière unique par une clé avec un temps d'expiration TTL optionnel.
165
     *
166
     * ### Utilisation :
167
     *
168
     * Écriture dans la configuration de cache active :
169
     *
170
     * ```
171
     * $cache->write('cached_data', $data);
172
     * ```
173
     *
174
     * @param mixed                 $value Données à mettre en cache - tout sauf une ressource
175
     * @param DateInterval|int|null $ttl   Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
176
     *                                     le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
177
     *                                     pour cela ou laissez le conducteur s'en occuper.
178
     *
179
     * @return bool Vrai si les données ont été mises en cache avec succès, faux en cas d'échec
180
     */
181
    public function write(string $key, mixed $value, null|DateInterval|int $ttl = null): bool
182
    {
183
        if (is_resource($value)) {
184
            return false;
185
        }
186
187
        $backend = $this->factory();
188
        $success = $backend->set($key, $value, $ttl);
189
        if ($success === false && $value !== '') {
190
            trigger_error(
191
                sprintf(
192
                    "Unable to write '%s' to %s cache",
193
                    $key,
194
                    get_class($backend)
195
                ),
196
                E_USER_WARNING
197
            );
198
        }
199
200
        return $success;
201
    }
202
203
    /**
204
     * {@inheritDoc}
205
     */
206
    public function set(string $key, mixed $value, null|DateInterval|int $ttl = null): bool
207
    {
208
        return $this->write($key, $value, $ttl);
209
    }
210
211
    /**
212
     * Écrire des données pour de nombreuses clés dans le cache.
213
     *
214
     * ### Utilisation :
215
     *
216
     * Écriture dans la configuration de cache active :
217
     *
218
     * ```
219
     * $cache->writeMany(['cached_data_1' => 'data 1', 'cached_data_2' => 'data 2']);
220
     * ```
221
     *
222
     * @param iterable              $data Un tableau ou Traversable de données à stocker dans le cache
223
     * @param DateInterval|int|null $ttl  Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
224
     *                                    le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
225
     *                                    pour cela ou laissez le conducteur s'en occuper.
226
     *
227
     * @return bool Vrai en cas de succès, faux en cas d'échec
228
     *
229
     * @throws InvalidArgumentException
230
     */
231
    public function writeMany(iterable $data, null|DateInterval|int $ttl = null): bool
232
    {
233
        return $this->factory()->setMultiple($data, $ttl);
234
    }
235
236
    /**
237
     * {@inheritDoc}
238
     */
239
    public function setMultiple(iterable $values, null|DateInterval|int $ttl = null): bool
240
    {
241
        return $this->writeMany($values, $ttl);
242
    }
243
244
    /**
245
     * Récupère une valeur dans le cache.
246
     *
247
     * ### Utilisation :
248
     *
249
     * Lecture à partir de la configuration du cache actif.
250
     *
251
     * ```
252
     * $cache->read('my_data');
253
	 * ```
254
     */
255
    public function read(string $key, mixed $default = null): mixed
256
    {
257
        if (is_callable($default)) {
258
            $_default = $default;
259
            $default  = null;
260
        }
261
262
        $result = $this->factory()->get($key, $default);
263
264
        if (empty($result) && isset($_default)) {
265
            if (function_exists('service')) {
266
                $result = service('container')->call($_default);
267
            } else {
268
                $result = call_user_func($_default);
269
            }
270
        }
271
272
        return $result;
273
    }
274
275
    /**
276
     * {@inheritDoc}
277
     */
278
    public function get(string $key, mixed $default = null): mixed
279
    {
280
        return $this->read($key, $default);
281
    }
282
283
    /**
284
     * Permet d'obtenir plusieurs éléments de cache à partir de leurs clés uniques.
285
     *
286
     * ### Utilisation :
287
     *
288
     * Lecture de plusieurs clés à partir de la configuration de cache active.
289
     *
290
     * ```
291
     * $cache->readMany(['my_data_1', 'my_data_2']);
292
	 * ```
293
     */
294
    public function readMany(iterable $keys, mixed $default = null): iterable
295
    {
296
        if (is_callable($default)) {
297
            $_default = $default;
298
            $default  = null;
299
        }
300
301
        $result = $this->factory()->getMultiple($keys, $default);
302
303
        if (empty($result) && isset($_default)) {
304
            if (function_exists('service')) {
305
                $result = service('container')->call($_default);
306
            } else {
307
                $result = call_user_func($_default);
308
            }
309
        }
310
311
        return $result;
312
    }
313
314
    /**
315
     * {@inheritDoc}
316
     */
317
    public function getMultiple(iterable $keys, mixed $default = null): iterable
318
    {
319
        return $this->readMany($keys, $default);
320
    }
321
322
    /**
323
     * Incrémente un nombre sous la clé et renvoie la valeur incrémentée.
324
     *
325
     * @param int $offset Combien ajouter
326
     *
327
     * @return false|int Nouvelle valeur, ou false si la donnée n'existe pas, n'est pas un entier,
328
     *                   ou si une erreur s'est produite lors de sa récupération.
329
     *
330
     * @throws InvalidArgumentException Lorsque décalage < 0
331
     */
332
    public function increment(string $key, int $offset = 1)
333
    {
334
        if ($offset < 0) {
335
            throw new InvalidArgumentException('Le décalage ne peut pas être inférieur à 0.');
336
        }
337
338
        return $this->factory()->increment($key, $offset);
339
    }
340
341
    /**
342
     * Décrémenter un nombre sous la clé et renvoyer la valeur décrémentée.
343
     *
344
     * @param int $offset Combien soustraire
345
     *
346
     * @return false|int Nouvelle valeur, ou false si la donnée n'existe pas, n'est pas un entier,
347
     *                   ou s'il y a eu une erreur lors de sa récupération
348
     *
349
     * @throws InvalidArgumentException lorsque décalage < 0
350
     */
351
    public function decrement(string $key, int $offset = 1)
352
    {
353
        if ($offset < 0) {
354
            throw new InvalidArgumentException('Le décalage ne peut pas être inférieur à 0.');
355
        }
356
357
        return $this->factory()->decrement($key, $offset);
358
    }
359
360
    /**
361
     * Supprimer une clé du cache.
362
     *
363
     * ### Utilisation :
364
     *
365
     * Suppression de la configuration du cache actif.
366
     *
367
     * ```
368
     * $cache->delete('my_data');
369
     * ```
370
     */
371
    public function delete(string $key): bool
372
    {
373
        return $this->factory()->delete($key);
374
    }
375
376
    /**
377
     * Supprime plusieurs éléments du cache en une seule opération.
378
     *
379
     * ### Utilisation :
380
     *
381
     * Suppression de plusieurs clés de la configuration du cache actif.
382
     *
383
     * ```
384
     * $cache->deleteMany(['my_data_1', 'my_data_2']);
385
     * ```
386
     *
387
     * @param iterable $keys Array ou Traversable de clés de cache à supprimer
388
     *
389
     * @throws InvalidArgumentException
390
     */
391
    public function deleteMany(iterable $keys): bool
392
    {
393
        return $this->factory()->deleteMultiple($keys);
394
    }
395
396
    /**
397
     * {@inheritDoc}
398
     */
399
    public function deleteMultiple(iterable $keys): bool
400
    {
401
        return $this->deleteMany($keys);
402
    }
403
404
    /**
405
     * Supprime toutes les clés du cache.
406
     */
407
    public function clear(): bool
408
    {
409
        return $this->factory()->clear();
410
    }
411
412
    /**
413
     * Supprime toutes les clés du cache appartenant au même groupe.
414
     */
415
    public function clearGroup(string $group): bool
416
    {
417
        return $this->factory()->clearGroup($group);
418
    }
419
420
    /**
421
     * Renvoie des informations sur l'ensemble du cache.
422
     *
423
     * Les informations retournées et la structure des données
424
     * varie selon le gestionnaire.
425
     *
426
     * @return array|false|object|null
427
     */
428
    public function info()
429
    {
430
        return $this->factory()->info();
431
    }
432
433
    /**
434
     * Réactivez la mise en cache.
435
     *
436
     * Si la mise en cache a été désactivée avec Cache::disable() cette méthode inversera cet effet.
437
     */
438
    public static function enable(): void
439
    {
440
        static::$_enabled = true;
441
    }
442
443
    /**
444
     * Désactivez la mise en cache.
445
     *
446
     * Lorsqu'il est désactivé, toutes les opérations de cache renverront null.
447
     */
448
    public static function disable(): void
449
    {
450
        static::$_enabled = false;
451
    }
452
453
    /**
454
     * Vérifiez si la mise en cache est activée.
455
     */
456
    public static function enabled(): bool
457
    {
458
        return static::$_enabled;
459
    }
460
461
    /**
462
     * Fournit la possibilité de faire facilement la mise en cache de lecture.
463
     *
464
     * Lorsqu'elle est appelée si la clé $ n'est pas définie dans $config, la fonction $callable
465
     * sera invoqué. Les résultats seront ensuite stockés dans la configuration du cache
466
     * à la clé.
467
     *
468
     * Exemples:
469
     *
470
     * En utilisant une Closure pour fournir des données, supposez que `$this` est un objet Table :
471
     *
472
     * ```
473
     * $resultats = $cache->remember('all_articles', function() {
474
     * 		return $this->find('all')->toArray();
475
     * });
476
     * ```
477
     *
478
     * @param string   $key      La clé de cache sur laquelle lire/stocker les données.
479
     * @param callable|DateInterval|int|null $ttl   Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
480
     *                                     le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
481
     *                                     pour cela ou laissez le conducteur s'en occuper.
482
     *
483
     * @param callable $callable Le callback qui fournit des données dans le cas où
484
     *                           la clé de cache est vide. Peut être n'importe quel type appelable pris en charge par votre PHP.
485
     *
486
     * @return mixed Si la clé est trouvée : les données en cache.
487
     *               Si la clé n'est pas trouvée, la valeur renvoyée par le callable.
488
     */
489
    public function remember(string $key, callable|DateInterval|int|null $ttl, callable $callable): mixed
490
    {
491
        return $this->factory()->remember($key, $callable, $ttl);
0 ignored issues
show
The method remember() does not exist on BlitzPHP\Cache\CacheInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to BlitzPHP\Cache\CacheInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

491
        return $this->factory()->/** @scrutinizer ignore-call */ remember($key, $callable, $ttl);
Loading history...
492
    }
493
494
    /**
495
     * Écrivez les données de la clé dans un moteur de cache si elles n'existent pas déjà.
496
     *
497
     * ### Utilisation :
498
     *
499
     * Écriture dans la configuration de cache active :
500
     *
501
     * ```
502
     * $cache->add('cached_data', $data);
503
     * ```
504
     *
505
     * @param mixed $value Données à mettre en cache - tout sauf une ressource.
506
     */
507
    public function add(string $key, mixed $value): bool
508
    {
509
        if (is_resource($value)) {
510
            return false;
511
        }
512
513
        return $this->factory()->add($key, $value);
514
    }
515
516
    /**
517
     * Détermine si un élément est présent dans le cache.
518
     *
519
     * REMARQUE : Il est recommandé que has() ne soit utilisé qu'à des fins de type réchauffement du cache
520
     * et à ne pas utiliser dans vos opérations d'applications en direct pour get/set, car cette méthode
521
     * est soumis à une condition de concurrence où votre has() renverra vrai et immédiatement après,
522
     * un autre script peut le supprimer, rendant l'état de votre application obsolète.
523
     *
524
     * @throws InvalidArgumentException DOIT être lancé si la chaîne $key n'est pas une valeur légale.
525
     */
526
    public function has(string $key): bool
527
    {
528
        return $this->factory()->has($key);
529
    }
530
}
531