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

BaseHandler::info()   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
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\Handlers;
13
14
use BlitzPHP\Cache\CacheInterface;
15
use BlitzPHP\Cache\InvalidArgumentException;
16
use BlitzPHP\Traits\InstanceConfigTrait;
17
use BlitzPHP\Utilities\Helpers;
18
use Closure;
19
use DateInterval;
20
use Exception;
21
22
abstract class BaseHandler implements CacheInterface
23
{
24
    use InstanceConfigTrait;
25
26
    /**
27
     * @var string
28
     */
29
    protected const CHECK_KEY = 'key';
30
31
    /**
32
     * @var string
33
     */
34
    protected const CHECK_VALUE = 'value';
35
36
    /**
37
     * Caractères réservés qui ne peuvent pas être utilisés dans une clé ou une étiquette. Peut être remplacé par le fichier config.
38
     * From https://github.com/symfony/cache-contracts/blob/c0446463729b89dd4fa62e9aeecc80287323615d/ItemInterface.php#L43
39
     */
40
    protected static string $reservedCharacters = '{}()/\@:';
41
42
    /**
43
     * Préfixe à appliquer aux clés de cache.
44
     * Ne peut pas être utilisé par tous les gestionnaires.
45
     */
46
    protected string $prefix;
47
48
    /**
49
     * La configuration de cache par défaut est remplacée dans la plupart des adaptateurs de cache. Ceux-ci sont
50
     * les clés communes à tous les adaptateurs. Si elle est remplacée, cette propriété n'est pas utilisée.
51
     *
52
     * - `duration` Spécifiez combien de temps durent les éléments de cette configuration de cache.
53
     * - `groups` Liste des groupes ou 'tags' associés à chaque clé stockée dans cette configuration.
54
     * 			pratique pour supprimer un groupe complet du cache.
55
     * - `prefix` Préfixe ajouté à toutes les entrées. Bon pour quand vous avez besoin de partager un keyspace
56
     * 			avec une autre configuration de cache ou une autre application.
57
     * - `warnOnWriteFailures` Certains moteurs, tels que ApcuEngine, peuvent déclencher des avertissements en cas d'échecs d'écriture.
58
     *
59
     * @var array<string, mixed>
60
     */
61
    protected array $_defaultConfig = [
62
        'duration'            => 3600,
63
        'groups'              => [],
64
        'prefix'              => 'blitz_',
65
        'warnOnWriteFailures' => true,
66
    ];
67
68
    /**
69
     * Contient la chaîne compilée avec tous les groupes
70
     * préfixes à ajouter à chaque clé dans ce moteur de cache
71
     */
72
    protected string $_groupPrefix = '';
73
74
    /**
75
     * Initialiser le moteur de cache
76
     *
77
     * Appelé automatiquement par le frontal du cache. Fusionner la configuration d'exécution avec les valeurs par défaut
78
     * Avant utilisation.
79
     *
80
     * @param array<string, mixed> $config Tableau associatif de paramètres pour le moteur
81
     *
82
     * @return bool Vrai si le moteur a été initialisé avec succès, faux sinon
83
     */
84
    public function init(array $config = []): bool
85
    {
86
        $this->setConfig($config);
87
88
        if (! empty($this->_config['groups'])) {
89
            sort($this->_config['groups']);
90
            $this->_groupPrefix = str_repeat('%s_', count($this->_config['groups']));
91
        }
92
        if (! is_numeric($this->_config['duration'])) {
93
            $this->_config['duration'] = strtotime($this->_config['duration']) - time();
94
        }
95
96
        return true;
97
    }
98
99
    /**
100
     * Modifie les caractères reservés
101
     */
102
    public function setReservedCharacters(string $reservedCharacters)
103
    {
104
        self::$reservedCharacters = $reservedCharacters;
105
    }
106
107
    /**
108
     * Assurez-vous de la validité de la clé de cache donnée.
109
     *
110
     * @throws InvalidArgumentException Quand la clé n'est pas valide
111
     */
112
    public function ensureValidKey(string $key): void
113
    {
114
        if (! is_string($key) || $key === '') {
0 ignored issues
show
introduced by
The condition is_string($key) is always true.
Loading history...
115
            throw new InvalidArgumentException('Une clé de cache doit être une chaîne non vide.');
116
        }
117
118
        $reserved = self::$reservedCharacters;
119
        if ($reserved && strpbrk($key, $reserved) !== false) {
120
            throw new InvalidArgumentException('La clé de cache contient des caractères réservés ' . $reserved);
121
        }
122
    }
123
124
    /**
125
     * Assurez-vous de la validité du type d'argument et des clés de cache.
126
     *
127
     * @param iterable $iterable L'itérable à vérifier.
128
     * @param string   $check    Indique s'il faut vérifier les clés ou les valeurs.
129
     *
130
     * @throws InvalidArgumentException
131
     */
132
    protected function ensureValidType($iterable, string $check = self::CHECK_VALUE): void
133
    {
134
        if (! is_iterable($iterable)) {
135
            throw new InvalidArgumentException(sprintf(
136
                'Un cache %s doit être soit un tableau soit un Traversable.',
137
                $check === self::CHECK_VALUE ? 'key set' : 'set'
138
            ));
139
        }
140
141
        foreach ($iterable as $key => $value) {
142
            if ($check === self::CHECK_VALUE) {
143
                $this->ensureValidKey($value);
144
            } else {
145
                $this->ensureValidKey($key);
146
            }
147
        }
148
    }
149
150
    /**
151
     * Obtenez un élément du cache ou exécutez la fermeture donnée et stockez le résultat.
152
     *
153
     * @param Closure $callback Valeur de retour du rappel
154
     */
155
    public function remember(string $key, int $ttl, Closure $callback): mixed
156
    {
157
        $value = $this->get($key);
158
159
        if ($value !== null) {
160
            return $value;
161
        }
162
163
        $this->set($key, $value = $callback(), $ttl);
164
165
        return $value;
166
    }
167
168
    /**
169
     * Supprime les éléments du magasin de cache correspondant à un modèle donné.
170
     *
171
     * @param string $pattern Modèle de style global des éléments du cache
172
     *
173
     * @throws Exception
174
     */
175
    public function deleteMatching(string $pattern)
0 ignored issues
show
Unused Code introduced by
The parameter $pattern is not used and could be removed. ( Ignorable by Annotation )

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

175
    public function deleteMatching(/** @scrutinizer ignore-unused */ string $pattern)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
176
    {
177
        throw new Exception('La méthode deleteMatching n\'est pas implémentée.');
178
    }
179
180
    /**
181
     * Obtient plusieurs éléments de cache par leurs clés uniques.
182
     *
183
     * @param iterable $keys    Une liste de clés pouvant être obtenues en une seule opération.
184
     * @param mixed    $default Valeur par défaut à renvoyer pour les clés qui n'existent pas.
185
     *
186
     * @return iterable Une liste de paires clé-valeur. Les clés de cache qui n'existent pas ou qui sont obsolètes auront $default comme valeur.
187
     *
188
     * @throws InvalidArgumentException Si $keys n'est ni un tableau ni un Traversable,
189
     *                                  ou si l'une des clés n'a pas de valeur légale.
190
     */
191
    public function getMultiple(iterable $keys, mixed $default = null): iterable
192
    {
193
        $this->ensureValidType($keys);
194
195
        $results = [];
196
197
        foreach ($keys as $key) {
198
            $results[$key] = $this->get($key, $default);
199
        }
200
201
        return $results;
202
    }
203
204
    /**
205
     * Persiste un ensemble de paires clé => valeur dans le cache, avec un TTL facultatif.
206
     *
207
     * @param iterable              $values Une liste de paires clé => valeur pour une opération sur plusieurs ensembles.
208
     * @param DateInterval|int|null $ttl    Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
209
     *                                      le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
210
     *                                      pour cela ou laissez le conducteur s'en occuper.
211
     *
212
     * @return bool Vrai en cas de succès et faux en cas d'échec.
213
     *
214
     * @throws InvalidArgumentException Si $values n'est ni un tableau ni un Traversable,
215
     *                                  ou si l'une des valeurs $ n'est pas une valeur légale.
216
     */
217
    public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool
218
    {
219
        $this->ensureValidType($values, self::CHECK_KEY);
220
221
        if ($ttl !== null) {
222
            $restore = $this->getConfig('duration');
223
            $this->setConfig('duration', $ttl);
224
        }
225
226
        try {
227
            foreach ($values as $key => $value) {
228
                $success = $this->set($key, $value);
229
                if ($success === false) {
230
                    return false;
231
                }
232
            }
233
234
            return true;
235
        } finally {
236
            if (isset($restore)) {
237
                $this->setConfig('duration', $restore);
238
            }
239
        }
240
    }
241
242
    /**
243
     * Supprime plusieurs éléments du cache sous forme de liste
244
     *
245
     * Il s'agit d'une tentative de meilleur effort. Si la suppression d'un élément
246
     * créer une erreur, elle sera ignorée et tous les éléments seront
247
     * être tenté.
248
     *
249
     * @param iterable $keys Une liste de clés basées sur des chaînes à supprimer.
250
     *
251
     * @return bool Vrai si les éléments ont été supprimés avec succès. Faux s'il y a eu une erreur.
252
     *
253
     * @throws InvalidArgumentException Si $keys n'est ni un tableau ni un Traversable,
254
     *                                  ou si l'une des clés $ n'a pas de valeur légale.
255
     */
256
    public function deleteMultiple(iterable $keys): bool
257
    {
258
        $this->ensureValidType($keys);
259
260
        $result = true;
261
262
        foreach ($keys as $key) {
263
            if (! $this->delete($key)) {
264
                $result = false;
265
            }
266
        }
267
268
        return $result;
269
    }
270
271
    /**
272
     * Détermine si un élément est présent dans le cache.
273
     *
274
     * REMARQUE : Il est recommandé que has() ne soit utilisé qu'à des fins de type réchauffement du cache
275
     * et à ne pas utiliser dans vos opérations d'applications en direct pour get/set, car cette méthode
276
     * est soumis à une condition de concurrence où votre has() renverra vrai et immédiatement après,
277
     * un autre script peut le supprimer, rendant l'état de votre application obsolète.
278
     *
279
     * @param mixed $key
280
     *
281
     * @throws InvalidArgumentException Si la chaîne $key n'est pas une valeur légale.
282
     */
283
    public function has(string $key): bool
284
    {
285
        return $this->get($key) !== null;
286
    }
287
288
    /**
289
     * Récupère la valeur d'une clé donnée dans le cache.
290
     */
291
    abstract public function get(string $key, mixed $default = null): mixed;
292
293
    /**
294
     * Persiste les données dans le cache, référencées de manière unique par la clé donnée avec un temps TTL d'expiration facultatif.
295
     *
296
     * @param DateInterval|int|null $ttl Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
297
     *                                   le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
298
     *                                   pour cela ou laissez le conducteur s'en occuper.
299
     *
300
     * @return bool Vrai en cas de succès et faux en cas d'échec.
301
     */
302
    abstract public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool;
303
304
    /**
305
     * {@inheritDoc}
306
     */
307
    abstract public function increment(string $key, int $offset = 1);
308
309
    /**
310
     * {@inheritDoc}
311
     */
312
    abstract public function decrement(string $key, int $offset = 1);
313
314
    /**
315
     * {@inheritDoc}
316
     */
317
    abstract public function delete(string $key): bool;
318
319
    /**
320
     * {@inheritDoc}
321
     */
322
    abstract public function clear(): bool;
323
324
    /**
325
     * {@inheritDoc}
326
     */
327
    public function add(string $key, mixed $value): bool
328
    {
329
        $cachedValue = $this->get($key);
330
        if ($cachedValue === null) {
331
            return $this->set($key, $value);
332
        }
333
334
        return false;
335
    }
336
337
    /**
338
     * {@inheritDoc}
339
     */
340
    abstract public function clearGroup(string $group): bool;
341
342
	/**
343
	 * {@inheritDoc}
344
	 */
345
	public function info()
346
	{
347
		return null;
348
	}
349
350
    /**
351
     * Effectue toute initialisation pour chaque groupe est nécessaire
352
     * et renvoie la "valeur du groupe" pour chacun d'eux, c'est
353
     * le jeton représentant chaque groupe dans la clé de cache
354
     *
355
     * @return string[]
356
     */
357
    public function groups(): array
358
    {
359
        return $this->_config['groups'];
360
    }
361
362
    /**
363
     * Génère une clé pour l'utilisation du backend du cache.
364
     *
365
     * Si la clé demandée est valide, la valeur du préfixe de groupe et le préfixe du moteur sont appliqués.
366
     * Les espaces blancs dans les clés seront remplacés.
367
     *
368
     * @param string $key la clé transmise
369
     *
370
     * @return string Clé préfixée avec des caractères potentiellement dangereux remplacés.
371
     *
372
     * @throws InvalidArgumentException Si la valeur de la clé n'est pas valide.
373
     */
374
    protected function _key($key): string
375
    {
376
        $this->ensureValidKey($key);
377
378
        $prefix = '';
379
        if ($this->_groupPrefix) {
380
            $prefix = md5(implode('_', $this->groups()));
381
        }
382
        $key = preg_replace('/[\s]+/', '_', $key);
383
384
        return $this->_config['prefix'] . $prefix . $key;
385
    }
386
387
    /**
388
     * Les moteurs de cache peuvent déclencher des avertissements s'ils rencontrent des pannes pendant le fonctionnement,
389
     * si l'option warnOnWriteFailures est définie sur true.
390
     */
391
    protected function warning(string $message): void
392
    {
393
        if ($this->getConfig('warnOnWriteFailures') !== true) {
394
            return;
395
        }
396
397
        Helpers::triggerWarning($message);
398
    }
399
400
    /**
401
     * Convertir les différentes expressions d'une valeur TTL en durée en secondes
402
     *
403
     * @param DateInterval|int|null $ttl La valeur TTL de cet élément. Si null est envoyé,
404
     *                                   La durée par défaut du conducteur sera utilisée.
405
     */
406
    protected function duration(DateInterval|int|null $ttl): int
407
    {
408
        if ($ttl === null) {
409
            return $this->_config['duration'];
410
        }
411
412
        if (is_int($ttl)) {
413
            return $ttl;
414
        }
415
416
        return (int) $ttl->format('%s');
417
    }
418
}
419