CacheManager   C
last analyzed

Complexity

Total Complexity 53

Size/Duplication

Total Lines 407
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 139
dl 0
loc 407
rs 6.96
c 5
b 0
f 0
wmc 53

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getInstances() 0 3 1
A clearInstances() 0 7 1
A removeCoreDriverOverride() 0 13 3
A getDriverList() 0 33 6
A getDefaultConfig() 0 3 1
A normalizeDriverName() 0 3 1
A setDefaultConfig() 0 6 2
A removeCustomDriver() 0 13 3
A addCustomDriver() 0 23 5
A getDefaultNamespacePath() 0 3 1
A addCoreDriverOverride() 0 34 6
A clearInstance() 0 17 2
B getInstance() 0 42 9
A getInstanceHash() 0 6 1
A customDriverExists() 0 3 1
A getInstanceById() 0 7 2
A validateConfig() 0 6 3
A getNamespacePath() 0 3 1
A getDriverClass() 0 13 4

How to fix   Complexity   

Complex Class

Complex classes like CacheManager 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 CacheManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 *
5
 * This file is part of Phpfastcache.
6
 *
7
 * @license MIT License (MIT)
8
 *
9
 * For full copyright and license information, please see the docs/CREDITS.txt and LICENCE files.
10
 *
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 * @author Contributors  https://github.com/PHPSocialNetwork/phpfastcache/graphs/contributors
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phpfastcache;
18
19
use Phpfastcache\Config\ConfigurationOption;
20
use Phpfastcache\Config\ConfigurationOptionInterface;
21
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
22
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException;
23
use Phpfastcache\Exceptions\PhpfastcacheDriverException;
24
use Phpfastcache\Exceptions\PhpfastcacheDriverNotFoundException;
25
use Phpfastcache\Exceptions\PhpfastcacheExtensionNotFoundException;
26
use Phpfastcache\Exceptions\PhpfastcacheExtensionNotInstalledException;
27
use Phpfastcache\Exceptions\PhpfastcacheInstanceNotFoundException;
28
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException;
29
use Phpfastcache\Exceptions\PhpfastcacheLogicException;
30
use Phpfastcache\Exceptions\PhpfastcacheUnsupportedOperationException;
31
use Phpfastcache\Helper\UninstanciableObjectTrait;
32
use Phpfastcache\Util\ClassNamespaceResolverTrait;
33
34
/**
35
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
36
 */
