Passed
Pull Request — final (#504)
by Pavel
02:30
created

CacheManager::getStaticAllDrivers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 8
rs 9.4285
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
15
namespace phpFastCache;
16
17
use phpFastCache\Core\Pool\ExtendedCacheItemPoolInterface;
18
use phpFastCache\Exceptions\phpFastCacheDriverCheckException;
19
use phpFastCache\Exceptions\phpFastCacheInvalidArgumentException;
20
use phpFastCache\Exceptions\phpFastCacheInvalidConfigurationException;
21
22
/**
23
 * Class CacheManager
24
 * @package phpFastCache
25
 *
26
 * @method static ExtendedCacheItemPoolInterface Apc() Apc($config = []) Return a driver "Apc" instance
27
 * @method static ExtendedCacheItemPoolInterface Apcu() Apcu($config = []) Return a driver "Apcu" instance
28
 * @method static ExtendedCacheItemPoolInterface Cassandra() Cassandra($config = []) Return a driver "Cassandra" instance
29
 * @method static ExtendedCacheItemPoolInterface Cookie() Cookie($config = []) Return a driver "Cookie" instance
30
 * @method static ExtendedCacheItemPoolInterface Couchbase() Couchbase($config = []) Return a driver "Couchbase" instance
31
 * @method static ExtendedCacheItemPoolInterface Couchdb() Couchdb($config = []) Return a driver "Couchdb" instance
32
 * @method static ExtendedCacheItemPoolInterface Devnull() Devnull($config = []) Return a driver "Devnull" instance
33
 * @method static ExtendedCacheItemPoolInterface Files() Files($config = []) Return a driver "files" instance
34
 * @method static ExtendedCacheItemPoolInterface Leveldb() Leveldb($config = []) Return a driver "Leveldb" instance
35
 * @method static ExtendedCacheItemPoolInterface Memcache() Memcache($config = []) Return a driver "Memcache" instance
36
 * @method static ExtendedCacheItemPoolInterface Memcached() Memcached($config = []) Return a driver "Memcached" instance
37
 * @method static ExtendedCacheItemPoolInterface Memstatic() Memstatic($config = []) Return a driver "Memstatic" instance
38
 * @method static ExtendedCacheItemPoolInterface Mongodb() Mongodb($config = []) Return a driver "Mongodb" instance
39
 * @method static ExtendedCacheItemPoolInterface Predis() Predis($config = []) Return a driver "Predis" instance
40
 * @method static ExtendedCacheItemPoolInterface Redis() Redis($config = []) Return a driver "Pedis" instance
41
 * @method static ExtendedCacheItemPoolInterface Sqlite() Sqlite($config = []) Return a driver "Sqlite" instance
42
 * @method static ExtendedCacheItemPoolInterface Ssdb() Ssdb($config = []) Return a driver "Ssdb" instance
43
 * @method static ExtendedCacheItemPoolInterface Wincache() Wincache($config = []) Return a driver "Wincache" instance
44
 * @method static ExtendedCacheItemPoolInterface Xcache() Xcache($config = []) Return a driver "Xcache" instance
45
 * @method static ExtendedCacheItemPoolInterface Zenddisk() Zenddisk($config = []) Return a driver "Zend disk cache" instance
46
 * @method static ExtendedCacheItemPoolInterface Zendshm() Zendshm($config = []) Return a driver "Zend memory cache" instance
47
 *
48
 */
49
class CacheManager
50
{
51
    /**
52
     * @var int
53
     */
54
    public static $ReadHits = 0;
55
56
    /**
57
     * @var int
58
     */
59
    public static $WriteHits = 0;
60
61
    /**
62
     * @var array
63
     */
64
    protected static $config = [
65
        /**
66
         * Specify if the item must provide detailed creation/modification dates
67
         */
68
      'itemDetailedDate' => false,
69
70
        /**
71
         * Automatically attempt to fallback to temporary directory
72
         * if the cache fails to write on the specified directory
73
         */
74
      'autoTmpFallback' => false,
75
76
        /**
77
         * Provide a secure file manipulation mechanism,
78
         * on intensive usage the performance can be affected.
79
         */
80
      'secureFileManipulation' => false,
81
82
        /**
83
         * Ignore Symfony notice for Symfony project which
84
         * do not makes use of PhpFastCache's Symfony Bundle
85
         */
86
      'ignoreSymfonyNotice' => false,
87
88
        /**
89
         * Default time-to-live in second
90
         */
91
      'defaultTtl' => 900,
92
93
        /**
94
         * Default key hash function
95
         * (md5 by default)
96
         */
97
      'defaultKeyHashFunction' => '',
98
99
        /**
100
         * The securityKey that will be used
101
         * to create sub-directory
102
         * (Files-based drivers only)
103
         */
104
      'securityKey' => 'Auto',
105
106
        /**
107
         * Auto-generate .htaccess if it's missing
108
         * (Files-based drivers only)
109
         */
110
      'htaccess' => true,
111
112
        /**
113
         * Default files chmod
114
         * 0777 recommended
115
         * (Files-based drivers only)
116
         */
117
      'default_chmod' => 0777,
118
119
        /**
120
         * The path where we will writecache files
121
         * default value if empty: sys_get_temp_dir()
122
         * (Files-based drivers only)
123
         */
124
      'path' => '',
125
126
        /**
127
         * Driver fallback in case of failure.
128
         * Caution, in case of failure an E_WARNING
129
         * error will always be raised
130
         */
131
      'fallback' => false,
132
133
        /**
134
         * Maximum size (bytes) of object store in memory
135
         * (Memcache(d) drivers only)
136
         */
137
      'limited_memory_each_object' => 4096,
138
139
        /**
140
         * Compress stored data, if the backend supports it
141
         * (Memcache(d) drivers only)
142
         */
143
      'compress_data' => false,
144
145
        /**
146
         * Prevent cache slams when
147
         * making use of heavy cache
148
         * items
149
         */
150
      'preventCacheSlams' => false,
151
152
        /**
153
         * Cache slams timeout
154
         * in seconds
155
         */
156
      'cacheSlamsTimeout' => 15,
157
158
        /**
159
         * Cache slams timeout
160
         * in seconds
161
         */
162
      'cacheFileExtension' => 'txt',
163
164
    ];
165
166
    /**
167
     * Feel free to propose your own one
168
     * by opening a pull request :)
169
     * @var array
170
     */
171
    protected static $safeFileExtensions = [
172
      'txt',
173
      'cache',
174
      'db',
175
      'pfc',
176
    ];
177
178
    /**
179
     * @var string
180
     */
181
    protected static $namespacePath;
182
183
    /**
184
     * @var ExtendedCacheItemPoolInterface[]
185
     */
186
    protected static $instances = [];
187
188
    /**
189
     * @param string $driver
190
     * @param array $config
191
     * @return ExtendedCacheItemPoolInterface
192
     * @throws phpFastCacheDriverCheckException
193
     * @throws phpFastCacheInvalidConfigurationException
194
     */
195
    public static function getInstance($driver = 'auto', array $config = [])
196
    {
197
        static $badPracticeOmeter = [];
198
199
        /**
200
         * @todo: Standardize a method for driver name
201
         */
202
        $driver = self::standardizeDriverName($driver);
203
        $config = array_merge(self::$config, $config);
204
        self::validateConfig($config);
205
        if (!$driver || $driver === 'Auto') {
206
            $driver = self::getAutoClass($config);
207
        }
208
209
        $instance = crc32($driver . serialize($config));
210
        if (!isset(self::$instances[ $instance ])) {
211
            $badPracticeOmeter[ $driver ] = 1;
212
            if (!$config[ 'ignoreSymfonyNotice' ] && interface_exists('Symfony\Component\HttpKernel\KernelInterface') && !class_exists('phpFastCache\Bundle\phpFastCacheBundle')) {
213
                trigger_error('A Symfony Bundle to make the PhpFastCache integration more easier is now available here: https://github.com/PHPSocialNetwork/phpfastcache-bundle',
214
                  E_USER_NOTICE);
215
            }
216
            $class = self::getNamespacePath() . $driver . '\Driver';
217
            try {
218
                self::$instances[ $instance ] = new $class($config);
219
                self::$instances[ $instance ]->setEventManager(EventManager::getInstance());
220
            } catch (phpFastCacheDriverCheckException $e) {
221
                if ($config[ 'fallback' ]) {
222
                    try {
223
                        $fallback = self::standardizeDriverName($config[ 'fallback' ]);
224
                        if ($fallback !== $driver) {
225
                            $class = self::getNamespacePath() . $fallback . '\Driver';
226
                            self::$instances[ $instance ] = new $class($config);
227
                            self::$instances[ $instance ]->setEventManager(EventManager::getInstance());
228
                            trigger_error(sprintf('The "%s" driver is unavailable at the moment, the fallback driver "%s" has been used instead.', $driver,
229
                              $fallback), E_USER_WARNING);
230
                        } else {
231
                            throw new phpFastCacheInvalidConfigurationException('The fallback driver cannot be the same than the default driver', 0, $e);
232
                        }
233
                    } catch (phpFastCacheInvalidArgumentException $e) {
234
                        throw new phpFastCacheInvalidConfigurationException('Invalid fallback driver configuration', 0, $e);
235
                    }
236
                } else {
237
                    throw new phpFastCacheDriverCheckException($e->getMessage(), $e->getCode(), $e);
238
                }
239
            }
240
        } else if ($badPracticeOmeter[ $driver ] >= 5) {
241
            trigger_error('[' . $driver . '] Calling many times CacheManager::getInstance() for already instanced drivers is a bad practice and have a significant impact on performances.
242
           See https://github.com/PHPSocialNetwork/phpfastcache/wiki/[V5]-Why-calling-getInstance%28%29-each-time-is-a-bad-practice-%3F');
243
        }
244
245
        $badPracticeOmeter[ $driver ]++;
246
247
        return self::$instances[ $instance ];
248
    }
249
250
    /**
251
     * This method is intended for internal
252
     * use only and should not be used for
253
     * any external development use the
254
     * getInstances() method instead
255
     *
256
     * @internal
257
     * @return ExtendedCacheItemPoolInterface[]
258
     */
259
    public static function getInstances()
260
    {
261
        return self::$instances;
262
    }
263
264
    /**
265
     * This method is intended for internal
266
     * use only and should not be used for
267
     * any external development use the
268
     * getInstances() method instead
269
     *
270
     * @internal
271
     * @return ExtendedCacheItemPoolInterface[]
272
     */
273
    public static function &getInternalInstances()
274
    {
275
        return self::$instances;
276
    }
277
278
    /**
279
     * @param $config
280
     * @return string
281
     * @throws phpFastCacheDriverCheckException
282
     */
283
    public static function getAutoClass(array $config = [])
284
    {
285
        static $autoDriver;
286
287
        if ($autoDriver === null) {
288
            foreach (self::getStaticSystemDrivers() as $driver) {
289
                try {
290
                    self::getInstance($driver, $config);
291
                    $autoDriver = $driver;
292
                } catch (phpFastCacheDriverCheckException $e) {
293
                    continue;
294
                }
295
            }
296
        }
297
298
        return $autoDriver;
299
    }
300
301
    /**
302
     * @param string $name
303
     * @param array $arguments
304
     * @return \Psr\Cache\CacheItemPoolInterface
305
     */
306
    public static function __callStatic($name, $arguments)
307
    {
308
        $options = (array_key_exists(0, $arguments) && is_array($arguments) ? $arguments[ 0 ] : []);
309
310
        return self::getInstance($name, $options);
311
    }
312
313
    /**
314
     * @return bool
315
     */
316
    public static function clearInstances()
317
    {
318
        self::$instances = [];
319
320
        gc_collect_cycles();
321
        return !count(self::$instances);
322
    }
323
324
    /**
325
     * @return string
326
     */
327
    public static function getNamespacePath()
328
    {
329
        return self::$namespacePath ?: __NAMESPACE__ . '\Drivers\\';
330
    }
331
332
    /**
333
     * @param string $path
334
     */
335
    public static function setNamespacePath($path)
336
    {
337
        self::$namespacePath = trim($path, "\\") . '\\';
338
    }
339
340
    /**
341
     * @param $name string|array
342
     * @param mixed $value
343
     * @throws phpFastCacheInvalidArgumentException
344
     */
345
    public static function setDefaultConfig($name, $value = null)
346
    {
347
        if (is_array($name)) {
348
            self::$config = array_merge(self::$config, $name);
349
        } else if (is_string($name)) {
350
            self::$config[ $name ] = $value;
351
        } else {
352
            throw new phpFastCacheInvalidArgumentException('Invalid variable type: $name');
353
        }
354
    }
355
356
    /**
357
     * @param $name string|array
358
     * @param mixed $value
359
     * @throws phpFastCacheInvalidConfigurationException
360
     * @deprecated Method "setup" is deprecated, please use "setDefaultConfig" method instead
361
     */
362
    public static function setup($name, $value = null)
363
    {
364
        throw new phpFastCacheInvalidConfigurationException(sprintf('Method "%s" is deprecated, please use "setDefaultConfig" method instead.', __FUNCTION__));
365
    }
366
367
368
    /**
369
     * @return array
370
     */
371
    public static function getDefaultConfig()
372
    {
373
        return self::$config;
374
    }
375
376
    /**
377
     * @return array
378
     */
379
    public static function getStaticSystemDrivers()
380
    {
381
        return [
382
          'Apc',
383
          'Apcu',
384
          'Cassandra',
385
          'Couchbase',
386
          'Couchdb',
387
          'Devnull',
388
          'Files',
389
          'Leveldb',
390
          'Memcache',
391
          'Memcached',
392
          'Memstatic',
393
          'Mongodb',
394
          'Predis',
395
          'Redis',
396
          'Ssdb',
397
          'Sqlite',
398
          'Wincache',
399
          'Xcache',
400
          'Zenddisk',
401
          'Zendshm',
402
        ];
403
    }
404
405
    /**
406
     * @return array
407
     */
408
    public static function getStaticAllDrivers()
409
    {
410
        return array_merge(self::getStaticSystemDrivers(), [
411
          'Devtrue',
412
          'Devfalse',
413
          'Cookie',
414
        ]);
415
    }
416
417
    /**
418
     * @param $driverName
419
     * @return string
420
     * @throws \phpFastCache\Exceptions\phpFastCacheInvalidArgumentException
421
     */
422
    public static function standardizeDriverName($driverName)
423
    {
424
        if (!is_string($driverName)) {
425
            throw new phpFastCacheInvalidArgumentException(sprintf('Expected $driverName to be a string got "%s" instead', gettype($driverName)));
426
        }
427
        return ucfirst(strtolower(trim($driverName)));
428
    }
429
430
    /**
431
     * @param array $config
432
     * @todo Move this to a config file
433
     * @throws phpFastCacheInvalidConfigurationException
434
     * @return bool
435
     */
436
    protected static function validateConfig(array $config)
437
    {
438
        foreach ($config as $configName => $configValue) {
439
            switch ($configName) {
440
                case 'itemDetailedDate':
441
                case 'autoTmpFallback':
442
                case 'secureFileManipulation':
443
                case 'ignoreSymfonyNotice':
444
                case 'htaccess':
445
                case 'compress_data':
446
                    if (!is_bool($configValue)) {
447
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
448
                    }
449
                    break;
450
                case 'defaultTtl':
451
                    if (!is_numeric($configValue)) {
452
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be numeric");
453
                    }
454
                    break;
455
                case 'defaultKeyHashFunction':
456
                    if (!is_string($configValue) && !function_exists($configValue)) {
457
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a valid function name string");
458
                    }
459
                    break;
460
                case 'securityKey':
461
                case 'path':
462
                    if (!is_string($configValue)) {
463
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a string");
464
                    }
465
                    break;
466
                case 'default_chmod':
467
                case 'limited_memory_each_object':
468
                    if (!is_int($configValue)) {
469
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be an integer");
470
                    }
471
                    break;
472
                case 'fallback':
473
                    if (!is_bool($configValue) && !is_string($configValue)) {
474
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean or string");
475
                    }
476
                    break;
477
                case 'cacheFileExtension':
478
                    if (!is_string($configValue)) {
479
                        throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
480
                    }
481
                    if (strpos($configValue, '.') !== false) {
482
                        throw new phpFastCacheInvalidConfigurationException("{$configName} cannot contain a dot \".\"");
483
                    }
484
                    if (!in_array($configValue, self::$safeFileExtensions)) {
485
                        throw new phpFastCacheInvalidConfigurationException(
486
                            "{$configName} is not a safe extension, currently allowed extension: " . implode(', ', self::$safeFileExtensions)
487
                        );
488
                    }
489
                    break;
490
            }
491
        }
492
493
        return true;
494
    }
495
}
496