BaseHandler   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 424
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 89
c 3
b 1
f 0
dl 0
loc 424
rs 9.1199
wmc 41

16 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 17 4
A ensureValidType() 0 14 5
A setReservedCharacters() 0 3 1
A ensureValidKey() 0 9 5
A groups() 0 3 1
A duration() 0 11 3
A _key() 0 11 2
A getMultiple() 0 11 2
A add() 0 8 2
A deleteMatching() 0 3 1
A has() 0 3 1
A setMultiple() 0 21 5
A deleteMultiple() 0 13 3
A info() 0 3 1
A warning() 0 7 2
A remember() 0 14 3

How to fix   Complexity   

Complex Class

Complex classes like BaseHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BaseHandler, and based on these observations, apply Extract Interface, too.

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\InvalidArgumentException;
15
use BlitzPHP\Contracts\Cache\CacheInterface;
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
        if (isset($config['prefix'])) {
87
            $config['prefix'] = str_replace(' ', '-', strtolower($config['prefix']));
88
        }
89
90
        $this->setConfig($config);
91
92
        if (! empty($this->_config['groups'])) {
93
            sort($this->_config['groups']);
94
            $this->_groupPrefix = str_repeat('%s_', count($this->_config['groups']));
95
        }
96
        if (! is_numeric($this->_config['duration'])) {
97
            $this->_config['duration'] = strtotime($this->_config['duration']) - time();
98
        }
99
100
        return true;
101
    }
102
103
    /**
104
     * Modifie les caractères reservés
105
     */
106
    public function setReservedCharacters(string $reservedCharacters)
107
    {
108
        self::$reservedCharacters = $reservedCharacters;
109
    }
110
111
    /**
112
     * Assurez-vous de la validité de la clé de cache donnée.
113
     *
114
     * @throws InvalidArgumentException Quand la clé n'est pas valide
115
     */
116
    public function ensureValidKey(string $key): void
117
    {
118
        if (! is_string($key) || $key === '') {
0 ignored issues
show
introduced by
The condition is_string($key) is always true.
Loading history...
119
            throw new InvalidArgumentException('Une clé de cache doit être une chaîne non vide.');
120
        }
121
122
        $reserved = self::$reservedCharacters;
123
        if ($reserved && strpbrk($key, $reserved) !== false) {
124
            throw new InvalidArgumentException('La clé de cache contient des caractères réservés ' . $reserved);
125
        }
126
    }
127
128
    /**
129
     * Assurez-vous de la validité du type d'argument et des clés de cache.
130
     *
131
     * @param iterable $iterable L'itérable à vérifier.
132
     * @param string   $check    Indique s'il faut vérifier les clés ou les valeurs.
133
     *
134
     * @throws InvalidArgumentException
135
     */
136
    protected function ensureValidType($iterable, string $check = self::CHECK_VALUE): void
137
    {
138
        if (! is_iterable($iterable)) {
139
            throw new InvalidArgumentException(sprintf(
140
                'Un cache %s doit être soit un tableau soit un Traversable.',
141
                $check === self::CHECK_VALUE ? 'key set' : 'set'
142
            ));
143
        }
144
145
        foreach ($iterable as $key => $value) {
146
            if ($check === self::CHECK_VALUE) {
147
                $this->ensureValidKey($value);
148
            } else {
149
                $this->ensureValidKey($key);
150
            }
151
        }
152
    }
153
154
    /**
155
     * Fournit la possibilité de faire facilement la mise en cache de lecture.
156
     *
157
     * Lorsqu'elle est appelée si la clé $ n'est pas définie dans $config, la fonction $callable
158
     * sera invoqué. Les résultats seront ensuite stockés dans la configuration du cache
159
     * à la clé.
160
     *
161
     * Exemples:
162
     *
163
     * En utilisant une Closure pour fournir des données, supposez que `$this` est un objet Table :
164
     *
165
     * ```
166
     * $resultats = $cache->remember('all_articles', function() {
167
     * 		return $this->find('all')->toArray();
168
     * });
169
     * ```
170
     *
171
     * @param string                         $key      La clé de cache sur laquelle lire/stocker les données.
172
     * @param callable|DateInterval|int|null $ttl      Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
173
     *                                                 le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
174
     *                                                 pour cela ou laissez le conducteur s'en occuper.
175
     * @param callable                       $callable Le callback qui fournit des données dans le cas où
176
     *                                                 la clé de cache est vide. Peut être n'importe quel type appelable pris en charge par votre PHP.
177
     *
178
     * @return mixed Si la clé est trouvée : les données en cache.
179
     *               Si la clé n'est pas trouvée, la valeur renvoyée par le callable.
180
     */
