Cache::getMultiple()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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