Passed
Push — v7 ( c2ec86...629ebb )
by Georges
01:43
created

CacheManager::getAutoClass()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 8
nop 1
dl 0
loc 26
rs 6.7272
c 0
b 0
f 0
1
<?php
2
/**
3
 *
4
 * This file is part of phpFastCache.
5
 *
6
 * @license MIT License (MIT)
7
 *
8
 * For full copyright and license information, please see the docs/CREDITS.txt file.
9
 *
10
 * @author Khoa Bui (khoaofgod)  <[email protected]> http://www.phpfastcache.com
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 *
13
 */
14
declare(strict_types=1);
15
16
namespace Phpfastcache;
17
18
use Phpfastcache\Config\ConfigurationOption;
19
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface;
20
use Phpfastcache\Exceptions\{
21
  PhpfastcacheDeprecatedException, PhpfastcacheDriverCheckException, PhpfastcacheDriverException, PhpfastcacheDriverNotFoundException, PhpfastcacheInstanceNotFoundException, PhpfastcacheInvalidArgumentException, PhpfastcacheInvalidConfigurationException, PhpfastcacheLogicException, PhpfastcacheUnsupportedOperationException
22
};
23
use Phpfastcache\Util\ClassNamespaceResolverTrait;
24
25
/**
26
 * Class CacheManager
27
 * @package phpFastCache
28
 *
29
 * @method static ExtendedCacheItemPoolInterface Apc() Apc($config = []) Return a driver "Apc" instance
30
 * @method static ExtendedCacheItemPoolInterface Apcu() Apcu($config = []) Return a driver "Apcu" instance
31
 * @method static ExtendedCacheItemPoolInterface Cassandra() Cassandra($config = []) Return a driver "Cassandra" instance
32
 * @method static ExtendedCacheItemPoolInterface Cookie() Cookie($config = []) Return a driver "Cookie" instance
33
 * @method static ExtendedCacheItemPoolInterface Couchbase() Couchbase($config = []) Return a driver "Couchbase" instance
34
 * @method static ExtendedCacheItemPoolInterface Couchdb() Couchdb($config = []) Return a driver "Couchdb" instance
35
 * @method static ExtendedCacheItemPoolInterface Devnull() Devnull($config = []) Return a driver "Devnull" instance
36
 * @method static ExtendedCacheItemPoolInterface Files() Files($config = []) Return a driver "files" instance
37
 * @method static ExtendedCacheItemPoolInterface Leveldb() Leveldb($config = []) Return a driver "Leveldb" instance
38
 * @method static ExtendedCacheItemPoolInterface Memcache() Memcache($config = []) Return a driver "Memcache" instance
39
 * @method static ExtendedCacheItemPoolInterface Memcached() Memcached($config = []) Return a driver "Memcached" instance
40
 * @method static ExtendedCacheItemPoolInterface Memstatic() Memstatic($config = []) Return a driver "Memstatic" instance
41
 * @method static ExtendedCacheItemPoolInterface Mongodb() Mongodb($config = []) Return a driver "Mongodb" instance
42
 * @method static ExtendedCacheItemPoolInterface Predis() Predis($config = []) Return a driver "Predis" instance
43
 * @method static ExtendedCacheItemPoolInterface Redis() Redis($config = []) Return a driver "Pedis" instance
44
 * @method static ExtendedCacheItemPoolInterface Riak() Riak($config = []) Return a driver "Riak" instance
45
 * @method static ExtendedCacheItemPoolInterface Sqlite() Sqlite($config = []) Return a driver "Sqlite" instance
46
 * @method static ExtendedCacheItemPoolInterface Ssdb() Ssdb($config = []) Return a driver "Ssdb" instance
47
 * @method static ExtendedCacheItemPoolInterface Wincache() Wincache($config = []) Return a driver "Wincache" instance
48
 * @method static ExtendedCacheItemPoolInterface Xcache() Xcache($config = []) Return a driver "Xcache" instance
49
 * @method static ExtendedCacheItemPoolInterface Zenddisk() Zenddisk($config = []) Return a driver "Zend disk cache" instance
50
 * @method static ExtendedCacheItemPoolInterface Zendshm() Zendshm($config = []) Return a driver "Zend memory cache" instance
51
 *
52
 */
