497 lines
17 KiB
PHP
497 lines
17 KiB
PHP
<?php
|
|
/**
|
|
*
|
|
* This file is part of phpFastCache.
|
|
*
|
|
* @license MIT License (MIT)
|
|
*
|
|
* For full copyright and license information, please see the docs/CREDITS.txt file.
|
|
*
|
|
* @author Khoa Bui (khoaofgod) <khoaofgod@gmail.com> http://www.phpfastcache.com
|
|
* @author Georges.L (Geolim4) <contact@geolim4.com>
|
|
*
|
|
*/
|
|
|
|
namespace phpFastCache;
|
|
|
|
use phpFastCache\Core\Pool\ExtendedCacheItemPoolInterface;
|
|
use phpFastCache\Exceptions\phpFastCacheDriverCheckException;
|
|
use phpFastCache\Exceptions\phpFastCacheInvalidArgumentException;
|
|
use phpFastCache\Exceptions\phpFastCacheInvalidConfigurationException;
|
|
|
|
/**
|
|
* Class CacheManager
|
|
* @package phpFastCache
|
|
*
|
|
* @method static ExtendedCacheItemPoolInterface Apc() Apc($config = []) Return a driver "Apc" instance
|
|
* @method static ExtendedCacheItemPoolInterface Apcu() Apcu($config = []) Return a driver "Apcu" instance
|
|
* @method static ExtendedCacheItemPoolInterface Cassandra() Cassandra($config = []) Return a driver "Cassandra" instance
|
|
* @method static ExtendedCacheItemPoolInterface Cookie() Cookie($config = []) Return a driver "Cookie" instance
|
|
* @method static ExtendedCacheItemPoolInterface Couchbase() Couchbase($config = []) Return a driver "Couchbase" instance
|
|
* @method static ExtendedCacheItemPoolInterface Couchdb() Couchdb($config = []) Return a driver "Couchdb" instance
|
|
* @method static ExtendedCacheItemPoolInterface Devnull() Devnull($config = []) Return a driver "Devnull" instance
|
|
* @method static ExtendedCacheItemPoolInterface Files() Files($config = []) Return a driver "files" instance
|
|
* @method static ExtendedCacheItemPoolInterface Leveldb() Leveldb($config = []) Return a driver "Leveldb" instance
|
|
* @method static ExtendedCacheItemPoolInterface Memcache() Memcache($config = []) Return a driver "Memcache" instance
|
|
* @method static ExtendedCacheItemPoolInterface Memcached() Memcached($config = []) Return a driver "Memcached" instance
|
|
* @method static ExtendedCacheItemPoolInterface Memstatic() Memstatic($config = []) Return a driver "Memstatic" instance
|
|
* @method static ExtendedCacheItemPoolInterface Mongodb() Mongodb($config = []) Return a driver "Mongodb" instance
|
|
* @method static ExtendedCacheItemPoolInterface Predis() Predis($config = []) Return a driver "Predis" instance
|
|
* @method static ExtendedCacheItemPoolInterface Redis() Redis($config = []) Return a driver "Pedis" instance
|
|
* @method static ExtendedCacheItemPoolInterface Sqlite() Sqlite($config = []) Return a driver "Sqlite" instance
|
|
* @method static ExtendedCacheItemPoolInterface Ssdb() Ssdb($config = []) Return a driver "Ssdb" instance
|
|
* @method static ExtendedCacheItemPoolInterface Wincache() Wincache($config = []) Return a driver "Wincache" instance
|
|
* @method static ExtendedCacheItemPoolInterface Xcache() Xcache($config = []) Return a driver "Xcache" instance
|
|
* @method static ExtendedCacheItemPoolInterface Zenddisk() Zenddisk($config = []) Return a driver "Zend disk cache" instance
|
|
* @method static ExtendedCacheItemPoolInterface Zendshm() Zendshm($config = []) Return a driver "Zend memory cache" instance
|
|
*
|
|
*/
|
|
class CacheManager
|
|
{
|
|
/**
|
|
* @var int
|
|
*/
|
|
public static $ReadHits = 0;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
public static $WriteHits = 0;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected static $config = [
|
|
/**
|
|
* Specify if the item must provide detailed creation/modification dates
|
|
*/
|
|
'itemDetailedDate' => false,
|
|
|
|
/**
|
|
* Automatically attempt to fallback to temporary directory
|
|
* if the cache fails to write on the specified directory
|
|
*/
|
|
'autoTmpFallback' => false,
|
|
|
|
/**
|
|
* Provide a secure file manipulation mechanism,
|
|
* on intensive usage the performance can be affected.
|
|
*/
|
|
'secureFileManipulation' => false,
|
|
|
|
/**
|
|
* Ignore Symfony notice for Symfony project which
|
|
* do not makes use of PhpFastCache's Symfony Bundle
|
|
*/
|
|
'ignoreSymfonyNotice' => false,
|
|
|
|
/**
|
|
* Default time-to-live in second
|
|
*/
|
|
'defaultTtl' => 900,
|
|
|
|
/**
|
|
* Default key hash function
|
|
* (md5 by default)
|
|
*/
|
|
'defaultKeyHashFunction' => '',
|
|
|
|
/**
|
|
* The securityKey that will be used
|
|
* to create sub-directory
|
|
* (Files-based drivers only)
|
|
*/
|
|
'securityKey' => 'Auto',
|
|
|
|
/**
|
|
* Auto-generate .htaccess if it's missing
|
|
* (Files-based drivers only)
|
|
*/
|
|
'htaccess' => true,
|
|
|
|
/**
|
|
* Default files chmod
|
|
* 0777 recommended
|
|
* (Files-based drivers only)
|
|
*/
|
|
'default_chmod' => 0777,
|
|
|
|
/**
|
|
* The path where we will writecache files
|
|
* default value if empty: sys_get_temp_dir()
|
|
* (Files-based drivers only)
|
|
*/
|
|
'path' => '',
|
|
|
|
/**
|
|
* Driver fallback in case of failure.
|
|
* Caution, in case of failure an E_WARNING
|
|
* error will always be raised
|
|
*/
|
|
'fallback' => false,
|
|
|
|
/**
|
|
* Maximum size (bytes) of object store in memory
|
|
* (Memcache(d) drivers only)
|
|
*/
|
|
'limited_memory_each_object' => 4096,
|
|
|
|
/**
|
|
* Compress stored data, if the backend supports it
|
|
* (Memcache(d) drivers only)
|
|
*/
|
|
'compress_data' => false,
|
|
|
|
/**
|
|
* Prevent cache slams when
|
|
* making use of heavy cache
|
|
* items
|
|
*/
|
|
'preventCacheSlams' => false,
|
|
|
|
/**
|
|
* Cache slams timeout
|
|
* in seconds
|
|
*/
|
|
'cacheSlamsTimeout' => 15,
|
|
|
|
/**
|
|
* Cache slams timeout
|
|
* in seconds
|
|
*/
|
|
'cacheFileExtension' => 'txt',
|
|
|
|
];
|
|
|
|
/**
|
|
* Feel free to propose your own one
|
|
* by opening a pull request :)
|
|
* @var array
|
|
*/
|
|
protected static $safeFileExtensions = [
|
|
'txt',
|
|
'cache',
|
|
'db',
|
|
'pfc',
|
|
];
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected static $namespacePath;
|
|
|
|
/**
|
|
* @var ExtendedCacheItemPoolInterface[]
|
|
*/
|
|
protected static $instances = [];
|
|
|
|
/**
|
|
* @param string $driver
|
|
* @param array $config
|
|
* @return ExtendedCacheItemPoolInterface
|
|
* @throws phpFastCacheDriverCheckException
|
|
* @throws phpFastCacheInvalidConfigurationException
|
|
*/
|
|
public static function getInstance($driver = 'auto', array $config = [])
|
|
{
|
|
static $badPracticeOmeter = [];
|
|
|
|
/**
|
|
* @todo: Standardize a method for driver name
|
|
*/
|
|
$driver = self::standardizeDriverName($driver);
|
|
$config = array_merge(self::$config, $config);
|
|
self::validateConfig($config);
|
|
if (!$driver || $driver === 'Auto') {
|
|
$driver = self::getAutoClass($config);
|
|
}
|
|
|
|
$instance = crc32($driver . serialize($config));
|
|
if (!isset(self::$instances[ $instance ])) {
|
|
$badPracticeOmeter[ $driver ] = 1;
|
|
if (!$config[ 'ignoreSymfonyNotice' ] && interface_exists('Symfony\Component\HttpKernel\KernelInterface') && !class_exists('phpFastCache\Bundle\phpFastCacheBundle')) {
|
|
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);
|
|
}
|
|
$class = self::getNamespacePath() . $driver . '\Driver';
|
|
try {
|
|
self::$instances[ $instance ] = new $class($config);
|
|
self::$instances[ $instance ]->setEventManager(EventManager::getInstance());
|
|
} catch (phpFastCacheDriverCheckException $e) {
|
|
if ($config[ 'fallback' ]) {
|
|
try {
|
|
$fallback = self::standardizeDriverName($config[ 'fallback' ]);
|
|
if ($fallback !== $driver) {
|
|
$class = self::getNamespacePath() . $fallback . '\Driver';
|
|
self::$instances[ $instance ] = new $class($config);
|
|
self::$instances[ $instance ]->setEventManager(EventManager::getInstance());
|
|
trigger_error(sprintf('The "%s" driver is unavailable at the moment, the fallback driver "%s" has been used instead.', $driver,
|
|
$fallback), E_USER_WARNING);
|
|
} else {
|
|
throw new phpFastCacheInvalidConfigurationException('The fallback driver cannot be the same than the default driver', 0, $e);
|
|
}
|
|
} catch (phpFastCacheInvalidArgumentException $e) {
|
|
throw new phpFastCacheInvalidConfigurationException('Invalid fallback driver configuration', 0, $e);
|
|
}
|
|
} else {
|
|
throw new phpFastCacheDriverCheckException($e->getMessage(), $e->getCode(), $e);
|
|
}
|
|
}
|
|
} else if ($badPracticeOmeter[ $driver ] >= 5) {
|
|
trigger_error('[' . $driver . '] Calling many times CacheManager::getInstance() for already instanced drivers is a bad practice and have a significant impact on performances.
|
|
See https://github.com/PHPSocialNetwork/phpfastcache/wiki/[V5]-Why-calling-getInstance%28%29-each-time-is-a-bad-practice-%3F');
|
|
}
|
|
|
|
$badPracticeOmeter[ $driver ]++;
|
|
|
|
return self::$instances[ $instance ];
|
|
}
|
|
|
|
/**
|
|
* This method is intended for internal
|
|
* use only and should not be used for
|
|
* any external development use the
|
|
* getInstances() method instead
|
|
*
|
|
* @internal
|
|
* @return ExtendedCacheItemPoolInterface[]
|
|
*/
|
|
public static function getInstances()
|
|
{
|
|
return self::$instances;
|
|
}
|
|
|
|
/**
|
|
* This method is intended for internal
|
|
* use only and should not be used for
|
|
* any external development use the
|
|
* getInstances() method instead
|
|
*
|
|
* @internal
|
|
* @return ExtendedCacheItemPoolInterface[]
|
|
*/
|
|
public static function &getInternalInstances()
|
|
{
|
|
return self::$instances;
|
|
}
|
|
|
|
/**
|
|
* @param $config
|
|
* @return string
|
|
* @throws phpFastCacheDriverCheckException
|
|
*/
|
|
public static function getAutoClass(array $config = [])
|
|
{
|
|
static $autoDriver;
|
|
|
|
if ($autoDriver === null) {
|
|
foreach (self::getStaticSystemDrivers() as $driver) {
|
|
try {
|
|
self::getInstance($driver, $config);
|
|
$autoDriver = $driver;
|
|
break;
|
|
} catch (phpFastCacheDriverCheckException $e) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $autoDriver;
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param array $arguments
|
|
* @return \Psr\Cache\CacheItemPoolInterface
|
|
*/
|
|
public static function __callStatic($name, $arguments)
|
|
{
|
|
$options = (array_key_exists(0, $arguments) && is_array($arguments) ? $arguments[ 0 ] : []);
|
|
|
|
return self::getInstance($name, $options);
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public static function clearInstances()
|
|
{
|
|
self::$instances = [];
|
|
|
|
gc_collect_cycles();
|
|
return !count(self::$instances);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public static function getNamespacePath()
|
|
{
|
|
return self::$namespacePath ?: __NAMESPACE__ . '\Drivers\\';
|
|
}
|
|
|
|
/**
|
|
* @param string $path
|
|
*/
|
|
public static function setNamespacePath($path)
|
|
{
|
|
self::$namespacePath = trim($path, "\\") . '\\';
|
|
}
|
|
|
|
/**
|
|
* @param $name string|array
|
|
* @param mixed $value
|
|
* @throws phpFastCacheInvalidArgumentException
|
|
*/
|
|
public static function setDefaultConfig($name, $value = null)
|
|
{
|
|
if (is_array($name)) {
|
|
self::$config = array_merge(self::$config, $name);
|
|
} else if (is_string($name)) {
|
|
self::$config[ $name ] = $value;
|
|
} else {
|
|
throw new phpFastCacheInvalidArgumentException('Invalid variable type: $name');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $name string|array
|
|
* @param mixed $value
|
|
* @throws phpFastCacheInvalidConfigurationException
|
|
* @deprecated Method "setup" is deprecated, please use "setDefaultConfig" method instead
|
|
*/
|
|
public static function setup($name, $value = null)
|
|
{
|
|
throw new phpFastCacheInvalidConfigurationException(sprintf('Method "%s" is deprecated, please use "setDefaultConfig" method instead.', __FUNCTION__));
|
|
}
|
|
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public static function getDefaultConfig()
|
|
{
|
|
return self::$config;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public static function getStaticSystemDrivers()
|
|
{
|
|
return [
|
|
'Apc',
|
|
'Apcu',
|
|
'Cassandra',
|
|
'Couchbase',
|
|
'Couchdb',
|
|
'Devnull',
|
|
'Files',
|
|
'Leveldb',
|
|
'Memcache',
|
|
'Memcached',
|
|
'Memstatic',
|
|
'Mongodb',
|
|
'Predis',
|
|
'Redis',
|
|
'Ssdb',
|
|
'Sqlite',
|
|
'Wincache',
|
|
'Xcache',
|
|
'Zenddisk',
|
|
'Zendshm',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public static function getStaticAllDrivers()
|
|
{
|
|
return array_merge(self::getStaticSystemDrivers(), [
|
|
'Devtrue',
|
|
'Devfalse',
|
|
'Cookie',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param $driverName
|
|
* @return string
|
|
* @throws \phpFastCache\Exceptions\phpFastCacheInvalidArgumentException
|
|
*/
|
|
public static function standardizeDriverName($driverName)
|
|
{
|
|
if (!is_string($driverName)) {
|
|
throw new phpFastCacheInvalidArgumentException(sprintf('Expected $driverName to be a string got "%s" instead', gettype($driverName)));
|
|
}
|
|
return ucfirst(strtolower(trim($driverName)));
|
|
}
|
|
|
|
/**
|
|
* @param array $config
|
|
* @todo Move this to a config file
|
|
* @throws phpFastCacheInvalidConfigurationException
|
|
* @return bool
|
|
*/
|
|
protected static function validateConfig(array $config)
|
|
{
|
|
foreach ($config as $configName => $configValue) {
|
|
switch ($configName) {
|
|
case 'itemDetailedDate':
|
|
case 'autoTmpFallback':
|
|
case 'secureFileManipulation':
|
|
case 'ignoreSymfonyNotice':
|
|
case 'htaccess':
|
|
case 'compress_data':
|
|
if (!is_bool($configValue)) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
|
|
}
|
|
break;
|
|
case 'defaultTtl':
|
|
if (!is_numeric($configValue)) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} must be numeric");
|
|
}
|
|
break;
|
|
case 'defaultKeyHashFunction':
|
|
if (!is_string($configValue) && !function_exists($configValue)) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} must be a valid function name string");
|
|
}
|
|
break;
|
|
case 'securityKey':
|
|
case 'path':
|
|
if (!is_string($configValue) && (!is_bool($configValue) || $configValue)) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} must be a string or a false boolean");
|
|
}
|
|
break;
|
|
case 'default_chmod':
|
|
case 'limited_memory_each_object':
|
|
if (!is_int($configValue)) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} must be an integer");
|
|
}
|
|
break;
|
|
case 'fallback':
|
|
if (!is_bool($configValue) && !is_string($configValue)) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean or string");
|
|
}
|
|
break;
|
|
case 'cacheFileExtension':
|
|
if (!is_string($configValue)) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} must be a boolean");
|
|
}
|
|
if (strpos($configValue, '.') !== false) {
|
|
throw new phpFastCacheInvalidConfigurationException("{$configName} cannot contain a dot \".\"");
|
|
}
|
|
if (!in_array($configValue, self::$safeFileExtensions)) {
|
|
throw new phpFastCacheInvalidConfigurationException(
|
|
"{$configName} is not a safe extension, currently allowed extension: " . implode(', ', self::$safeFileExtensions)
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|