37
class CacheManager
38
{
39
    use ClassNamespaceResolverTrait;
40
    use UninstanciableObjectTrait;
41
42
    public const CORE_DRIVER_NAMESPACE = 'Phpfastcache\Drivers\\';
43
44
    protected static ConfigurationOptionInterface $config;
45
46
    protected static string $namespacePath;
47
48
    /**
49
     * @var ExtendedCacheItemPoolInterface[]
50
     */
51
    protected static array $instances = [];
52
53
    /**
54
     * @var string[]
55
     */
56
    protected static array $driverOverrides = [];
57
58
    /**
59
     * @var string[]
60
     */
61
    protected static array $driverCustoms = [];
62
63
    /**
64
     * @var string[]
65
     */
66
    protected static array $driverExtensions = [];
67
68
    /**
69
     * @param string $instanceId
70
     * @return ExtendedCacheItemPoolInterface
71
     * @throws PhpfastcacheInstanceNotFoundException
72
     */
73
    public static function getInstanceById(string $instanceId): ExtendedCacheItemPoolInterface
74
    {
75
        if (isset(self::$instances[$instanceId])) {
76
            return self::$instances[$instanceId];
77
        }
78
79
        throw new PhpfastcacheInstanceNotFoundException(sprintf('Instance ID %s not found', $instanceId));
80
    }
81
82
    /**
83
     * Return the list of instances
84
     *
85
     * @return ExtendedCacheItemPoolInterface[]
86
     */
87
    public static function getInstances(): array
88
    {
89
        return self::$instances;
90
    }
91
92
    /**
93
     * @param string $driver
94
     * @param ConfigurationOptionInterface|null $config
95
     * @param string|null $instanceId
96
     * @return ExtendedCacheItemPoolInterface
97
     * @throws PhpfastcacheDriverCheckException
98
     * @throws PhpfastcacheDriverException
99
     * @throws PhpfastcacheDriverNotFoundException
100
     * @throws PhpfastcacheExtensionNotInstalledException
101
     * @throws PhpfastcacheLogicException
102
     */
103
    public static function getInstance(string $driver, ?ConfigurationOptionInterface $config = null, ?string $instanceId = null): ExtendedCacheItemPoolInterface
104
    {
105
        if (\class_exists($driver) && \str_starts_with($driver, 'Phpfastcache')) {
106
            $driverClass = $driver;
107
        } else {
108
            $driver = self::normalizeDriverName($driver);
109
            $driverClass = self::getDriverClass($driver);
110
        }
111
        $config = self::validateConfig($config);
112
        $instanceId = $instanceId ?: self::getInstanceHash($driverClass, $config);
113
114
        if (!isset(self::$instances[$instanceId])) {
115
            if (\is_a($driverClass, ExtendedCacheItemPoolInterface::class, true)) {
116
                if (($configClass = $driverClass::getConfigClass()) !== $config::class) {
117
                    $config = new $configClass($config->toArray());
118
                }
119
                self::$instances[$instanceId] = new $driverClass(
120
                    $config,
121
                    $instanceId,
122
                    EventManager::getInstance()
123
                );
124
            } else {
125
                try {
126
                    self::$driverExtensions[$driver] = ExtensionManager::getExtension($driver);
127
                    return CacheManager::getInstance($driver, $config, $instanceId);
128
                } catch (PhpfastcacheExtensionNotFoundException) {
129
                    if (in_array($driver, ExtensionManager::KNOWN_EXTENSION_NAMES, true)) {
130
                        throw new PhpfastcacheExtensionNotInstalledException(sprintf(
131
                            'You requested a driver which is now an extension. Run the following command to solve this issue: %s',
132
                            sprintf('composer install phpfastcache/%s-extension', strtolower($driver))
133
                        ));
134
                    }
135
                    throw new PhpfastcacheDriverNotFoundException(sprintf(
136
                        'The driver "%s" does not exist or does not implement %s.',
137
                        $driver,
138
                        ExtendedCacheItemPoolInterface::class,
139
                    ));
140
                }
141
            }
142
        }
143
144
        return self::$instances[$instanceId];
145
    }
146
147
    /**
148
     * @param string $driverClass
149
     * @param ConfigurationOptionInterface $config
150
     * @return string
151
     */
152
    protected static function getInstanceHash(string $driverClass, ConfigurationOptionInterface $config): string
153
    {
154
        return \md5($driverClass . \serialize(
155
            \array_filter(
156
                $config->toArray(),
157
                static fn ($val) => $config->isValueSerializable($val)
158
            )
159
        ));
160
    }
161
162
    /**
163
     * @param ConfigurationOptionInterface|null $config
164
     * @return ConfigurationOptionInterface
165
     * @throws PhpfastcacheLogicException
166
     */
167
    protected static function validateConfig(?ConfigurationOptionInterface $config): ConfigurationOptionInterface
168
    {
169
        if ($config instanceof ConfigurationOptionInterface && $config->isLocked()) {
170
            throw new PhpfastcacheLogicException('You provided an already locked configuration, cannot continue.');
171
        }
172
        return $config ?? self::getDefaultConfig();
173
    }
174
175
    /**
176
     * @return ConfigurationOptionInterface
177
     */
178
    public static function getDefaultConfig(): ConfigurationOptionInterface
179
    {
180
        return self::$config ?? self::$config = new ConfigurationOption();
181
    }
182
183
    /**
184
     * @param string $driverName
185
     * @return string
186
     */
187
    public static function normalizeDriverName(string $driverName): string
188
    {
189
        return \ucfirst(\strtolower(\trim($driverName)));
190
    }
191
192
    /**
193
     * @param string $driverName
194
     * @return string
195
     */
196
    public static function getDriverClass(string $driverName): string
197
    {
198
        if (!empty(self::$driverExtensions[$driverName])) {
199
            $driverClass = self::$driverExtensions[$driverName];
200
        } elseif (!empty(self::$driverCustoms[$driverName])) {
201
            $driverClass = self::$driverCustoms[$driverName];
202
        } elseif (!empty(self::$driverOverrides[$driverName])) {
203
            $driverClass = self::$driverOverrides[$driverName];
204
        } else {
205
            $driverClass = self::getNamespacePath() . $driverName . '\Driver';
206
        }
207
208
        return $driverClass;
209
    }
210
211
    /**
212
     * @return string
213
     */
214
    public static function getNamespacePath(): string
215
    {
216
        return self::$namespacePath ?? self::getDefaultNamespacePath();
217
    }
218
219
    /**
220
     * @return string
221
     */
222
    public static function getDefaultNamespacePath(): string
223
    {
224
        return self::CORE_DRIVER_NAMESPACE;
225
    }
226
227
    /**
228
     * @return bool
229
     */
230
    public static function clearInstances(): bool
231
    {
232
        self::$instances = [];
233
234
        \gc_collect_cycles();
235
236
        return true;
237
    }
238
239
    /**
240
     * @param ExtendedCacheItemPoolInterface $cachePoolInstance
241
     * @return bool
242
     * @since 7.0.4
243
     */
244
    public static function clearInstance(ExtendedCacheItemPoolInterface $cachePoolInstance): bool
245
    {
246
        $found = false;
247
        self::$instances = \array_filter(
248
            \array_map(
249
                static function (ExtendedCacheItemPoolInterface $cachePool) use ($cachePoolInstance, &$found) {
250
                    if (\spl_object_hash($cachePool) === \spl_object_hash($cachePoolInstance)) {
251
                        $found = true;
252
                        return null;
253
                    }
254
                    return $cachePool;
255
                },
256
                self::$instances
257
            )
258
        );
259
260
        return $found;
261
    }
262
263
    /**
264
     * @param ConfigurationOptionInterface $config
265
     * @throws PhpfastcacheInvalidArgumentException
266
     */
267
    public static function setDefaultConfig(ConfigurationOptionInterface $config): void
268
    {
269
        if (\is_subclass_of($config, ConfigurationOption::class)) {
270
            throw new PhpfastcacheInvalidArgumentException('Default configuration cannot be a child class of ConfigurationOption::class');
271
        }
272
        self::$config = $config;
273
    }
274
275
    /**
276
     * @param string $driverName
277
     * @param string $className
278
     * @return void
279
     * @throws PhpfastcacheLogicException
280
     * @throws PhpfastcacheUnsupportedOperationException
281
     * @throws PhpfastcacheInvalidArgumentException
282
     */
283
    public static function addCustomDriver(string $driverName, string $className): void
284
    {
285
        $driverName = self::normalizeDriverName($driverName);
286
287
        if (empty($driverName)) {
288
            throw new PhpfastcacheInvalidArgumentException("Can't add a custom driver because its name is empty");
289
        }
290
291
        if (!\class_exists($className)) {
292
            throw new PhpfastcacheInvalidArgumentException(
293
                \sprintf("Can't add '%s' because the class '%s' does not exist", $driverName, $className)
294
            );
295
        }
296
297
        if (!empty(self::$driverCustoms[$driverName])) {
298
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' has been already added", $driverName));
299
        }
300
301
        if (\in_array($driverName, self::getDriverList(), true)) {
302
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' is already a part of the Phpfastcache core", $driverName));
303
        }
304
305
        self::$driverCustoms[$driverName] = $className;
306
    }
307
308
    /**
309
     * @param string $driverName
310
     * @return bool
311
     */
312
    public static function customDriverExists(string $driverName): bool
313
    {
314
        return isset(self::$driverCustoms[$driverName]);
315
    }
316
317
    /**
318
     * Return the list of available drivers Capitalized
319
     * with optional FQCN as key
320
     *
321
     * @param bool $fqcnAsKey Describe keys with Full Qualified Class Name
322
     * @return string[]
323
     * @throws PhpfastcacheUnsupportedOperationException
324
     */
325
    public static function getDriverList(bool $fqcnAsKey = false): array
326
    {
327
        static $driverList;
328
329
        if (self::getDefaultNamespacePath() === self::getNamespacePath()) {
330
            if ($driverList === null) {
331
                $prefix = self::CORE_DRIVER_NAMESPACE;
332
                $classMap = self::createClassMap(__DIR__ . '/Drivers');
333
                $driverList = [];
334
335
                foreach (\array_keys($classMap) as $class) {
336
                    $driverList[] = \str_replace($prefix, '', \substr($class, 0, \strrpos($class, '\\')));
337
                }
338
339
                $driverList = \array_values(\array_unique($driverList));
340
            }
341
342
            $driverList = \array_merge($driverList, \array_keys(self::$driverCustoms));
343
344
            if ($fqcnAsKey) {
345
                $realDriverList = [];
346
                foreach ($driverList as $driverName) {
347
                    $realDriverList[self::getDriverClass($driverName)] = $driverName;
348
                }
349
                $driverList = $realDriverList;
350
            }
351
352
            \asort($driverList);
353
354
            return $driverList;
355
        }
356
357
        throw new PhpfastcacheUnsupportedOperationException('Cannot get the driver list if the default namespace path has changed.');
358
    }
359
360
    /**
361
     * @param string $driverName
362
     * @return void
363
     * @throws PhpfastcacheLogicException
364
     * @throws PhpfastcacheInvalidArgumentException
365
     */
366
    public static function removeCustomDriver(string $driverName): void
367
    {
368
        $driverName = self::normalizeDriverName($driverName);
369
370
        if (empty($driverName)) {
371
            throw new PhpfastcacheInvalidArgumentException("Can't remove a custom driver because its name is empty");
372
        }
373
374
        if (!isset(self::$driverCustoms[$driverName])) {
375
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' does not exist", $driverName));
376
        }
377
378
        unset(self::$driverCustoms[$driverName]);
379
    }
380
381
    /**
382
     * @param string $driverName
383
     * @param string $className
384
     * @return void
385
     * @throws PhpfastcacheLogicException
386
     * @throws PhpfastcacheUnsupportedOperationException
387
     * @throws PhpfastcacheInvalidArgumentException
388
     */
389
    public static function addCoreDriverOverride(string $driverName, string $className): void
390
    {
391
        $driverName = self::normalizeDriverName($driverName);
392
393
        if (empty($driverName)) {
394
            throw new PhpfastcacheInvalidArgumentException("Can't add a core driver override because its name is empty");
395
        }
396
397
        if (!\class_exists($className)) {
398
            throw new PhpfastcacheInvalidArgumentException(
399
                \sprintf("Can't override '%s' because the class '%s' does not exist", $driverName, $className)
400
            );
401
        }
402
403
        if (!empty(self::$driverOverrides[$driverName])) {
404
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' has been already overridden", $driverName));
405
        }
406
407
        if (!\in_array($driverName, self::getDriverList(), true)) {
408
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' can't be overridden since its not a part of the Phpfastcache core", $driverName));
409
        }
410
411
        if (!\is_subclass_of($className, self::CORE_DRIVER_NAMESPACE . $driverName . '\\Driver', true)) {
412
            throw new PhpfastcacheLogicException(
413
                \sprintf(
414
                    "Can't override '%s' because the class '%s' MUST extend '%s'",
415
                    $driverName,
416
                    $className,
417
                    self::CORE_DRIVER_NAMESPACE . $driverName . '\\Driver'
418
                )
419
            );
420
        }
421
422
        self::$driverOverrides[$driverName] = $className;
423
    }
424
425
    /**
426
     * @param string $driverName
427
     * @return void
428
     * @throws PhpfastcacheLogicException
429
     * @throws PhpfastcacheInvalidArgumentException
430
     */
431
    public static function removeCoreDriverOverride(string $driverName): void
432
    {
433
        $driverName = self::normalizeDriverName($driverName);
434
435
        if (empty($driverName)) {
436
            throw new PhpfastcacheInvalidArgumentException("Can't remove a core driver override because its name is empty");
437
        }
438
439
        if (!isset(self::$driverOverrides[$driverName])) {
440
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' were not overridden", $driverName));
441
        }
442
443
        unset(self::$driverOverrides[$driverName]);
444
    }
445
}
446