Passed
Push — main ( 8c5693...4676aa )
by Dimitri
02:12
created

Cache::set()   A

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 3
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 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 (isset($this->config[$handler]) && is_array($this->config[$handler])) {
145
			$this->config = array_merge($this->config, $this->config[$handler]);
146
			unset($this->config[$handler]);
147
		}
148
149
        if (! $adapter->init($this->config)) {
150
            throw new RuntimeException(
151
                sprintf(
152
                    'Le moteur de cache %s n\'est pas correctement configuré. Consultez le journal des erreurs pour plus d\'informations.',
153
                    get_class($adapter)
154
                )
155
            );
156
        }
157
158
        return $this->adapter = $adapter;
159
    }
160
161
    /**
162
     * Écrivez les données de la clé dans le cache.
163
     *
164
     * ### Utilisation :
165
     *
166
     * Écriture dans la configuration de cache active :
167
     *
168
     * ```
169
     * $cache->write('cached_data', $data);
170
     * ```
171
     *
172
     * @param mixed                 $value Données à mettre en cache - tout sauf une ressource
173
     * @param DateInterval|int|null $ttl   Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
174
     *                                     le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
175
     *                                     pour cela ou laissez le conducteur s'en occuper.
176
     *
177
     * @return bool Vrai si les données ont été mises en cache avec succès, faux en cas d'échec
178
     */
179
    public function write(string $key, mixed $value, DateInterval|int|null $ttl = null): bool
180
    {
181
        if (is_resource($value)) {
182
            return false;
183
        }
184
185
        $backend = $this->factory();
186
        $success = $backend->set($key, $value, $ttl);
187
        if ($success === false && $value !== '') {
188
            trigger_error(
189
                sprintf(
190
                    "Unable to write '%s' to %s cache",
191
                    $key,
192
                    get_class($backend)
193
                ),
194
                E_USER_WARNING
195
            );
196
        }
197
198
        return $success;
199
    }
200
201
    /**
202
     * {@inheritDoc}
203
     */
204
    public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool
205
    {
206
        return $this->write($key, $value, $ttl);
207
    }
208
209
    /**
210
     * Écrire des données pour de nombreuses clés dans le cache.
211
     *
212
     * ### Utilisation :
213
     *
214
     * Écriture dans la configuration de cache active :
215
     *
216
     * ```
217
     * $cache->writeMany(['cached_data_1' => 'data 1', 'cached_data_2' => 'data 2']);
218
     * ```
219
     *
220
     * @param iterable              $data Un tableau ou Traversable de données à stocker dans le cache
221
     * @param DateInterval|int|null $ttl  Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
222
     *                                    le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
223
     *                                    pour cela ou laissez le conducteur s'en occuper.
224
     *
225
     * @return bool Vrai en cas de succès, faux en cas d'échec
226
     *
227
     * @throws InvalidArgumentException
228
     */
229
    public function writeMany(iterable $data, DateInterval|int|null $ttl = null): bool
230
    {
231
        return $this->factory()->setMultiple($data, $ttl);
232
    }
233
234
    /**
235
     * {@inheritDoc}
236
     */
237
    public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool
238
    {
239
        return $this->writeMany($values, $ttl);
240
    }
241
242
    /**
243
     * Lire une clé du cache.
244
     *
245
     * ### Utilisation :
246
     *
247
     * Lecture à partir de la configuration du cache actif.
248
     *
249
     * ```
250
     * $cache->read('my_data');
251
     */
252
    public function read(string $key, mixed $default = null): mixed
253
    {
254
        return $this->factory()->get($key, $default);
255
    }
256
257
    /**
258
     * {@inheritDoc}
259
     */
260
    public function get(string $key, mixed $default = null): mixed
261
    {
262
        return $this->read($key, $default);
263
    }
264
265
    /**
266
     * Lire plusieurs clés du cache.
267
     *
268
     * ### Utilisation :
269
     *
270
     * Lecture de plusieurs clés à partir de la configuration de cache active.
271
     *
272
     * ```
273
     * $cache->readMany(['my_data_1', 'my_data_2]);
274
     */
