Passed
Push — v9 ( e6f9d3...0a233b )
by Georges
02:05
created

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