Cache::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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