275
    public function readMany(iterable $keys, mixed $default = null): iterable
276
    {
277
        return $this->factory()->getMultiple($keys, $default);
278
    }
279
280
    /**
281
     * {@inheritDoc}
282
     */
283
    public function getMultiple(iterable $keys, mixed $default = null): iterable
284
    {
285
        return $this->readMany($keys, $default);
286
    }
287
288
    /**
289
     * Incrémente un nombre sous la clé et renvoie la valeur incrémentée.
290
     *
291
     * @param int $offset Combien ajouter
292
     *
293
     * @return false|int Nouvelle valeur, ou false si la donnée n'existe pas, n'est pas un entier,
294
     *                   ou si une erreur s'est produite lors de sa récupération.
295
     *
296
     * @throws InvalidArgumentException Lorsque décalage < 0
297
     */
298
    public function increment(string $key, int $offset = 1)
299
    {
300
        if ($offset < 0) {
301
            throw new InvalidArgumentException('Le décalage ne peut pas être inférieur à 0.');
302
        }
303
304
        return $this->factory()->increment($key, $offset);
305
    }
306
307
    /**
308
     * Décrémenter un nombre sous la clé et renvoyer la valeur décrémentée.
309
     *
310
     * @param int $offset Combien soustraire
311
     *
312
     * @return false|int Nouvelle valeur, ou false si la donnée n'existe pas, n'est pas un entier,
313
     *                   ou s'il y a eu une erreur lors de sa récupération
314
     *
315
     * @throws InvalidArgumentException lorsque décalage < 0
316
     */
317
    public function decrement(string $key, int $offset = 1)
318
    {
319
        if ($offset < 0) {
320
            throw new InvalidArgumentException('Le décalage ne peut pas être inférieur à 0.');
321
        }
322
323
        return $this->factory()->decrement($key, $offset);
324
    }
325
326
    /**
327
     * Supprimer une clé du cache.
328
     *
329
     * ### Utilisation :
330
     *
331
     * Suppression de la configuration du cache actif.
332
     *
333
     * ```
334
     * $cache->delete('my_data');
335
     * ```
336
     */
337
    public function delete(string $key): bool
338
    {
339
        return $this->factory()->delete($key);
340
    }
341
342
    /**
343
     * Supprimez de nombreuses clés du cache.
344
     *
345
     * ### Utilisation :
346
     *
347
     * Suppression de plusieurs clés de la configuration du cache actif.
348
     *
349
     * ```
350
     * $cache->deleteMany(['my_data_1', 'my_data_2']);
351
     * ```
352
     *
353
     * @param iterable $keys Array ou Traversable de clés de cache à supprimer
354
     *
355
     * @throws InvalidArgumentException
356
     */
357
    public function deleteMany(iterable $keys): bool
358
    {
359
        return $this->factory()->deleteMultiple($keys);
360
    }
361
362
    /**
363
     * {@inheritDoc}
364
     */
365
    public function deleteMultiple(iterable $keys): bool
366
    {
367
        return $this->deleteMany($keys);
368
    }
369
370
    /**
371
     * Supprimez toutes les clés du cache.
372
     */
373
    public function clear(): bool
374
    {
375
        return $this->factory()->clear();
376
    }
377
378
    /**
379
     * Supprimez toutes les clés du cache appartenant au même groupe.
380
     */
381
    public function clearGroup(string $group): bool
382
    {
383
        return $this->factory()->clearGroup($group);
384
    }
385
386
    /**
387
     * Renvoie des informations sur l'ensemble du cache.
388
     *
389
     * Les informations retournées et la structure des données
390
     * varie selon le gestionnaire.
391
     *
392
     * @return array|false|object|null
393
     */
394
    public function info()
395
    {
396
        return $this->factory()->info();
397
    }
398
399
    /**
400
     * Réactivez la mise en cache.
401
     *
402
     * Si la mise en cache a été désactivée avec Cache::disable() cette méthode inversera cet effet.
403
     */
