Completed
Push — master ( be9660...707803 )
by Georges
16s queued 13s
created

CacheManager::getInstance()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 18
c 0
b 0
f 0
nc 4
nop 3
dl 0
loc 30
rs 9.3554
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 file.
10
 *
11
 * @author Khoa Bui (khoaofgod)  <[email protected]> https://www.phpfastcache.com
12
 * @author Georges.L (Geolim4)  <[email protected]>
13
 *
14
 */
15
declare(strict_types=1);
16
17
namespace Phpfastcache;
18
19
use Phpfastcache\Cluster\AggregatablePoolInterface;
20
use Phpfastcache\Config\ConfigurationOption;
21
use Phpfastcache\Config\ConfigurationOptionInterface;
22
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
23
use Phpfastcache\Exceptions\{PhpfastcacheDriverCheckException,
24
    PhpfastcacheDriverException,
25
    PhpfastcacheDriverNotFoundException,
26
    PhpfastcacheInstanceNotFoundException,
27
    PhpfastcacheInvalidArgumentException,
28
    PhpfastcacheInvalidConfigurationException,
29
    PhpfastcacheLogicException,
30
    PhpfastcacheUnsupportedOperationException
31
};
32
use Phpfastcache\Util\ClassNamespaceResolverTrait;
33
34
35
/**
36
 * Class CacheManager
37
 * @package phpFastCache
38
 *
39
 * @method static ExtendedCacheItemPoolInterface Apcu() Apcu($config = []) Return a driver "Apcu" instance
40
 * @method static ExtendedCacheItemPoolInterface Cassandra() Cassandra($config = []) Return a driver "Cassandra" instance
41
 * @method static ExtendedCacheItemPoolInterface Cookie() Cookie($config = []) Return a driver "Cookie" instance
42
 * @method static ExtendedCacheItemPoolInterface Couchbase() Couchbase($config = []) Return a driver "Couchbase" instance
43
 * @method static ExtendedCacheItemPoolInterface Couchdb() Couchdb($config = []) Return a driver "Couchdb" instance
44
 * @method static ExtendedCacheItemPoolInterface Devnull() Devnull($config = []) Return a driver "Devnull" instance
45
 * @method static ExtendedCacheItemPoolInterface Files() Files($config = []) Return a driver "files" instance
46
 * @method static ExtendedCacheItemPoolInterface Leveldb() Leveldb($config = []) Return a driver "Leveldb" instance
47
 * @method static ExtendedCacheItemPoolInterface Memcache() Memcache($config = []) Return a driver "Memcache" instance
48
 * @method static ExtendedCacheItemPoolInterface Memcached() Memcached($config = []) Return a driver "Memcached" instance
49
 * @method static ExtendedCacheItemPoolInterface Memstatic() Memstatic($config = []) Return a driver "Memstatic" instance
50
 * @method static ExtendedCacheItemPoolInterface Mongodb() Mongodb($config = []) Return a driver "Mongodb" instance
51
 * @method static ExtendedCacheItemPoolInterface Predis() Predis($config = []) Return a driver "Predis" instance
52
 * @method static ExtendedCacheItemPoolInterface Redis() Redis($config = []) Return a driver "Pedis" instance
53
 * @method static ExtendedCacheItemPoolInterface Riak() Riak($config = []) Return a driver "Riak" instance
54
 * @method static ExtendedCacheItemPoolInterface Sqlite() Sqlite($config = []) Return a driver "Sqlite" instance
55
 * @method static ExtendedCacheItemPoolInterface Ssdb() Ssdb($config = []) Return a driver "Ssdb" instance
56
 * @method static ExtendedCacheItemPoolInterface Wincache() Wincache($config = []) Return a driver "Wincache" instance
57
 * @method static ExtendedCacheItemPoolInterface Zenddisk() Zenddisk($config = []) Return a driver "Zend disk cache" instance
58
 * @method static ExtendedCacheItemPoolInterface Zendshm() Zendshm($config = []) Return a driver "Zend memory cache" instance
59
 *
60
 */