181
    public function remember(string $key, callable|DateInterval|int|null $ttl, ?callable $callable = null): mixed
182
    {
183
        if (is_callable($ttl)) {
184
            $callable = $ttl;
185
            $ttl      = null;
186
        }
187
188
        if (null !== $value = $this->get($key)) {
189
            return $value;
190
        }
191
192
        $this->set($key, $value = $callable(), $ttl);
0 ignored issues
show
Bug introduced by
It seems like $ttl can also be of type callable; however, parameter $ttl of BlitzPHP\Cache\Handlers\BaseHandler::set() 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

192
        $this->set($key, $value = $callable(), /** @scrutinizer ignore-type */ $ttl);
Loading history...
193
194
        return $value;
195
    }
196
197
    /**
198
     * Supprime les éléments du magasin de cache correspondant à un modèle donné.
199
     *
200
     * @param string $pattern Modèle de style global des éléments du cache
201
     *
202
     * @throws Exception
203
     */
204
    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

204
    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...
205
    {
206
        throw new Exception('La méthode deleteMatching n\'est pas implémentée.');
207
    }
208
209
    /**
210
     * Obtient plusieurs éléments de cache par leurs clés uniques.
211
     *
212
     * @param iterable $keys    Une liste de clés pouvant être obtenues en une seule opération.
213
     * @param mixed    $default Valeur par défaut à renvoyer pour les clés qui n'existent pas.
214
     *
215
     * @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.
216
     *
217
     * @throws InvalidArgumentException Si $keys n'est ni un tableau ni un Traversable,
218
     *                                  ou si l'une des clés n'a pas de valeur légale.
219
     */
220
    public function getMultiple(iterable $keys, mixed $default = null): iterable
221
    {
222
        $this->ensureValidType($keys);
223
224
        $results = [];
225
226
        foreach ($keys as $key) {
227
            $results[$key] = $this->get($key, $default);
228
        }
229
230
        return $results;
231
    }
232
233
    /**
234
     * Persiste un ensemble de paires clé => valeur dans le cache, avec un TTL facultatif.
235
     *
236
     * @param iterable              $values Une liste de paires clé => valeur pour une opération sur plusieurs ensembles.
237
     * @param DateInterval|int|null $ttl    Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
238
     *                                      le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
239
     *                                      pour cela ou laissez le conducteur s'en occuper.
240
     *
241
     * @return bool Vrai en cas de succès et faux en cas d'échec.
242
     *
243
     * @throws InvalidArgumentException Si $values n'est ni un tableau ni un Traversable,
244
     *                                  ou si l'une des valeurs $ n'est pas une valeur légale.
245
     */
246
    public function setMultiple(iterable $values, DateInterval|int|null $ttl = null): bool
247
    {
248
        $this->ensureValidType($values, self::CHECK_KEY);
249
250
        if ($ttl !== null) {
251
            $restore = $this->getConfig('duration');
252
            $this->setConfig('duration', $ttl);
253
        }
254
255
        try {
256
            foreach ($values as $key => $value) {
257
                $success = $this->set($key, $value);
258
                if ($success === false) {
259
                    return false;
260
                }
261
            }
262
263
            return true;
264
        } finally {
265
            if (isset($restore)) {
266
                $this->setConfig('duration', $restore);
267
            }
268
        }
269
    }
270
271
    /**
272
     * Supprime plusieurs éléments du cache sous forme de liste
273
     *
274
     * Il s'agit d'une tentative de meilleur effort. Si la suppression d'un élément
275
     * créer une erreur, elle sera ignorée et tous les éléments seront
276
     * être tenté.
277
     *
278
     * @param iterable $keys Une liste de clés basées sur des chaînes à supprimer.
279
     *
280
     * @return bool Vrai si les éléments ont été supprimés avec succès. Faux s'il y a eu une erreur.
281
     *
282
     * @throws InvalidArgumentException Si $keys n'est ni un tableau ni un Traversable,
283
     *                                  ou si l'une des clés $ n'a pas de valeur légale.
284
     */
285
    public function deleteMultiple(iterable $keys): bool
286
    {
287
        $this->ensureValidType($keys);
288
289
        $result = true;
290
291
        foreach ($keys as $key) {
292
            if (! $this->delete($key)) {
293
                $result = false;
294
            }
295
        }
296
297
        return $result;
298
    }
299
300
    /**
301
     * Détermine si un élément est présent dans le cache.
302
     *
303
     * REMARQUE : Il est recommandé que has() ne soit utilisé qu'à des fins de type réchauffement du cache
304
     * et à ne pas utiliser dans vos opérations d'applications en direct pour get/set, car cette méthode
305
     * est soumis à une condition de concurrence où votre has() renverra vrai et immédiatement après,
306
     * un autre script peut le supprimer, rendant l'état de votre application obsolète.
307
     *
308
     * @param mixed $key
309
     *
310
     * @throws InvalidArgumentException Si la chaîne $key n'est pas une valeur légale.
311
     */
