Completed
Pull Request — final (#469)
by Georges
02:44
created

CacheManager::getStaticSystemDrivers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 23
rs 9.0856
cc 1
eloc 20
nc 1
nop 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
namespace phpFastCache;
15
16
use phpFastCache\Core\Pool\ExtendedCacheItemPoolInterface;
17
use phpFastCache\Exceptions\phpFastCacheDriverCheckException;
18
use phpFastCache\Exceptions\phpFastCacheInvalidArgumentException;
19
use phpFastCache\Exceptions\phpFastCacheInvalidConfigurationException;
20
21
/**
22
 * Class CacheManager
23
 * @package phpFastCache
24
 *
25
 * @method static ExtendedCacheItemPoolInterface Apc() Apc($config = []) Return a driver "Apc" instance
26
 * @method static ExtendedCacheItemPoolInterface Apcu() Apcu($config = []) Return a driver "Apcu" instance
27
 * @method static ExtendedCacheItemPoolInterface Cassandra() Cassandra($config = []) Return a driver "Cassandra" instance
28
 * @method static ExtendedCacheItemPoolInterface Cookie() Cookie($config = []) Return a driver "Cookie" instance
29
 * @method static ExtendedCacheItemPoolInterface Couchbase() Couchbase($config = []) Return a driver "Couchbase" instance
30
 * @method static ExtendedCacheItemPoolInterface Couchdb() Couchdb($config = []) Return a driver "Couchdb" instance
31
 * @method static ExtendedCacheItemPoolInterface Devnull() Devnull($config = []) Return a driver "Devnull" instance
32
 * @method static ExtendedCacheItemPoolInterface Files() Files($config = []) Return a driver "files" instance
33
 * @method static ExtendedCacheItemPoolInterface Leveldb() Leveldb($config = []) Return a driver "Leveldb" instance
34
 * @method static ExtendedCacheItemPoolInterface Memcache() Memcache($config = []) Return a driver "Memcache" instance
35
 * @method static ExtendedCacheItemPoolInterface Memcached() Memcached($config = []) Return a driver "Memcached" instance
36
 * @method static ExtendedCacheItemPoolInterface Memstatic() Memstatic($config = []) Return a driver "Memstatic" instance
37
 * @method static ExtendedCacheItemPoolInterface Mongodb() Mongodb($config = []) Return a driver "Mongodb" instance
38
 * @method static ExtendedCacheItemPoolInterface Predis() Predis($config = []) Return a driver "Predis" instance
39
 * @method static ExtendedCacheItemPoolInterface Redis() Redis($config = []) Return a driver "Pedis" instance
40
 * @method static ExtendedCacheItemPoolInterface Sqlite() Sqlite($config = []) Return a driver "Sqlite" instance
41
 * @method static ExtendedCacheItemPoolInterface Ssdb() Ssdb($config = []) Return a driver "Ssdb" instance
42
 * @method static ExtendedCacheItemPoolInterface Wincache() Wincache($config = []) Return a driver "Wincache" instance
43
 * @method static ExtendedCacheItemPoolInterface Xcache() Xcache($config = []) Return a driver "Xcache" instance
44
 * @method static ExtendedCacheItemPoolInterface Zenddisk() Zenddisk($config = []) Return a driver "Zend disk cache" instance
45
 * @method static ExtendedCacheItemPoolInterface Zendshm() Zendshm($config = []) Return a driver "Zend memory cache" instance
46
 *
47
 */
48
class CacheManager
49
{
50
    /**
51
     * @var int
52
     */
53
    public static $ReadHits = 0;
54
55
    /**
56
     * @var int
57
     */
58
    public static $WriteHits = 0;
59
60
    /**
61
     * @var ExtendedCacheItemPoolInterface[]
62
     */
63
    protected static $config = [
64
        /**
65
         * Specify if the item must provide detailed creation/modification dates
66
         */
67
      'itemDetailedDate' => false,
68
69
        /**
70
         * Automatically attempt to fallback to temporary directory
71
         * if the cache fails to write on the specified directory
72
         */
73
      'autoTmpFallback' => false,
74
75
        /**
76
         * Provide a secure file manipulation mechanism,
77
         * on intensive usage the performance can be affected.
78
         */
79
      'secureFileManipulation' => false,
80
81
        /**
82
         * Ignore Symfony notice for Symfony project which
83
         * do not makes use of PhpFastCache's Symfony Bundle
84
         */
85
      'ignoreSymfonyNotice' => false,
86
87
        /**
88
         * Default time-to-live in second
89
         */
90
      'defaultTtl' => 900,
91
92
        /**
93
         * Default key hash function
94
         * (md5 by default)
95
         */
96
      'defaultKeyHashFunction' => '',
97
98
        /**
99
         * The securityKey that will be used
100
         * to create sub-directory
101
         * (Files-based drivers only)
102
         */
103
      'securityKey' => 'Auto',
104
105
        /**
106
         * Auto-generate .htaccess if it's missing
107
         * (Files-based drivers only)
108
         */
109
      'htaccess' => true,
110
111
        /**
112
         * Default files chmod
113
         * 0777 recommended
114
         * (Files-based drivers only)
115
         */
116
      'default_chmod' => 0777,
117
118
        /**
119
         * The path where we will writecache files
120
         * default value if empty: sys_get_temp_dir()
121
         * (Files-based drivers only)
122
         */
123
      'path' => '',
124
125
        /**
126
         * Driver fallback in case of failure.
127
         * Caution, in case of failure an E_WARNING
128
         * error will always be raised
129
         */
130
      'fallback' => false,
131
132
        /**
133
         * Maximum size (bytes) of object store in memory
134
         * (Memcache(d) drivers only)
135
         */
136
      'limited_memory_each_object' => 4096,
137
138
        /**
139
         * Compress stored data, if the backend supports it
140
         * (Memcache(d) drivers only)
141
         */
142
      'compress_data' => false,
143
144
        /**
145
         * Prevent cache slams when
146
         * making use of heavy cache
147
         * items
148
         */
149
      'preventCacheSlams' => false,
150
151
        /**
152
         * Cache slams timeout
153
         * in seconds
154
         */
155
      'cacheSlamsTimeout' => 15,
156
157
        /**
158
         * Cache slams timeout
159
         * in seconds
160
         */
161
      'cacheFileExtension' => 'txt',
162
163
    ];
164
165
    /**
166
     * Feel free to propose your own one
167
     * by opening a pull request :)
168
     * @var array
169
     */
170
    protected static $safeFileExtensions = [
171
      'txt', 'cache', 'db', 'pfc'
172
    ];
173
174
    /**
175
     * @var string
176
     */
177
    protected static $namespacePath;
178
179
    /**
180
     * @var ExtendedCacheItemPoolInterface[]
181
     */
182
    protected static $instances = [];
183
184
    /**
185
     * @param string $driver
186
     * @param array $config
187
     * @return ExtendedCacheItemPoolInterface
188
     * @throws phpFastCacheDriverCheckException
189
     * @throws phpFastCacheInvalidConfigurationException
190
     */
191
    public static function getInstance($driver = 'auto', array $config = [])
192
    {
193
        static $badPracticeOmeter = [];
194
195
        /**
196
         * @todo: Standardize a method for driver name
197
         */
198
        $driver = self::standardizeDriverName($driver);
199
        $config = array_merge(self::$config, $config);
200
        self::validateConfig($config);
201
        if (!$driver || $driver === 'Auto') {
202
            $driver = self::getAutoClass($config);
203
        }
204
205
        $instance = crc32($driver . serialize($config));
206
        if (!isset(self::$instances[ $instance ])) {
207
            $badPracticeOmeter[$driver] = 1;
208
            if(!$config['ignoreSymfonyNotice'] && interface_exists('Symfony\Component\HttpKernel\KernelInterface') && !class_exists('phpFastCache\Bundle\phpFastCacheBundle')){
209
                trigger_error('A Symfony Bundle to make the PhpFastCache integration more easier is now available here: https://github.com/PHPSocialNetwork/phpfastcache-bundle', E_USER_NOTICE);
210
            }
211
            $class = self::getNamespacePath() . $driver . '\Driver';
212
            try{
213
                self::$instances[ $instance ] = new $class($config);
214
                self::$instances[ $instance ]->setEventManager(EventManager::getInstance());
215
            }catch(phpFastCacheDriverCheckException $e){
216
                if($config['fallback']){
217
                    try{
218
                        $fallback = self::standardizeDriverName($config['fallback']);
219
                        if($fallback !== $driver){
220
                            $class = self::getNamespacePath() . $fallback . '\Driver';
221
                            self::$instances[ $instance ] = new $class($config);
222
                            self::$instances[ $instance ]->setEventManager(EventManager::getInstance());
223
                            trigger_error(sprintf('The "%s" driver is unavailable at the moment, the fallback driver "%s" has been used instead.', $driver, $fallback), E_USER_WARNING);
224
                        }else{
225
                            throw new phpFastCacheInvalidConfigurationException('The fallback driver cannot be the same than the default driver', 0, $e);
226
                        }
227
                    }catch (phpFastCacheInvalidArgumentException $e){
228
                        throw new phpFastCacheInvalidConfigurationException('Invalid fallback driver configuration', 0, $e);
229
                    }
230
                }else{
231
                    throw new phpFastCacheDriverCheckException($e->getMessage(), $e->getCode(), $e);
232
                }
233
            }
234
        } else if($badPracticeOmeter[$driver] >= 5){
235
            trigger_error('[' . $driver . '] Calling many times CacheManager::getInstance() for already instanced drivers is a bad practice and have a significant impact on performances.
236
           See https://github.com/PHPSocialNetwork/phpfastcache/wiki/[V5]-Why-calling-getInstance%28%29-each-time-is-a-bad-practice-%3F');
237
        }
238
239
        $badPracticeOmeter[$driver]++;
240
241
        return self::$instances[ $instance ];
242
    }
243
244
    /**
245
     * This method is intended for internal
246
     * use only and should not be used for
247
     * any external development use the
248
     * getInstances() method instead
249
     *
250
     * @internal
251
     * @return ExtendedCacheItemPoolInterface[]
252
     */
253
    public static function getInstances()
254
    {
255
        return self::$instances;
256
    }
257
258
    /**
259
     * This method is intended for internal
260
     * use only and should not be used for
261
     * any external development use the
262
     * getInstances() method instead
263
     *
264
     * @internal
265
     * @return ExtendedCacheItemPoolInterface[]
266
     */
267
    public static function &getInternalInstances()
268
    {
269
        return self::$instances;
270
    }
271
272
    /**
273
     * @param $config
274
     * @return string
275
     * @throws phpFastCacheDriverCheckException
276
     */
277
    public static function getAutoClass(array $config = [])
278
    {
279
        static $autoDriver;
280
281
        if ($autoDriver === null) {
282
            foreach (self::getStaticSystemDrivers() as $driver) {
283
                try {
284
                    self::getInstance($driver, $config);
285
                    $autoDriver = $driver;
286
                } catch (phpFastCacheDriverCheckException $e) {
287
                    continue;
288
                }
289
            }
290
        }
291
292
        return $autoDriver;
293
    }
294
295
    /**
296
     * @param string $name
297
     * @param array $arguments
298
     * @return \Psr\Cache\CacheItemPoolInterface
299
     */
300
    public static function __callStatic($name, $arguments)
301
    {
302
        $options = (array_key_exists(0, $arguments) && is_array($arguments) ? $arguments[ 0 ] : []);
303
304
        return self::getInstance($name, $options);
305
    }
306
307
    /**
308
     * @return bool
309
     */
310
    public static function clearInstances()
311
    {
312
        self::$instances = [];
313
314
        gc_collect_cycles();
315
        return !count(self::$instances);
316
    }
317
318
    /**
319
     * @return string
320
     */
321
    public static function getNamespacePath()
322
    {
323
        return self::$namespacePath ?: __NAMESPACE__ . '\Drivers\\';
324
    }
325
326
    /**
327
     * @param string $path
328
     */
329
    public static function setNamespacePath($path)
330
    {
331
        self::$namespacePath = $path;
332
    }
333
334
    /**
335
     * @param $name string|array
336
     * @param mixed $value
337
     * @throws phpFastCacheInvalidArgumentException
338
     */
339
    public static function setDefaultConfig($name, $value = null)
340
    {
341
        if (is_array($name)) {
342
            self::$config = array_merge(self::$config, $name);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge(self::$config, $name) of type array is incompatible with the declared type array<integer,object<php...acheItemPoolInterface>> of property $config.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
343
        } else if (is_string($name)){
344
            self::$config[ $name ] = $value;
345
        }else{
346
            throw new phpFastCacheInvalidArgumentException('Invalid variable type: $name');
347
        }
348
    }
349
350
    /**
351
     * @param $name string|array
352
     * @param mixed $value
353
     * @throws phpFastCacheInvalidConfigurationException
354
     * @deprecated Method "setup" is deprecated, please use "setDefaultConfig" method instead
355
     */
356
    public static function setup($name, $value = null)
357
    {
358
        throw new phpFastCacheInvalidConfigurationException(sprintf('Method "%s" is deprecated, please use "setDefaultConfig" method instead.', __FUNCTION__));
359
    }
360
361
362
    /**
363
     * @return array
364
     */
365
    public static function getDefaultConfig()
366
    {
367
        return self::$config;
368
    }
369
370
    /**
371
     * @return array
372
     */
373
    public static function getStaticSystemDrivers()
374
    {
375
        return [
376
          'Apc',
377
          'Apcu',
378
          'Cassandra',
379
          'Couchbase',
380
          'Couchdb',
381
          'Devnull',
382
          'Files',
383
          'Leveldb',
384
          'Memcache',
385
          'Memcached',
386
          'Memstatic',
387
          'Mongodb',
388
          'Predis',
389
          'Redis',
390
          'Ssdb',
391
          'Sqlite',
392
          'Wincache',
393
          'Xcache',
394
        ];
395
    }
396
397
    /**
398
     * @return array
399
     */
400
    public static function getStaticAllDrivers()
401
    {
402
        return array_merge(self::getStaticSystemDrivers(), [
403
          'Devtrue',
404
          'Devfalse',
405
          'Cookie',
406
        ]);
407
    }
408
409
    /**
410
     * @param $driverName
411
     * @return string
412
     * @throws \phpFastCache\Exceptions\phpFastCacheInvalidArgumentException
413
     */
414
    public static function standardizeDriverName($driverName)
415
    {
416
        if(!is_string($driverName)){
417
            throw new phpFastCacheInvalidArgumentException(sprintf('Expected $driverName to be a string got "%s" instead', gettype($driverName)));
418
        }
419
        return ucfirst(strtolower(trim($driverName)));
420
    }
421
422
    /**
423
     * @param array $config
424
     * @todo Move this to a config file
425
     * @throws phpFastCacheInvalidConfigurationException
426
     * @return bool
427
     */
428
    protected static function validateConfig(array $config)
429
    {
430
        foreach ($config as $configName => $configValue) {
431
            switch($configName)
432
            {
433
                case 'itemDetailedDate':
434
                    if(!is_bool($configValue)){
435
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
436
                    }
437
                    break;
438
                case 'autoTmpFallback':
439
                    if(!is_bool($configValue)){
440
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
441
                    }
442
                    break;
443
                case 'secureFileManipulation':
444
                    if(!is_bool($configValue)){
445
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
446
                    }
447
                    break;
448
                case 'ignoreSymfonyNotice':
449
                    if(!is_bool($configValue)){
450
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
451
                    }
452
                    break;
453
                case 'defaultTtl':
454
                    if(!is_numeric($configValue)){
455
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be numeric");
456
                    }
457
                    break;
458
                case 'defaultKeyHashFunction':
459
                    if(!is_string($configValue) && !function_exists($configValue)){
460
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a valid function name string");
461
                    }
462
                    break;
463
                case 'securityKey':
464
                    if(!is_string($configValue)){
465
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a string");
466
                    }
467
                    break;
468
                case 'htaccess':
469
                    if(!is_bool($configValue)){
470
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
471
                    }
472
                    break;
473
                case 'default_chmod':
474
                    if(!is_int($configValue)){
475
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be an integer");
476
                    }
477
                    break;
478
                case 'path':
479
                    if(!is_string($configValue)){
480
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a string");
481
                    }
482
                    break;
483
                case 'fallback':
484
                    if(!is_bool($configValue)){
485
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
486
                    }
487
                    break;
488
                case 'limited_memory_each_object':
489
                    if(!is_int($configValue)){
490
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be an integer");
491
                    }
492
                    break;
493
                case 'compress_data':
494
                    if(!is_bool($configValue)){
495
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
496
                    }
497
                    break;
498
                case 'cacheFileExtension':
499
                    if(!is_string($configValue)){
500
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
501
                    }
502
                    if(strpos($configValue, '.') !== false){
503
                        throw new phpFastCacheInvalidConfigurationException("{$configName} cannot contain a dot \".\"");
504
                    }
505
                    if(!in_array($configValue, self::$safeFileExtensions)){
506
                        throw new phpFastCacheInvalidConfigurationException(
507
                          "{$configName} is not a safe extension, currently allowed extension: " . implode(', ', self::$safeFileExtensions)
508
                        );
509
                    }
510
                    break;
511
            }
512
        }
513
514
        return true;
515
    }
516
}
517