404
    public static function enable(): void
405
    {
406
        static::$_enabled = true;
407
    }
408
409
    /**
410
     * Désactivez la mise en cache.
411
     *
412
     * Lorsqu'il est désactivé, toutes les opérations de cache renverront null.
413
     */
414
    public static function disable(): void
415
    {
416
        static::$_enabled = false;
417
    }
418
419
    /**
420
     * Vérifiez si la mise en cache est activée.
421
     */
422
    public static function enabled(): bool
423
    {
424
        return static::$_enabled;
425
    }
426
427
    /**
428
     * Fournit la possibilité de faire facilement la mise en cache de lecture.
429
     *
430
     * Lorsqu'elle est appelée si la clé $ n'est pas définie dans $config, la fonction $callable
431
     * sera invoqué. Les résultats seront ensuite stockés dans la configuration du cache
432
     * à la clé.
433
     *
434
     * Exemples:
435
     *
436
     * En utilisant une Closure pour fournir des données, supposez que `$this` est un objet Table :
437
     *
438
     * ```
439
     * $resultats = $cache->remember('all_articles', function() {
440
     * 		return $this->find('all')->toArray();
441
     * });
442
     * ```
443
     *
444
     * @param string   $key      La clé de cache sur laquelle lire/stocker les données.
445
     * @param DateInterval|int|null $ttl   Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
446
     *                                     le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
447
     *                                     pour cela ou laissez le conducteur s'en occuper.
448
     *
449
     * @param callable $callable Le callback qui fournit des données dans le cas où
450
     *                           la clé de cache est vide. Peut être n'importe quel type appelable pris en charge par votre PHP.
451
     *
452
     * @return mixed Si la clé est trouvée : les données en cache.
453
     *               Si la clé n'est pas trouvée, la valeur renvoyée par le callable.
454
     */
455
    public function remember(string $key, callable|DateInterval|int|null $ttl, callable $callable): mixed
456
    {
457
        if (is_callable($ttl)) {
458
            $callable = $ttl;
459
            $ttl      = null;
460
        }
461
462
        $existing = $this->read($key);
463
        if ($existing !== null) {
464
            return $existing;
465
        }
466
        $results = $callable();
467
        $this->write($key, $results, $ttl);
0 ignored issues
show
Bug introduced by
It seems like $ttl can also be of type callable; however, parameter $ttl of BlitzPHP\Cache\Cache::write() does only seem to accept DateInterval|integer|null, maybe add an additional type check? ( Ignorable by Annotation )

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

467
        $this->write($key, $results, /** @scrutinizer ignore-type */ $ttl);
Loading history...
468
469
        return $results;
470
    }
471
472
    /**
473
     * Écrivez les données de la clé dans un moteur de cache si elles n'existent pas déjà.
474
     *
475
     * ### Utilisation :
476
     *
477
     * Écriture dans la configuration de cache active :
478
     *
479
     * ```
480
     * $cache->add('cached_data', $data);
481
     * ```
482
     *
483
     * @param mixed $value Données à mettre en cache - tout sauf une ressource.
484
     */
485
    public function add(string $key, mixed $value): bool
486
    {
487
        if (is_resource($value)) {
488
            return false;
489
        }
490
491
        return $this->factory()->add($key, $value);
492
    }
493
494
    /**
495
     * Détermine si un élément est présent dans le cache.
496
     *
497
     * REMARQUE : Il est recommandé que has() ne soit utilisé qu'à des fins de type réchauffement du cache
498
     * et à ne pas utiliser dans vos opérations d'applications en direct pour get/set, car cette méthode
499
     * est soumis à une condition de concurrence où votre has() renverra vrai et immédiatement après,
500
     * un autre script peut le supprimer, rendant l'état de votre application obsolète.
501
     *
502
     * @throws InvalidArgumentException DOIT être lancé si la chaîne $key n'est pas une valeur légale.
503
     */
504
    public function has(string $key): bool
505
    {
506
        return $this->factory()->has($key);
507
    }
508
}
509