312
    public function has(string $key): bool
313
    {
314
        return $this->get($key) !== null;
315
    }
316
317
    /**
318
     * Récupère la valeur d'une clé donnée dans le cache.
319
     */
320
    abstract public function get(string $key, mixed $default = null): mixed;
321
322
    /**
323
     * 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.
324
     *
325
     * @param DateInterval|int|null $ttl Facultatif. La valeur TTL de cet élément. Si aucune valeur n'est envoyée et
326
     *                                   le pilote prend en charge TTL, la bibliothèque peut définir une valeur par défaut
327
     *                                   pour cela ou laissez le conducteur s'en occuper.
328
     *
329
     * @return bool Vrai en cas de succès et faux en cas d'échec.
330
     */
331
    abstract public function set(string $key, mixed $value, DateInterval|int|null $ttl = null): bool;
332
333
    /**
334
     * {@inheritDoc}
335
     */
336
    abstract public function increment(string $key, int $offset = 1);
337
338
    /**
339
     * {@inheritDoc}
340
     */
341
    abstract public function decrement(string $key, int $offset = 1);
342
343
    /**
344
     * {@inheritDoc}
345
     */
346
    abstract public function delete(string $key): bool;
347
348
    /**
349
     * {@inheritDoc}
350
     */
351
    abstract public function clear(): bool;
352
353
    /**
354
     * {@inheritDoc}
355
     */
356
    public function add(string $key, mixed $value): bool
357
    {
358
        $cachedValue = $this->get($key);
359
        if ($cachedValue === null) {
360
            return $this->set($key, $value);
361
        }
362
363
        return false;
364
    }
365
366
    /**
367
     * {@inheritDoc}
368
     */
369
    abstract public function clearGroup(string $group): bool;
370
371
    /**
372
     * {@inheritDoc}
373
     */
374
    public function info()
375
    {
376
        return null;
377
    }
378
379
    /**
380
     * Effectue toute initialisation pour chaque groupe est nécessaire
381
     * et renvoie la "valeur du groupe" pour chacun d'eux, c'est
382
     * le jeton représentant chaque groupe dans la clé de cache
383
     *
384
     * @return list<string>
0 ignored issues
show
Bug introduced by
The type BlitzPHP\Cache\Handlers\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
385
     */
386
    public function groups(): array
387
    {
388
        return $this->_config['groups'];
389
    }
390
391
    /**
392
     * Génère une clé pour l'utilisation du backend du cache.
393
     *
394
     * Si la clé demandée est valide, la valeur du préfixe de groupe et le préfixe du moteur sont appliqués.
395
     * Les espaces blancs dans les clés seront remplacés.
396
     *
397
     * @param string $key la clé transmise
398
     *
399
     * @return string Clé préfixée avec des caractères potentiellement dangereux remplacés.
400
     *
401
     * @throws InvalidArgumentException Si la valeur de la clé n'est pas valide.
402
     */
403
    protected function _key($key): string
404
    {
405
        $this->ensureValidKey($key);
406
407
        $prefix = '';
408
        if ($this->_groupPrefix) {
409
            $prefix = md5(implode('_', $this->groups()));
410
        }
411
        $key = preg_replace('/[\s]+/', '_', $key);
412
413
        return $this->_config['prefix'] . $prefix . $key;
414
    }
415
416
    /**
417
     * Les moteurs de cache peuvent déclencher des avertissements s'ils rencontrent des pannes pendant le fonctionnement,
418
     * si l'option warnOnWriteFailures est définie sur true.
419
     */
420
    protected function warning(string $message): void
421
    {
422
        if ($this->getConfig('warnOnWriteFailures') !== true) {
423
            return;
424
        }
425
426
        Helpers::triggerWarning($message);
427
    }
428
429
    /**
430
     * Convertir les différentes expressions d'une valeur TTL en durée en secondes
431
     *
432
     * @param DateInterval|int|null $ttl La valeur TTL de cet élément. Si null est envoyé,
433
     *                                   La durée par défaut du conducteur sera utilisée.
434
     */
435
    protected function duration(DateInterval|int|null $ttl): int
436
    {
437
        if ($ttl === null) {
438
            return $this->_config['duration'];
439
        }
440
441
        if (is_int($ttl)) {
442
            return $ttl;
443
        }
444
445
        return (int) $ttl->format('%s');
446
    }
447
}
448