61
class CacheManager
62
{
63
    public const CORE_DRIVER_NAMESPACE = 'Phpfastcache\Drivers\\';
64
65
    use ClassNamespaceResolverTrait;
66
67
    /**
68
     * @var ConfigurationOption
69
     */
70
    protected static $config;
71
    /**
72
     * @var string
73
     */
74
    protected static $namespacePath;
75
76
    /**
77
     * @var ExtendedCacheItemPoolInterface[]
78
     */
79
    protected static $instances = [];
80
81
    /**
82
     * @var array
83
     */
84
    protected static $driverOverrides = [];
85
86
    /**
87
     * @var array
88
     */
89
    protected static $driverCustoms = [];
90
91
    /**
92
     * @var array
93
     */
94
    protected static $badPracticeOmeter = [];
95
96
    /**
97
     * CacheManager constructor.
98
     */
99
    final protected function __construct()
100
    {
101
        // The cache manager is not meant to be instantiated
102
    }
103
104
    /**
105
     * @param string $instanceId
106
     * @return ExtendedCacheItemPoolInterface
107
     * @throws PhpfastcacheInstanceNotFoundException
108
     */
109
    public static function getInstanceById(string $instanceId): ExtendedCacheItemPoolInterface
110
    {
111
        if (isset(self::$instances[$instanceId])) {
112
            return self::$instances[$instanceId];
113
        }
114
115
        throw new PhpfastcacheInstanceNotFoundException(sprintf('Instance ID %s not found', $instanceId));
116
    }
117
118
    /**
119
     * Return the list of instances
120
     *
121
     * @return ExtendedCacheItemPoolInterface[]
122
     */
123
    public static function getInstances(): array
124
    {
125
        return self::$instances;
126
    }
127
128
    /**
129
     * This method is intended for internal
130
     * use only and should not be used for
131
     * any external development use the
132
     * getInstances() method instead
133
     *
134
     * @return ExtendedCacheItemPoolInterface[]
135
     * @internal
136
     * @todo Use a proper way to passe them as a reference ?
137
     */
138
    public static function &getInternalInstances(): array
139
    {
140
        return self::$instances;
141
    }
142
143
    /**
144
     * @param string $name
145
     * @param array $arguments
146
     * @return ExtendedCacheItemPoolInterface
147
     * @throws PhpfastcacheDriverCheckException
148
     * @throws PhpfastcacheDriverException
149
     * @throws PhpfastcacheDriverNotFoundException
150
     * @throws PhpfastcacheInvalidArgumentException
151
     * @throws PhpfastcacheInvalidConfigurationException
152
     * @throws PhpfastcacheLogicException
153
     * @throws \ReflectionException
154
     */
155
    public static function __callStatic(string $name, array $arguments): ExtendedCacheItemPoolInterface
156
    {
157
        $options = (\array_key_exists(0, $arguments) && \is_array($arguments) ? $arguments[0] : []);
158
159
        return self::getInstance($name, $options);
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type array; however, parameter $config of Phpfastcache\CacheManager::getInstance() does only seem to accept Phpfastcache\Config\Conf...ionOptionInterface|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

159
        return self::getInstance($name, /** @scrutinizer ignore-type */ $options);
Loading history...
160
    }
161
162
    /**
163
     * @param string $driver
164
     * @param ConfigurationOptionInterface $config
165
     * @param string|null $instanceId
166
     * @return ExtendedCacheItemPoolInterface|AggregatablePoolInterface
167
     * @throws PhpfastcacheDriverCheckException
168
     * @throws PhpfastcacheDriverException
169
     * @throws PhpfastcacheDriverNotFoundException
170
     * @throws PhpfastcacheInvalidArgumentException
171
     * @throws PhpfastcacheInvalidConfigurationException
172
     * @throws PhpfastcacheLogicException
173
     * @throws \ReflectionException
174
     */
175
    public static function getInstance(string $driver, ?ConfigurationOptionInterface $config = null, ?string $instanceId = null): ExtendedCacheItemPoolInterface
176
    {
177
        $config = self::validateConfig($config);
178
        $driver = self::standardizeDriverName($driver);
179
180
        $instanceId = $instanceId ?: md5($driver . serialize($config->toArray()));
181
182
        if (!isset(self::$instances[$instanceId])) {
183
            self::$badPracticeOmeter[$driver] = 1;
184
            $driverClass = self::validateDriverClass(self::getDriverClass($driver));
185
186
            if (class_exists($driverClass)) {
187
                $configClass = $driverClass::getConfigClass();
188
                self::$instances[$instanceId] = new $driverClass(new $configClass($config->toArray()), $instanceId);
189
                self::$instances[$instanceId]->setEventManager(EventManager::getInstance());
190
            } else {
191
                throw new PhpfastcacheDriverNotFoundException(sprintf('The driver "%s" does not exists', $driver));
192
            }
193
        } else {
194
            if (self::$badPracticeOmeter[$driver] >= 2) {
195
                trigger_error(
196
                    '[' . $driver . '] Calling many times CacheManager::getInstance() for already instanced drivers is a bad practice and have a significant impact on performances.
197
           See https://github.com/PHPSocialNetwork/phpfastcache/wiki/[V5]-Why-calling-getInstance%28%29-each-time-is-a-bad-practice-%3F'
198
                );
199
            }
200
        }
201
202
        self::$badPracticeOmeter[$driver]++;
203
204
        return self::$instances[$instanceId];
205
    }
206
207
    /**
208
     * @param ConfigurationOptionInterface|null $config
209
     * @return ConfigurationOption
210
     * @throws PhpfastcacheInvalidArgumentException
211
     * @throws PhpfastcacheInvalidConfigurationException
212
     * @throws \ReflectionException
213
     */
214
    protected static function validateConfig(?ConfigurationOptionInterface $config): ConfigurationOption
215
    {
216
        if ($config === null) {
217
            $config = self::getDefaultConfig();
218
        } else {
219
            if (!($config instanceof ConfigurationOption)) {
220
                throw new PhpfastcacheInvalidArgumentException(sprintf('Unsupported config type: %s', gettype($config)));
221
            }
222
        }
223
224
        return $config;
225
    }
226
227
    /**
228
     * @return ConfigurationOptionInterface
229
     * @throws PhpfastcacheInvalidConfigurationException
230
     * @throws \ReflectionException
231
     */
232
    public static function getDefaultConfig(): ConfigurationOptionInterface
233
    {
234
        return self::$config ?: self::$config = new ConfigurationOption();
235
    }
236
237
    /**
238
     * @param string $driverName
239
     * @return string
240
     */
241
    public static function standardizeDriverName(string $driverName): string
242
    {
243
        return ucfirst(strtolower(trim($driverName)));
244
    }
245
246
    /**
247
     * @param string $driverClass
248
     * @return string|ExtendedCacheItemPoolInterface
249
     * @throws PhpfastcacheDriverException
250
     */
251
    protected static function validateDriverClass(string $driverClass): string
252
    {
253
        if (!\is_a($driverClass, ExtendedCacheItemPoolInterface::class, true)) {
254
            throw new PhpfastcacheDriverException(
255
                \sprintf(
256
                    'Class "%s" does not implement "%s"',
257
                    $driverClass,
258
                    ExtendedCacheItemPoolInterface::class
259
                )
260
            );
261
        }
262
        return $driverClass;
263
    }
264
265
    /**
266
     * @param string $driverName
267
     * @return string
268
     */
269
    public static function getDriverClass(string $driverName): string
270
    {
271
        if (!empty(self::$driverCustoms[$driverName])) {
272
            $driverClass = self::$driverCustoms[$driverName];
273
        } else {
274
            if (!empty(self::$driverOverrides[$driverName])) {
275
                $driverClass = self::$driverOverrides[$driverName];
276
            } else {
277
                $driverClass = self::getNamespacePath() . $driverName . '\Driver';
278
            }
279
        }
280
281
        return $driverClass;
282
    }
283
284
    /**
285
     * @return string
286
     */
287
    public static function getNamespacePath(): string
288
    {
289
        return self::$namespacePath ?: self::getDefaultNamespacePath();
290
    }
291
292
    /**
293
     * @return string
294
     */
295
    public static function getDefaultNamespacePath(): string
296
    {
297
        return self::CORE_DRIVER_NAMESPACE;
298
    }
299
300
    /**
301
     * @return bool
302
     */
303
    public static function clearInstances(): bool
304
    {
305
        self::$instances = [];
306
307
        \gc_collect_cycles();
308
        return !\count(self::$instances);
309
    }
310
311
    /**
312
     * @param ExtendedCacheItemPoolInterface $cachePoolInstance
313
     * @return bool
314
     * @since 7.0.4
315
     */
316
    public static function clearInstance(ExtendedCacheItemPoolInterface $cachePoolInstance): bool
317
    {
318
        $found = false;
319
        self::$instances = \array_filter(
320
            \array_map(
321
                static function (ExtendedCacheItemPoolInterface $cachePool) use ($cachePoolInstance, &$found) {
322
                    if (\spl_object_hash($cachePool) === \spl_object_hash($cachePoolInstance)) {
323
                        $found = true;
324
                        return null;
325
                    }
326
                    return $cachePool;
327
                },
328
                self::$instances
329
            )
330
        );
331
332
        return $found;
333
    }
334
335
    /**
336
     * @param ConfigurationOption $config
337
     */
338
    public static function setDefaultConfig(ConfigurationOption $config): void
339
    {
340
        self::$config = $config;
341
    }
342
343
    /**
344
     * @param string $driverName
345
     * @param string $className
346
     * @return void
347
     * @throws PhpfastcacheLogicException
348
     * @throws PhpfastcacheUnsupportedOperationException
349
     * @throws PhpfastcacheInvalidArgumentException
350
     */
351
    public static function addCustomDriver(string $driverName, string $className): void
352
    {
353
        $driverName = self::standardizeDriverName($driverName);
354
355
        if (empty($driverName)) {
356
            throw new PhpfastcacheInvalidArgumentException("Can't add a custom driver because its name is empty");
357
        }
358
359
        if (!\class_exists($className)) {
360
            throw new PhpfastcacheInvalidArgumentException(
361
                \sprintf("Can't add '%s' because the class '%s' does not exists", $driverName, $className)
362
            );
363
        }
364
365
        if (!empty(self::$driverCustoms[$driverName])) {
366
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' has been already added", $driverName));
367
        }
368
369
        if (\in_array($driverName, self::getDriverList(), true)) {
370
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' is already a part of the PhpFastCache core", $driverName));
371
        }
372
373
        self::$driverCustoms[$driverName] = $className;
374
    }
375
376
    /**
377
     * Return the list of available drivers Capitalized
378
     * with optional FQCN as key
379
     *
380
     * @param bool $FQCNAsKey Describe keys with Full Qualified Class Name
381
     * @return string[]
382
     * @throws PhpfastcacheUnsupportedOperationException
383
     */
384
    public static function getDriverList(bool $FQCNAsKey = false): array
385
    {
386
        static $driverList;
387
388
        if (self::getDefaultNamespacePath() === self::getNamespacePath()) {
389
            if ($driverList === null) {
390
                $prefix = self::CORE_DRIVER_NAMESPACE;
391
                $classMap = self::createClassMap(__DIR__ . '/Drivers');
392
                $driverList = [];
393
394
                foreach ($classMap as $class => $file) {
395
                    $driverList[] = \str_replace($prefix, '', \substr($class, 0, \strrpos($class, '\\')));
396
                }
397
398
                $driverList = \array_values(\array_unique($driverList));
399
            }
400
401
            $driverList = \array_merge($driverList, \array_keys(self::$driverCustoms));
402
403
            if ($FQCNAsKey) {
404
                $realDriverList = [];
405
                foreach ($driverList as $driverName) {
406
                    $realDriverList[self::getDriverClass($driverName)] = $driverName;
407
                }
408
                $driverList = $realDriverList;
409
            }
410
411
            \asort($driverList);
412
413
            return $driverList;
414
        }
415
416
        throw new PhpfastcacheUnsupportedOperationException('Cannot get the driver list if the default namespace path has changed.');
417
    }
418
419
    /**
420
     * @param string $driverName
421
     * @return void
422
     * @throws PhpfastcacheLogicException
423
     * @throws PhpfastcacheInvalidArgumentException
424
     */
425
    public static function removeCustomDriver(string $driverName): void
426
    {
427
        $driverName = self::standardizeDriverName($driverName);
428
429
        if (empty($driverName)) {
430
            throw new PhpfastcacheInvalidArgumentException("Can't remove a custom driver because its name is empty");
431
        }
432
433
        if (!isset(self::$driverCustoms[$driverName])) {
434
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' does not exists", $driverName));
435
        }
436
437
        unset(self::$driverCustoms[$driverName]);
438
    }
439
440
    /**
441
     * @param string $driverName
442
     * @param string $className
443
     * @return void
444
     * @throws PhpfastcacheLogicException
445
     * @throws PhpfastcacheUnsupportedOperationException
446
     * @throws PhpfastcacheInvalidArgumentException
447
     */
448
    public static function addCoreDriverOverride(string $driverName, string $className): void
449
    {
450
        $driverName = self::standardizeDriverName($driverName);
451
452
        if (empty($driverName)) {
453
            throw new PhpfastcacheInvalidArgumentException("Can't add a core driver override because its name is empty");
454
        }
455
456
        if (!\class_exists($className)) {
457
            throw new PhpfastcacheInvalidArgumentException(
458
                \sprintf("Can't override '%s' because the class '%s' does not exists", $driverName, $className)
459
            );
460
        }
461
462
        if (!empty(self::$driverOverrides[$driverName])) {
463
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' has been already overridden", $driverName));
464
        }
465
466
        if (!\in_array($driverName, self::getDriverList(), true)) {
467
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' can't be overridden since its not a part of the PhpFastCache core", $driverName));
468
        }
469
470
        if (!\is_subclass_of($className, self::CORE_DRIVER_NAMESPACE . $driverName . '\\Driver', true)) {
471
            throw new PhpfastcacheLogicException(
472
                \sprintf(
473
                    "Can't override '%s' because the class '%s' MUST extend '%s'",
474
                    $driverName,
475
                    $className,
476
                    self::CORE_DRIVER_NAMESPACE . $driverName . '\\Driver'
477
                )
478
            );
479
        }
480
481
        self::$driverOverrides[$driverName] = $className;
482
    }
483
484
    /**
485
     * @param string $driverName
486
     * @return void
487
     * @throws PhpfastcacheLogicException
488
     * @throws PhpfastcacheInvalidArgumentException
489
     */
490
    public static function removeCoreDriverOverride(string $driverName): void
491
    {
492
        $driverName = self::standardizeDriverName($driverName);
493
494
        if (empty($driverName)) {
495
            throw new PhpfastcacheInvalidArgumentException("Can't remove a core driver override because its name is empty");
496
        }
497
498
        if (!isset(self::$driverOverrides[$driverName])) {
499
            throw new PhpfastcacheLogicException(\sprintf("Driver '%s' were not overridden", $driverName));
500
        }
501
502
        unset(self::$driverOverrides[$driverName]);
503
    }
504
}
505