53
class CacheManager
54
{
55
    const AUTOMATIC_DRIVER_CLASS = 'Auto';
56
    const CORE_DRIVER_NAMESPACE = 'Phpfastcache\Drivers\\';
57
58
    use ClassNamespaceResolverTrait;
59
60
    /**
61
     * @var ConfigurationOption
62
     */
63
    protected static $config;
64
65
    /**
66
     * @var int
67
     */
68
    public static $ReadHits = 0;
69
70
    /**
71
     * @var int
72
     */
73
    public static $WriteHits = 0;
74
75
    /**
76
     * @var string
77
     */
78
    protected static $namespacePath;
79
80
    /**
81
     * @var ExtendedCacheItemPoolInterface[]
82
     */
83
    protected static $instances = [];
84
85
    /**
86
     * @var array
87
     */
88
    protected static $driverOverrides = [];
89
90
    /**
91
     * @var array
92
     */
93
    protected static $driverCustoms = [];
94
95
    /**
96
     * @var array
97
     */
98
    protected static $badPracticeOmeter = [];
99
100
    /**
101
     * @param string $driver
102
     * @param array|ConfigurationOption $config
103
     * @param string $instanceId
104
     *
105
     * @return ExtendedCacheItemPoolInterface
106
     *
107
     * @throws PhpfastcacheDriverCheckException
108
     * @throws PhpfastcacheInvalidConfigurationException
109
     * @throws PhpfastcacheDriverNotFoundException
110
     * @throws PhpfastcacheInvalidArgumentException
111
     * @throws PhpfastcacheDriverException
112
     */
113
    public static function getInstance(string $driver = 'auto', $config = null, string $instanceId = null): ExtendedCacheItemPoolInterface
114
    {
115
        if (\is_array($config)) {
116
            $config = new ConfigurationOption($config);
117
            trigger_error(
118
              'The CacheManager will drops the support of primitive configuration arrays, use a "\Phpfastcache\Config\ConfigurationOption" object instead',
119
              E_USER_DEPRECATED
120
            );
121
        }elseif ($config === null){
122
            $config = self::getDefaultConfig();
123
        }else if(!($config instanceof ConfigurationOption)){
0 ignored issues
show
introduced by
$config is always a sub-type of Phpfastcache\Config\ConfigurationOption.
Loading history...
124
            throw new PhpfastcacheInvalidArgumentException(\sprintf('Unsupported config type: %s', gettype($config)));
125
        }
126
127
        $driver = self::standardizeDriverName($driver);
128
129
        if (!$driver || $driver === self::AUTOMATIC_DRIVER_CLASS) {
130
            $driver = self::getAutoClass($config);
131
        }
132
133
        $instance = $instanceId ?: md5($driver . \serialize($config->toArray()));
134
135
        if (!isset(self::$instances[ $instance ])) {
136
            self::$badPracticeOmeter[ $driver ] = 1;
137
            $driverClass = self::getDriverClass($driver);
138
139
            if(!is_a($driverClass, ExtendedCacheItemPoolInterface::class, true)){
140
                throw new PhpfastcacheDriverException(sprintf(
141
                  'Class "%s" does not implement "%s"',
142
                  $driverClass,
143
                  ExtendedCacheItemPoolInterface::class
144
                ));
145
            }
146
            try {
147
                if (\class_exists($driverClass)) {
148
                    $configClass = $driverClass::getConfigClass();
149
                    self::$instances[ $instance ] = new $driverClass(new $configClass($config->toArray()), $instance);
150
                    self::$instances[ $instance ]->setEventManager(EventManager::getInstance());
151
                } else {
152
                    throw new PhpfastcacheDriverNotFoundException(\sprintf('The driver "%s" does not exists', $driver));
153
                }
154
            } catch (PhpfastcacheDriverCheckException $e) {
155
                if ($config->getFallback()) {
156
                    try {
157
                        $fallback = $config->getFallback();
158
                        $config->setFallback('');
159
                        trigger_error(\sprintf('The "%s" driver is unavailable at the moment, the fallback driver "%s" has been used instead.', $driver, $fallback), E_USER_WARNING);
160
                        return self::getInstance($fallback, $config->getFallbackConfig());
161
                    } catch (PhpfastcacheInvalidArgumentException $e) {
162
                        throw new PhpfastcacheInvalidConfigurationException('Invalid fallback driver configuration', 0, $e);
163
                    }
164
                } else {
165
                    throw new PhpfastcacheDriverCheckException($e->getMessage(), $e->getCode(), $e);
166
                }
167
            }
168
        } else if (self::$badPracticeOmeter[ $driver ] >= 2) {
169
            trigger_error('[' . $driver . '] Calling many times CacheManager::getInstance() for already instanced drivers is a bad practice and have a significant impact on performances.
170
           See https://github.com/PHPSocialNetwork/phpfastcache/wiki/[V5]-Why-calling-getInstance%28%29-each-time-is-a-bad-practice-%3F');
171
        }
172
173
        self::$badPracticeOmeter[ $driver ]++;
174
175
        return self::$instances[ $instance ];
176
    }
177
178
    /**
179
     * @param string $instanceId
180
     *
181
     * @return ExtendedCacheItemPoolInterface
182
     *
183
     * @throws PhpfastcacheInvalidArgumentException
184
     * @throws PhpfastcacheInstanceNotFoundException
185
     */
186
    public static function getInstanceById(string $instanceId): ExtendedCacheItemPoolInterface
187
    {
188
        if ($instanceId !== null && !\is_string($instanceId)) {
0 ignored issues
show
introduced by
The condition is_string($instanceId) is always true.
Loading history...
189
            throw new PhpfastcacheInvalidArgumentException('The Instance ID must be a string');
190
        }
191
192
        if (isset(self::$instances[ $instanceId ])) {
193
            return self::$instances[ $instanceId ];
194
        }
195
196
        throw new PhpfastcacheInstanceNotFoundException(\sprintf('Instance ID %s not found', $instanceId));
197
    }
198
199
    /**
200
     * This method is intended for internal
201
     * use only and should not be used for
202
     * any external development use the
203
     * getInstances() method instead
204
     *
205
     * @internal
206
     * @return ExtendedCacheItemPoolInterface[]
207
     */
208
    public static function getInstances(): array
209
    {
210
        return self::$instances;
211
    }
212
213
    /**
214
     * This method is intended for internal
215
     * use only and should not be used for
216
     * any external development use the
217
     * getInstances() method instead
218
     *
219
     * @todo Use a proper way to passe them as a reference ?
220
     * @internal
221
     * @return ExtendedCacheItemPoolInterface[]
222
     */
223
    public static function &getInternalInstances(): array
224
    {
225
        return self::$instances;
226
    }
227
228
    /**
229
     * @param ConfigurationOption $config
230
     * @return string
231
     * @throws PhpfastcacheDriverCheckException
232
     * @throws \Phpfastcache\Exceptions\PhpfastcacheLogicException
233
     */
234
    public static function getAutoClass(ConfigurationOption $config): string
235
    {
236
        static $autoDriver;
237
238
        if ($autoDriver === null) {
239
            foreach (self::getDriverList() as $driver) {
240
                /** @var ExtendedCacheItemPoolInterface $driver */
241
                if((self::CORE_DRIVER_NAMESPACE . $driver . '\Driver')::isUsableInAutoContext()){
0 ignored issues
show
Bug introduced by
Are you sure $driver of type Phpfastcache\Core\Pool\E...dCacheItemPoolInterface can be used in concatenation? Consider adding a __toString()-method. ( Ignorable by Annotation )

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

241
                if((self::CORE_DRIVER_NAMESPACE . /** @scrutinizer ignore-type */ $driver . '\Driver')::isUsableInAutoContext()){
Loading history...
242
                    try {
243
                        self::getInstance($driver, $config);
0 ignored issues
show
Bug introduced by
$driver of type Phpfastcache\Core\Pool\E...dCacheItemPoolInterface is incompatible with the type string expected by parameter $driver of Phpfastcache\CacheManager::getInstance(). ( Ignorable by Annotation )

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

243
                        self::getInstance(/** @scrutinizer ignore-type */ $driver, $config);
Loading history...
244
                        $autoDriver = $driver;
245
                        break;
246
                    } catch (PhpfastcacheDriverCheckException $e) {
247
                        continue;
248
                    }
249
                }
250
            }
251
        }
252
253
        if(!$autoDriver || !\is_string($autoDriver)){
254
            throw new PhpfastcacheLogicException('Unable to find out a valid driver automatically');
255
        }
256
257
        self::$badPracticeOmeter[ $autoDriver ]--;
258
259
        return $autoDriver;
260
    }
261
262
    /**
263
     * @param string $name
264
     * @param array $arguments
265
     * @return \Psr\Cache\ExtendedCacheItemPoolInterface
0 ignored issues
show
Bug introduced by
The type Psr\Cache\ExtendedCacheItemPoolInterface 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...
266
     */
267
    public static function __callStatic(string $name, array $arguments): ExtendedCacheItemPoolInterface
268
    {
269
        $options = (\array_key_exists(0, $arguments) && \is_array($arguments) ? $arguments[ 0 ] : []);
270
271
        return self::getInstance($name, $options);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getInstance($name, $options) returns the type Phpfastcache\Core\Pool\E...dCacheItemPoolInterface which is incompatible with the documented return type Psr\Cache\ExtendedCacheItemPoolInterface.
Loading history...
272
    }
273
274
    /**
275
     * @return bool
276
     */
277
    public static function clearInstances(): bool
278
    {
279
        self::$instances = [];
280
281
        gc_collect_cycles();
282
        return !\count(self::$instances);
283
    }
284
285
    /**
286
     * @return string
287
     */
288
    public static function getNamespacePath(): string
289
    {
290
        return self::$namespacePath ?: self::getDefaultNamespacePath();
291
    }
292
293
    /**
294
     * @return string
295
     */
296
    public static function getDefaultNamespacePath(): string
297
    {
298
        return  __NAMESPACE__ . '\Drivers\\';
299
    }
300
301
    /**
302
     * @param string $path
303
     * @deprecated This method has been deprecated as of V7, please use driver override feature instead
304
     */
305
    public static function setNamespacePath($path)
306
    {
307
        trigger_error('This method has been deprecated as of V7, please use cache manager "override" or "custom driver" features instead', E_USER_DEPRECATED);
308
        self::$namespacePath = \trim($path, "\\") . '\\';
309
    }
310
311
    /**
312
     * @param ConfigurationOption $config
313
     */
314
    public static function setDefaultConfig(ConfigurationOption $config)
315
    {
316
        self::$config = $config;
317
    }
318
319
    /**
320
     * @return ConfigurationOption
321
     */
322
    public static function getDefaultConfig(): ConfigurationOption
323
    {
324
        return self::$config ?: self::$config = new ConfigurationOption();
325
    }
326
327
    /**
328
     * @return array
329
     * @deprecated As of V7 will be removed soon or later, use CacheManager::getDriverList() instead
330
     */
331
    public static function getStaticSystemDrivers(): array
332
    {
333
        trigger_error(\sprintf('Method "%s" is deprecated as of the V7 and will be removed soon or later, use CacheManager::getDriverList() instead.', __METHOD__), E_USER_DEPRECATED);
334
        return [
335
          'Apc',
336
          'Apcu',
337
          'Cassandra',
338
          'Couchbase',
339
          'Couchdb',
340
          'Devnull',
341
          'Files',
342
          'Leveldb',
343
          'Memcache',
344
          'Memcached',
345
          'Memstatic',
346
          'Mongodb',
347
          'Predis',
348
          'Redis',
349
          'Riak',
350
          'Ssdb',
351
          'Sqlite',
352
          'Wincache',
353
          'Xcache',
354
          'Zenddisk',
355
          'Zendshm',
356
        ];
357
    }
358
359
    /**
360
     * @return array
361
     * @deprecated As of V7 will be removed soon or later, use CacheManager::getDriverList() instead
362
     */
363
    public static function getStaticAllDrivers(): array
364
    {
365
        trigger_error(\sprintf('Method "%s" is deprecated as of the V7 and will be removed soon or later, use CacheManager::getDriverList() instead.', __METHOD__), E_USER_DEPRECATED);
366
        return \array_merge(self::getStaticSystemDrivers(), [
1 ignored issue
show
Deprecated Code introduced by
The function Phpfastcache\CacheManage...etStaticSystemDrivers() has been deprecated: As of V7 will be removed soon or later, use CacheManager::getDriverList() instead ( Ignorable by Annotation )

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

366
        return \array_merge(/** @scrutinizer ignore-deprecated */ self::getStaticSystemDrivers(), [

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
367
          'Devtrue',
368
          'Devfalse',
369
          'Cookie',
370
        ]);
371
    }
372
373
    /**
374
     * @return string[]
375
     * @throws PhpfastcacheUnsupportedOperationException
376
     */
377
    public static function getDriverList(): array
378
    {
379
        static $driverList;
380
381
        if(self::getDefaultNamespacePath() === self::getNamespacePath()){
382
            if($driverList === null){
383
                $prefix = self::CORE_DRIVER_NAMESPACE;
384
                $classMap = self::createClassMap(__DIR__ . '/Drivers');
385
                $driverList = [];
386
387
                foreach ($classMap as $class => $file) {
388
                    $driverList[] = str_replace($prefix, '', substr($class, 0, strrpos($class, '\\') ));
389
                }
390
391
                $driverList = array_values(array_unique($driverList));
392
            }
393
394
            $driverList = array_merge($driverList, array_keys(self::$driverCustoms));
395
396
            sort($driverList);
397
398
            return $driverList;
399
        }
400
401
        throw new PhpfastcacheUnsupportedOperationException('Cannot get the driver list if the default namespace path has changed.');
402
    }
403
404
    /**
405
     * @param string $driverName
406
     * @return string
407
     */
408
    public static function standardizeDriverName(string $driverName): string
409
    {
410
        return \ucfirst(\strtolower(\trim($driverName)));
411
    }
412
413
    /**
414
     * @param string $driverName
415
     * @return string
416
     */
417
    public static function getDriverClass(string $driverName): string
418
    {
419
        if(!empty(self::$driverCustoms[$driverName])){
420
            $driverClass = self::$driverCustoms[$driverName];
421
        }else if(!empty(self::$driverOverrides[$driverName])){
422
            $driverClass = self::$driverOverrides[$driverName];
423
        } else{
424
            $driverClass = self::getNamespacePath() . $driverName . '\Driver';
425
        }
426
427
        return $driverClass;
428
    }
429
430
    /**
431
     * @param string $driverName
432
     * @param string $className
433
     * @throws \Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException
434
     * @throws \Phpfastcache\Exceptions\PhpfastcacheLogicException
435
     * @throws \Phpfastcache\Exceptions\PhpfastcacheUnsupportedOperationException
436
     * @return void
437
     */
438
    public static function addCustomDriver(string $driverName, string $className){
439
        $driverName = self::standardizeDriverName($driverName);
440
441
        if(empty($driverName)){
442
            throw new PhpfastcacheInvalidArgumentException("Can't add a custom driver because its name is empty");
443
        }
444
445
        if(!\class_exists($className)){
446
            throw new PhpfastcacheInvalidArgumentException(
447
              sprintf("Can't add '%s' because the class '%s' does not exists", $driverName, $className)
448
            );
449
        }
450
451
        if(!empty(self::$driverCustoms[$driverName])){
452
            throw new PhpfastcacheLogicException(sprintf("Driver '%s' has been already added", $driverName));
453
        }
454
455
        if(\in_array($driverName, self::getDriverList(), true)){
456
            throw new PhpfastcacheLogicException(sprintf("Driver '%s' is already a part of the PhpFastCache core", $driverName));
457
        }
458
459
        self::$driverCustoms[$driverName] = $className;
460
    }
461
462
    /**
463
     * @param string $driverName
464
     * @throws \Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException
465
     * @throws \Phpfastcache\Exceptions\PhpfastcacheLogicException
466
     * @return void
467
     */
468
    public static function removeCustomDriver(string $driverName)
469
    {
470
        $driverName = self::standardizeDriverName($driverName);
471
472
        if(empty($driverName)){
473
            throw new PhpfastcacheInvalidArgumentException("Can't remove a custom driver because its name is empty");
474
        }
475
476
        if(!isset(self::$driverCustoms[$driverName])){
477
            throw new PhpfastcacheLogicException(sprintf("Driver '%s' does not exists", $driverName));
478
        }
479
480
        unset(self::$driverCustoms[$driverName]);
481
    }
482
483
    /**
484
     * @param string $driverName
485
     * @param string $className
486
     * @throws \Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException
487
     * @throws \Phpfastcache\Exceptions\PhpfastcacheLogicException
488
     * @throws \Phpfastcache\Exceptions\PhpfastcacheUnsupportedOperationException
489
     * @return void
490
     */
491
    public static function addCoreDriverOverride(string $driverName, string $className){
492
        $driverName = self::standardizeDriverName($driverName);
493
494
        if(empty($driverName)){
495
            throw new PhpfastcacheInvalidArgumentException("Can't add a core driver override because its name is empty");
496
        }
497
498
        if(!\class_exists($className)){
499
            throw new PhpfastcacheInvalidArgumentException(
500
              sprintf("Can't override '%s' because the class '%s' does not exists", $driverName, $className)
501
            );
502
        }
503
504
        if(!empty(self::$driverOverrides[$driverName])){
505
            throw new PhpfastcacheLogicException(sprintf("Driver '%s' has been already overridden", $driverName));
506
        }
507
508
        if(!\in_array($driverName, self::getDriverList(), true)){
509
            throw new PhpfastcacheLogicException(sprintf("Driver '%s' can't be overridden since its not a part of the PhpFastCache core", $driverName));
510
        }
511
512
        if(!\is_subclass_of($className, self::CORE_DRIVER_NAMESPACE . $driverName . '\\Driver', true)){
513
            throw new PhpfastcacheLogicException(
514
              sprintf("Can't override '%s' because the class '%s' MUST extend '%s'", $driverName, $className, self::CORE_DRIVER_NAMESPACE . $driverName . '\\Driver')
515
            );
516
        }
517
518
        self::$driverOverrides[$driverName] = $className;
519
    }
520
521
    /**
522
     * @param string $driverName
523
     * @throws \Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException
524
     * @throws \Phpfastcache\Exceptions\PhpfastcacheLogicException
525
     * @return void
526
     */
527
    public static function removeCoreDriverOverride(string $driverName)
528
    {
529
        $driverName = self::standardizeDriverName($driverName);
530
531
        if(empty($driverName)){
532
            throw new PhpfastcacheInvalidArgumentException("Can't remove a core driver override because its name is empty");
533
        }
534
535
        if(!isset(self::$driverOverrides[$driverName])){
536
            throw new PhpfastcacheLogicException(sprintf("Driver '%s' were not overridden", $driverName));
537
        }
538
539
        unset(self::$driverOverrides[$driverName]);
540
    }
541
}
542