1
0

[+]: update composer.json (require min. php 7.0) + update "voku/simple-cache"

This commit is contained in:
Lars Moelleken
2019-03-03 11:46:11 +01:00
committed by onli
parent a93b232c68
commit d75ace7440
574 changed files with 44341 additions and 8530 deletions

View File

@ -1,3 +1,15 @@
# Changelog 4.0.1 (2019-03-03)
- hide "warning" about Zend OPcache API is restricted by "restrict_api"
# Changelog 4.0.0 (2019-02-24)
- add "CacheAdapterAutoManager"
- fix typos in "CachePsr16"
- "iCache->setItemToDate()" now accepts DateTimeInterface instead of DateTime
# Changelog 3.2.2 (2018-12-21)
- fix APC(u) detection for CLI usage

View File

@ -6,10 +6,10 @@
[![Latest Stable Version](https://poser.pugx.org/voku/simple-cache/v/stable)](https://packagist.org/packages/voku/simple-cache)
[![Total Downloads](https://poser.pugx.org/voku/simple-cache/downloads)](https://packagist.org/packages/voku/simple-cache)
[![License](https://poser.pugx.org/voku/simple-cache/license)](https://packagist.org/packages/voku/simple-cache)
[![Donate to this project using Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/moelleken)
[![Donate to this project using Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://www.patreon.com/voku)
:zap: Simple Cache Class
===================
# :zap: Simple Cache Class
This is a simple Cache Abstraction Layer for PHP >= 7.0 that provides a simple interaction
with your cache-server. You can define the Adapter / Serializer in the "constructor" or the class will auto-detect you server-cache in this order:
@ -21,27 +21,26 @@ with your cache-server. You can define the Adapter / Serializer in the "construc
5. OpCache (via PHP-files)
6. Static-PHP-Cache
## Get "Simple Cache"
### Get "Simple Cache"
You can download it from here, or require it using [composer](https://packagist.org/packages/voku/simple-cache).
```json
{
"require": {
"voku/simple-cache": "3.*"
"voku/simple-cache": "4.*"
}
}
```
## Install via "composer require"
### Install via "composer require"
```shell
composer require voku/simple-cache
composer require predis/predis # if you will use redis as cache, then add predis
```
## Quick Start
### Quick Start
```php
use voku\cache\Cache;
@ -55,7 +54,7 @@ $bar = $cache->getItem('foo');
```
## Usage
### Usage
```php
use voku\cache\Cache;
@ -100,6 +99,84 @@ If you use the parameter "$checkForUser" (=== true) in the constructor, then the
-> You can also overwrite the check for the user, if you add a global function named "checkForDev()".
## Overwrite the auto-connection option
## License
You can overwrite the cache auto-detect via "CacheAdapterAutoManager" and the
"$cacheAdapterManagerForAutoConnect" option in the "Cache"-constructor. Additional you can also
activate the "$cacheAdapterManagerForAutoConnectOverwrite" option in the "Cache"-constructor, so that
you can implement your own cache auto-detect logic.
```php
$cacheManager = new \voku\cache\CacheAdapterAutoManager();
// 1. check for "APCu" support first
$cacheManager->addAdapter(
\voku\cache\AdapterApcu::class
);
// 2. check for "APC" support
$cacheManager->addAdapter(
\voku\cache\AdapterApcu::class
);
// 3. try "OpCache"-Cache
$cacheManager->addAdapter(
\voku\cache\AdapterOpCache::class,
static function () {
$cacheDir = \realpath(\sys_get_temp_dir()) . '/simple_php_cache_opcache';
return $cacheDir;
}
);
// 4. try "File"-Cache
$cacheManager->addAdapter(
\voku\cache\AdapterFileSimple::class,
static function () {
$cacheDir = \realpath(\sys_get_temp_dir()) . '/simple_php_cache_file';
return $cacheDir;
}
);
// 5. use Memory Cache as final fallback
$cacheManager->addAdapter(
\voku\cache\AdapterArray::class
);
$cache = new \voku\cache\CachePsr16(
null, // use auto-detection
null, // use auto-detection
false, // do not check for usage
true, // enable the cache
false, // do not check for admin session
false, // do not check for dev
false, // do not check for admin session
false, // do not check for server vs. client ip
'', // do not use "_GET"-parameter for disabling
$cacheManager, // new auto-detection logic
true // overwrite the auto-detection logic
);
```
### Support
For support and donations please visit [Github](https://github.com/voku/simple-cache/) | [Issues](https://github.com/voku/simple-cache/issues) | [PayPal](https://paypal.me/moelleken) | [Patreon](https://www.patreon.com/voku).
For status updates and release announcements please visit [Releases](https://github.com/voku/simple-cache/releases) | [Twitter](https://twitter.com/suckup_de) | [Patreon](https://www.patreon.com/voku/posts).
For professional support please contact [me](https://about.me/voku).
### Thanks
- Thanks to [GitHub](https://github.com) (Microsoft) for hosting the code and a good infrastructure including Issues-Managment, etc.
- Thanks to [IntelliJ](https://www.jetbrains.com) as they make the best IDEs for PHP and they gave me an open source license for PhpStorm!
- Thanks to [Travis CI](https://travis-ci.com/) for being the most awesome, easiest continous integration tool out there!
- Thanks to [StyleCI](https://styleci.io/) for the simple but powerfull code style check.
- Thanks to [PHPStan](https://github.com/phpstan/phpstan) && [Psalm](https://github.com/vimeo/psalm) for relly great Static analysis tools and for discover bugs in the code!
### License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fvoku%2Fsimple-cache.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fvoku%2Fsimple-cache?ref=badge_large)

View File

@ -25,7 +25,7 @@
"psr/simple-cache": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~6.0"
"phpunit/phpunit": "~6.0 || ~7.0"
},
"autoload": {
"psr-4": {

View File

@ -85,7 +85,8 @@ class AdapterApc implements iAdapter
* @param bool $limited - If $limited is TRUE, the return value will exclude the individual list of cache
* entries. This is useful when trying to optimize calls for statistics gathering
*
* @return array|bool <p>Array of cached data (and meta-data) or FALSE on failure.</p>
* @return array|false
* <p>Array of cached data (and meta-data) or FALSE on failure.</p>
*/
public function cacheInfo(string $type = '', bool $limited = false): array
{

View File

@ -84,7 +84,8 @@ class AdapterApcu implements iAdapter
* @param bool $limited - If $limited is TRUE, the return value will exclude the individual list of cache
* entries. This is useful when trying to optimize calls for statistics gathering
*
* @return array|bool <p>Array of cached data (and meta-data) or FALSE on failure.</p>
* @return array|false
* <p>Array of cached data (and meta-data) or FALSE on failure.</p>
*/
public function cacheInfo(bool $limited = false): array
{

View File

@ -86,7 +86,9 @@ class AdapterFile extends AdapterFileAbstract
\fflush($fp);
\flock($fp, \LOCK_UN);
}
\fclose($fp);
if ($fp !== false) {
\fclose($fp);
}
return $octetWritten !== false;
}

View File

@ -34,7 +34,7 @@ abstract class AdapterFileAbstract implements iAdapter
protected $fileMode = '0755';
/**
* @param string|null $cacheDir
* @param \callable|string|null $cacheDir
*/
public function __construct($cacheDir = null)
{
@ -44,9 +44,13 @@ abstract class AdapterFileAbstract implements iAdapter
$cacheDir = \realpath(\sys_get_temp_dir()) . '/simple_php_cache';
}
$this->cacheDir = (string) $cacheDir;
if (\is_callable($cacheDir)) {
$this->cacheDir = (string) \call_user_func($cacheDir);
} else {
$this->cacheDir = (string) $cacheDir;
}
if ($this->createCacheDirectory($cacheDir) === true) {
if ($this->createCacheDirectory($this->cacheDir) === true) {
$this->installed = true;
}
}
@ -106,14 +110,14 @@ abstract class AdapterFileAbstract implements iAdapter
}
/**
* @param $cacheFile
* @param string $cacheFileWithPath
*
* @return bool
*/
protected function deleteFile($cacheFile): bool
protected function deleteFile($cacheFileWithPath): bool
{
if (\is_file($cacheFile)) {
return \unlink($cacheFile);
if (\is_file($cacheFileWithPath)) {
return \unlink($cacheFileWithPath);
}
return false;
@ -199,7 +203,7 @@ abstract class AdapterFileAbstract implements iAdapter
*
* e.g. '0777', or '0755' ...
*
* @param $fileMode
* @param string $fileMode
*/
public function setFileMode($fileMode)
{
@ -207,7 +211,7 @@ abstract class AdapterFileAbstract implements iAdapter
}
/**
* @param $ttl
* @param int $ttl
*
* @return bool
*/

View File

@ -29,7 +29,12 @@ class AdapterOpCache extends AdapterFileSimple
if (self::$hasCompileFileFunction === null) {
/** @noinspection PhpComposerExtensionStubsInspection */
self::$hasCompileFileFunction = \function_exists('opcache_compile_file') && !empty(\opcache_get_status());
/** @noinspection PhpUsageOfSilenceOperatorInspection */
self::$hasCompileFileFunction = (
\function_exists('opcache_compile_file')
&&
!empty(@\opcache_get_status())
);
}
}

View File

@ -20,12 +20,27 @@ use voku\cache\Exception\InvalidArgumentException;
class Cache implements iCache
{
/**
* @var iAdapter
* @var array
*/
protected static $STATIC_CACHE = [];
/**
* @var array
*/
protected static $STATIC_CACHE_EXPIRE = [];
/**
* @var array
*/
protected static $STATIC_CACHE_COUNTER = [];
/**
* @var iAdapter|null
*/
protected $adapter;
/**
* @var iSerializer
* @var iSerializer|null
*/
protected $serializer;
@ -69,21 +84,6 @@ class Cache implements iCache
*/
protected $isAdminSession;
/**
* @var array
*/
protected static $STATIC_CACHE = [];
/**
* @var array
*/
protected static $STATIC_CACHE_EXPIRE = [];
/**
* @var array
*/
protected static $STATIC_CACHE_COUNTER = [];
/**
* @var int
*/
@ -92,19 +92,27 @@ class Cache implements iCache
/**
* __construct
*
* @param iAdapter|null $adapter
* @param iSerializer|null $serializer
* @param bool $checkForUsage <p>admin-session || server-ip == client-ip || check for
* dev</p>
* @param bool $cacheEnabled <p>false will disable the cache (use it e.g. for global
* settings)</p>
* @param bool|string $isAdminSession <p>set a admin-id, if the user is a admin (so we can
* disable cache for this user)
* @param bool $useCheckForAdminSession <p>use $isAdminSession flag or not</p>
* @param bool $useCheckForDev <p>use checkForDev() or not</p>
* @param bool $useCheckForServerIpIsClientIp <p>use check for server-ip == client-ip or not</p>
* @param string $disableCacheGetParameter <p>set the _GET parameter for disabling the cache,
* disable this check via empty string</p>
* @param iAdapter|null $adapter
* @param iSerializer|null $serializer
* @param bool $checkForUsage <p>check for admin-session && check for
* server-ip == client-ip
* && check for dev</p>
* @param bool $cacheEnabled <p>false === disable the cache (use it
* e.g. for global settings)</p>
* @param bool $isAdminSession <p>true === disable cache for this user
* (use it e.g. for admin user settings)
* @param bool $useCheckForAdminSession <p>use $isAdminSession flag or not</p>
* @param bool $useCheckForDev <p>use checkForDev() or not</p>
* @param bool $useCheckForServerIpIsClientIp <p>use check for server-ip == client-ip
* or
* not</p>
* @param string $disableCacheGetParameter <p>set the _GET parameter for disabling
* the cache, disable this check via empty
* string</p>
* @param CacheAdapterAutoManager $cacheAdapterManagerForAutoConnect <p>Overwrite some Adapters for the
* auto-connect-function.</p>
* @param bool $cacheAdapterManagerForAutoConnectOverwrite <p>true === Use only Adapters from your
* "CacheAdapterManager".</p>
*/
public function __construct(
iAdapter $adapter = null,
@ -115,7 +123,9 @@ class Cache implements iCache
bool $useCheckForDev = true,
bool $useCheckForAdminSession = true,
bool $useCheckForServerIpIsClientIp = true,
string $disableCacheGetParameter = 'testWithoutCache'
string $disableCacheGetParameter = 'testWithoutCache',
CacheAdapterAutoManager $cacheAdapterManagerForAutoConnect = null,
bool $cacheAdapterManagerForAutoConnectOverwrite = false
) {
$this->isAdminSession = $isAdminSession;
@ -128,19 +138,19 @@ class Cache implements iCache
// First check if the cache is active at all.
$this->isActive = $cacheEnabled;
if (
$this->isActive === true
$this->isActive
&&
$checkForUsage === true
$checkForUsage
) {
$this->setActive($this->isCacheActiveForTheCurrentUser());
}
// If the cache is active, then try to auto-connect to the best possible cache-system.
if ($this->isActive === true) {
if ($this->isActive) {
$this->setPrefix($this->getTheDefaultPrefix());
if ($adapter === null) {
$adapter = $this->autoConnectToAvailableCacheSystem();
$adapter = $this->autoConnectToAvailableCacheSystem($cacheAdapterManagerForAutoConnect, $cacheAdapterManagerForAutoConnectOverwrite);
}
// INFO: Memcache(d) has his own "serializer", so don't use it twice
@ -172,93 +182,81 @@ class Cache implements iCache
}
/**
* enable / disable the cache
* Auto-connect to the available cache-system on the server.
*
* @param bool $isActive
*/
public function setActive(bool $isActive)
{
$this->isActive = $isActive;
}
/**
* check if the current use is a admin || dev || server == client
* @param CacheAdapterAutoManager $cacheAdapterManagerForAutoConnect <p>Overwrite some Adapters for the
* auto-connect-function.</p>
* @param bool $cacheAdapterManagerForAutoConnectOverwrite <p>true === Use only Adapters from your
* "CacheAdapterManager".</p>
*
* @return bool
* @return iAdapter
*/
public function isCacheActiveForTheCurrentUser(): bool
{
$active = true;
protected function autoConnectToAvailableCacheSystem(
CacheAdapterAutoManager $cacheAdapterManagerForAutoConnect = null,
bool $cacheAdapterManagerForAutoConnectOverwrite = false
): iAdapter {
static $AUTO_ADAPTER_STATIC_CACHE = null;
// test the cache, with this GET-parameter
if ($this->disableCacheGetParameter) {
$testCache = isset($_GET[$this->disableCacheGetParameter]) ? (int) $_GET[$this->disableCacheGetParameter] : 0;
} else {
$testCache = 0;
if (
\is_object($AUTO_ADAPTER_STATIC_CACHE)
&&
$AUTO_ADAPTER_STATIC_CACHE instanceof iAdapter
) {
return $AUTO_ADAPTER_STATIC_CACHE;
}
if ($testCache !== 1) {
if (
// admin session is active
(
$this->useCheckForAdminSession
&&
$this->isAdminSession
)
||
// server == client
(
$this->useCheckForServerIpIsClientIp === true
&&
isset($_SERVER['SERVER_ADDR'])
&&
$_SERVER['SERVER_ADDR'] === $this->getClientIp()
)
||
// user is a dev
(
$this->useCheckForDev === true
&&
$this->checkForDev() === true
)
) {
$active = false;
// init
$adapter = null;
$cacheAdapterManagerDefault = CacheAdapterAutoManager::getDefaultsForAutoInit();
if ($cacheAdapterManagerForAutoConnect !== null) {
if ($cacheAdapterManagerForAutoConnectOverwrite) {
$cacheAdapterManagerDefault = $cacheAdapterManagerForAutoConnect;
} else {
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManagerDefault->merge($cacheAdapterManagerForAutoConnect);
}
}
return $active;
foreach ($cacheAdapterManagerDefault->getAdapters() as $adapterTmp => $callableFunctionTmp) {
/** @var iAdapter $adapterTest */
if ($callableFunctionTmp !== null) {
$adapterTest = new $adapterTmp($callableFunctionTmp);
} else {
$adapterTest = new $adapterTmp();
}
if ($adapterTest->installed()) {
$adapter = $adapterTest;
break;
}
}
// save to static cache
$AUTO_ADAPTER_STATIC_CACHE = $adapter;
return $adapter;
}
/**
* returns the IP address of the client
* Calculate store-key (prefix + $rawKey).
*
* @param bool $trust_proxy_headers <p>
* Whether or not to trust the
* proxy headers HTTP_CLIENT_IP
* and HTTP_X_FORWARDED_FOR. ONLY
* use if your $_SERVER is behind a
* proxy that sets these values
* </p>
* @param string $rawKey
*
* @return string
*/
protected function getClientIp(bool $trust_proxy_headers = false): string
protected function calculateStoreKey(string $rawKey): string
{
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? 'NO_REMOTE_ADDR';
$str = $this->getPrefix() . $rawKey;
if ($trust_proxy_headers) {
return $remoteAddr;
if ($this->adapter instanceof AdapterFileAbstract) {
$str = $this->cleanStoreKey($str);
}
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $remoteAddr;
}
return $ip;
return $str;
}
/**
@ -297,182 +295,54 @@ class Cache implements iCache
}
/**
* Set the default-prefix via "SERVER"-var + "SESSION"-language.
*/
protected function getTheDefaultPrefix(): string
{
return ($_SERVER['SERVER_NAME'] ?? '') . '_' .
($_SERVER['THEME'] ?? '') . '_' .
($_SERVER['STAGE'] ?? '') . '_' .
($_SESSION['language'] ?? '') . '_' .
($_SESSION['language_extra'] ?? '');
}
/**
* Auto-connect to the available cache-system on the server.
*
* @return iAdapter
*/
protected function autoConnectToAvailableCacheSystem(): iAdapter
{
static $adapterCache;
if (\is_object($adapterCache) && $adapterCache instanceof iAdapter) {
return $adapterCache;
}
$memcached = null;
$isMemcachedAvailable = false;
if (\extension_loaded('memcached')) {
/** @noinspection PhpComposerExtensionStubsInspection */
$memcached = new \Memcached();
/** @noinspection PhpUsageOfSilenceOperatorInspection */
$isMemcachedAvailable = @$memcached->addServer('127.0.0.1', 11211);
}
if ($isMemcachedAvailable === false) {
$memcached = null;
}
$adapterMemcached = new AdapterMemcached($memcached);
if ($adapterMemcached->installed() === true) {
// -------------------------------------------------------------
// "Memcached"
// -------------------------------------------------------------
$adapter = $adapterMemcached;
} else {
$memcache = null;
$isMemcacheAvailable = false;
/** @noinspection ClassConstantCanBeUsedInspection */
if (\class_exists('\Memcache')) {
/** @noinspection PhpComposerExtensionStubsInspection */
$memcache = new \Memcache();
/** @noinspection PhpUsageOfSilenceOperatorInspection */
$isMemcacheAvailable = @$memcache->connect('127.0.0.1', 11211);
}
if ($isMemcacheAvailable === false) {
$memcache = null;
}
$adapterMemcache = new AdapterMemcache($memcache);
if ($adapterMemcache->installed() === true) {
// -------------------------------------------------------------
// "Memcache"
// -------------------------------------------------------------
$adapter = $adapterMemcache;
} else {
$redis = null;
$isRedisAvailable = false;
if (
\extension_loaded('redis')
&&
\class_exists('\Predis\Client')
) {
/** @noinspection PhpUndefinedNamespaceInspection */
/** @noinspection PhpUndefinedClassInspection */
$redis = new \Predis\Client(
[
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
'timeout' => '2.0',
]
);
try {
/** @noinspection PhpUndefinedMethodInspection */
$redis->connect();
/** @noinspection PhpUndefinedMethodInspection */
$isRedisAvailable = $redis->getConnection()->isConnected();
} catch (\Exception $e) {
// nothing
}
}
if ($isRedisAvailable === false) {
$redis = null;
}
$adapterRedis = new AdapterPredis($redis);
if ($adapterRedis->installed() === true) {
// -------------------------------------------------------------
// Redis
// -------------------------------------------------------------
$adapter = $adapterRedis;
} else {
$adapterXcache = new AdapterXcache();
if ($adapterXcache->installed() === true) {
// -------------------------------------------------------------
// "Xcache"
// -------------------------------------------------------------
$adapter = $adapterXcache;
} else {
$adapterApcu = new AdapterApcu();
if ($adapterApcu->installed() === true) {
// -------------------------------------------------------------
// "APCu"
// -------------------------------------------------------------
$adapter = $adapterApcu;
} else {
$adapterApc = new AdapterApc();
if ($adapterApc->installed() === true) {
// -------------------------------------------------------------
// "APC"
// -------------------------------------------------------------
$adapter = $adapterApc;
} else {
$adapterObCache = new AdapterOpCache();
if ($adapterObCache->installed() === true) {
// -------------------------------------------------------------
// OpCache (via PHP-files)
// -------------------------------------------------------------
$adapter = $adapterObCache;
} else {
// -------------------------------------------------------------
// Static-PHP-Cache
// -------------------------------------------------------------
$adapter = new AdapterArray();
}
}
}
}
}
}
}
// save to static cache
$adapterCache = $adapter;
return $adapter;
}
/**
* Set "isReady" state.
*
* @param bool $isReady
*/
protected function setCacheIsReady(bool $isReady)
{
$this->isReady = $isReady;
}
/**
* Get the "isReady" state.
* @param string $storeKey
*
* @return bool
*/
public function getCacheIsReady(): bool
protected function checkForStaticCache(string $storeKey): bool
{
return $this->isReady;
return !empty(self::$STATIC_CACHE)
&&
\array_key_exists($storeKey, self::$STATIC_CACHE)
&&
\array_key_exists($storeKey, self::$STATIC_CACHE_EXPIRE)
&&
\time() <= self::$STATIC_CACHE_EXPIRE[$storeKey];
}
/**
* Clean store-key (required e.g. for the "File"-Adapter).
*
* @param string $str
*
* @return string
*/
protected function cleanStoreKey(string $str): string
{
return \md5($str);
}
/**
* Check if cached-item exists.
*
* @param string $key
*
* @return bool
*/
public function existsItem(string $key): bool
{
if (!$this->adapter instanceof iAdapter) {
return false;
}
$storeKey = $this->calculateStoreKey($key);
// check static-cache
if ($this->checkForStaticCache($storeKey)) {
return true;
}
return $this->adapter->exists($storeKey);
}
/**
@ -503,21 +373,21 @@ class Cache implements iCache
// get from static-cache
if (
$useStaticCache === true
$useStaticCache
&&
$this->checkForStaticCache($storeKey) === true
$this->checkForStaticCache($storeKey)
) {
return self::$STATIC_CACHE[$storeKey];
}
$serialized = $this->adapter->get($storeKey);
$value = $serialized ? $this->serializer->unserialize($serialized) : null;
$value = $serialized && $this->serializer ? $this->serializer->unserialize($serialized) : null;
self::$STATIC_CACHE_COUNTER[$storeKey]++;
// save into static-cache if needed
if (
$useStaticCache === true
$useStaticCache
&&
(
(
@ -540,97 +410,55 @@ class Cache implements iCache
}
/**
* Calculate store-key (prefix + $rawKey).
*
* @param string $rawKey
*
* @return string
*/
protected function calculateStoreKey(string $rawKey): string
{
$str = $this->getPrefix() . $rawKey;
if ($this->adapter instanceof AdapterFileAbstract) {
$str = $this->cleanStoreKey($str);
}
return $str;
}
/**
* Clean store-key (required e.g. for the "File"-Adapter).
*
* @param string $str
*
* @return string
*/
protected function cleanStoreKey(string $str): string
{
return \md5($str);
}
/**
* Get the prefix.
*
* @return string
*/
public function getPrefix(): string
{
return $this->prefix;
}
/**
* !!! Set the prefix. !!!
*
* WARNING: Do not use if you don't know what you do. Because this will overwrite the default prefix.
*
* @param string $prefix
*/
public function setPrefix(string $prefix)
{
$this->prefix = $prefix;
}
/**
* Get the current value, when the static cache is used.
*
* @return int
*/
public function getStaticCacheHitCounter(): int
{
return $this->staticCacheHitCounter;
}
/**
* Set the static-hit-counter: Who often do we hit the cache, before we use static cache?
*
* @param int $staticCacheHitCounter
*/
public function setStaticCacheHitCounter(int $staticCacheHitCounter)
{
$this->staticCacheHitCounter = $staticCacheHitCounter;
}
/**
* Set cache-item by key => value + date.
*
* @param string $key
* @param mixed $value
* @param \DateTime $date <p>If the date is in the past, we will remove the existing cache-item.</p>
*
* @throws InvalidArgumentException <p>If the $date is in the past.</p>
* Remove all cached-items.
*
* @return bool
*/
public function setItemToDate(string $key, $value, \DateTime $date): bool
public function removeAll(): bool
{
$ttl = $date->getTimestamp() - \time();
if ($ttl <= 0) {
throw new InvalidArgumentException('Date in the past.');
if (!$this->adapter instanceof iAdapter) {
return false;
}
return $this->setItem($key, $value, $ttl);
// remove static-cache
if (!empty(self::$STATIC_CACHE)) {
self::$STATIC_CACHE = [];
self::$STATIC_CACHE_COUNTER = [];
self::$STATIC_CACHE_EXPIRE = [];
}
return $this->adapter->removeAll();
}
/**
* Remove a cached-item.
*
* @param string $key
*
* @return bool
*/
public function removeItem(string $key): bool
{
if (!$this->adapter instanceof iAdapter) {
return false;
}
$storeKey = $this->calculateStoreKey($key);
// remove static-cache
if (
!empty(self::$STATIC_CACHE)
&&
\array_key_exists($storeKey, self::$STATIC_CACHE)
) {
unset(
self::$STATIC_CACHE[$storeKey],
self::$STATIC_CACHE_COUNTER[$storeKey],
self::$STATIC_CACHE_EXPIRE[$storeKey]
);
}
return $this->adapter->remove($storeKey);
}
/**
@ -658,15 +486,15 @@ class Cache implements iCache
$serialized = $this->serializer->serialize($value);
// update static-cache, if it's exists
if (\array_key_exists($storeKey, self::$STATIC_CACHE) === true) {
if (\array_key_exists($storeKey, self::$STATIC_CACHE)) {
self::$STATIC_CACHE[$storeKey] = $value;
}
if ($ttl) {
if ($ttl instanceof \DateInterval) {
// Converting to a TTL in seconds
$dateTimeNow = new \DateTime('now');
$ttl = $dateTimeNow->add($ttl)->getTimestamp() - \time();
/** @noinspection PhpUnhandledExceptionInspection */
$ttl = (new \DateTimeImmutable('now'))->add($ttl)->getTimestamp() - \time();
}
// always cache the TTL time, maybe we need this later ...
@ -679,91 +507,100 @@ class Cache implements iCache
}
/**
* Remove a cached-item.
* Set cache-item by key => value + date.
*
* @param string $key
* @param string $key
* @param mixed $value
* @param \DateTimeInterface $date <p>If the date is in the past, we will remove the existing cache-item.</p>
*
* @throws InvalidArgumentException
* <p>If the $date is in the past.</p>
*
* @return bool
*/
public function removeItem(string $key): bool
public function setItemToDate(string $key, $value, \DateTimeInterface $date): bool
{
if (!$this->adapter instanceof iAdapter) {
return false;
$ttl = $date->getTimestamp() - \time();
if ($ttl <= 0) {
throw new InvalidArgumentException('Date in the past.');
}
$storeKey = $this->calculateStoreKey($key);
// remove static-cache
if (
!empty(self::$STATIC_CACHE)
&&
\array_key_exists($storeKey, self::$STATIC_CACHE) === true
) {
unset(self::$STATIC_CACHE[$storeKey], self::$STATIC_CACHE_COUNTER[$storeKey], self::$STATIC_CACHE_EXPIRE[$storeKey]
);
}
return $this->adapter->remove($storeKey);
return $this->setItem($key, $value, $ttl);
}
/**
* Remove all cached-items.
* Get the "isReady" state.
*
* @return bool
*/
public function removeAll(): bool
public function getCacheIsReady(): bool
{
if (!$this->adapter instanceof iAdapter) {
return false;
}
// remove static-cache
if (!empty(self::$STATIC_CACHE)) {
self::$STATIC_CACHE = [];
self::$STATIC_CACHE_COUNTER = [];
self::$STATIC_CACHE_EXPIRE = [];
}
return $this->adapter->removeAll();
return $this->isReady;
}
/**
* Check if cached-item exists.
* returns the IP address of the client
*
* @param string $key
* @param bool $trust_proxy_headers <p>
* Whether or not to trust the
* proxy headers HTTP_CLIENT_IP
* and HTTP_X_FORWARDED_FOR. ONLY
* use if your $_SERVER is behind a
* proxy that sets these values
* </p>
*
* @return bool
* @return string
*/
public function existsItem(string $key): bool
protected function getClientIp(bool $trust_proxy_headers = false): string
{
if (!$this->adapter instanceof iAdapter) {
return false;
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? 'NO_REMOTE_ADDR';
if ($trust_proxy_headers) {
return $remoteAddr;
}
$storeKey = $this->calculateStoreKey($key);
// check static-cache
if ($this->checkForStaticCache($storeKey) === true) {
return true;
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $remoteAddr;
}
return $this->adapter->exists($storeKey);
return $ip;
}
/**
* @param string $storeKey
* Get the prefix.
*
* @return bool
* @return string
*/
protected function checkForStaticCache(string $storeKey): bool
public function getPrefix(): string
{
return !empty(self::$STATIC_CACHE)
&&
\array_key_exists($storeKey, self::$STATIC_CACHE) === true
&&
\array_key_exists($storeKey, self::$STATIC_CACHE_EXPIRE) === true
&&
\time() <= self::$STATIC_CACHE_EXPIRE[$storeKey];
return $this->prefix;
}
/**
* Get the current value, when the static cache is used.
*
* @return int
*/
public function getStaticCacheHitCounter(): int
{
return $this->staticCacheHitCounter;
}
/**
* Set the default-prefix via "SERVER"-var + "SESSION"-language.
*/
protected function getTheDefaultPrefix(): string
{
return ($_SERVER['SERVER_NAME'] ?? '') . '_' .
($_SERVER['THEME'] ?? '') . '_' .
($_SERVER['STAGE'] ?? '') . '_' .
($_SESSION['language'] ?? '') . '_' .
($_SESSION['language_extra'] ?? '');
}
/**
@ -795,4 +632,94 @@ class Cache implements iCache
return '';
}
/**
* check if the current use is a admin || dev || server == client
*
* @return bool
*/
public function isCacheActiveForTheCurrentUser(): bool
{
$active = true;
// test the cache, with this GET-parameter
if ($this->disableCacheGetParameter) {
$testCache = isset($_GET[$this->disableCacheGetParameter]) ? (int) $_GET[$this->disableCacheGetParameter] : 0;
} else {
$testCache = 0;
}
if ($testCache !== 1) {
if (
// admin session is active
(
$this->useCheckForAdminSession
&&
$this->isAdminSession
)
||
// server == client
(
$this->useCheckForServerIpIsClientIp
&&
isset($_SERVER['SERVER_ADDR'])
&&
$_SERVER['SERVER_ADDR'] === $this->getClientIp()
)
||
// user is a dev
(
$this->useCheckForDev
&&
$this->checkForDev()
)
) {
$active = false;
}
}
return $active;
}
/**
* enable / disable the cache
*
* @param bool $isActive
*/
public function setActive(bool $isActive)
{
$this->isActive = $isActive;
}
/**
* Set "isReady" state.
*
* @param bool $isReady
*/
protected function setCacheIsReady(bool $isReady)
{
$this->isReady = $isReady;
}
/**
* !!! Set the prefix. !!!
*
* WARNING: Do not use if you don't know what you do. Because this will overwrite the default prefix.
*
* @param string $prefix
*/
public function setPrefix(string $prefix)
{
$this->prefix = $prefix;
}
/**
* Set the static-hit-counter: Who often do we hit the cache, before we use static cache?
*
* @param int $staticCacheHitCounter
*/
public function setStaticCacheHitCounter(int $staticCacheHitCounter)
{
$this->staticCacheHitCounter = $staticCacheHitCounter;
}
}

View File

@ -0,0 +1,230 @@
<?php
declare(strict_types=1);
namespace voku\cache;
use voku\cache\Exception\InvalidArgumentException;
class CacheAdapterAutoManager
{
/**
* @var string[]
*/
private $adapter = [];
/**
* @var callable[]|null[]
*/
private $callableFunctions = [];
/**
* @param string $adapter
* @param callable|null $callableFunction
*
* @throws InvalidArgumentException
*
* @return $this
*/
public function addAdapter(
string $adapter,
callable $callableFunction = null
): self
{
$this->validateAdapter($adapter);
$this->validateCallable($callableFunction);
$this->adapter[] = $adapter;
$this->callableFunctions[] = $callableFunction;
return $this;
}
/**
* @return \Generator|\Generator<string, callable>
*/
public function getAdapters(): \Generator
{
foreach ($this->adapter as $key => $value) {
yield $this->adapter[$key] => $this->callableFunctions[$key];
}
}
/**
* @param self $adapterManager
*
* @throws InvalidArgumentException
*
* @return CacheAdapterAutoManager
*/
public function merge(self $adapterManager): self
{
foreach ($adapterManager->getAdapters() as $adapterTmp => $callableFunctionTmp) {
$this->validateAdapter($adapterTmp);
$this->validateCallable($callableFunctionTmp);
$key = \array_search($adapterTmp, $this->adapter, true);
if ($key) {
$this->adapter[$key] = $adapterTmp;
$this->callableFunctions[$key] = $callableFunctionTmp;
} else {
$this->adapter[] = $adapterTmp;
$this->callableFunctions[] = $callableFunctionTmp;
}
}
return $this;
}
/**
* @param string $replaceAdapter
*
* @throws InvalidArgumentException
*/
private function validateAdapter(string $replaceAdapter)
{
/** @noinspection PhpUnhandledExceptionInspection */
$interfaces = (new \ReflectionClass($replaceAdapter))->getInterfaces();
if (!\array_key_exists(iAdapter::class, $interfaces)) {
throw new InvalidArgumentException('"' . $replaceAdapter . '" did not implement the "iAdapter"-interface [' . \print_r($interfaces, true) . ']');
}
}
/**
* @param callable $callableFunction
*
* @throws InvalidArgumentException
*/
private function validateCallable(callable $callableFunction = null)
{
if (
$callableFunction !== null
&&
!\is_callable($callableFunction)
) {
throw new InvalidArgumentException('$callableFunction is not callable');
}
}
/**
* @return CacheAdapterAutoManager
*/
public static function getDefaultsForAutoInit(): self
{
$cacheAdapterManager = new self();
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterMemcached::class,
static function () {
$memcached = null;
$isMemcachedAvailable = false;
if (\extension_loaded('memcached')) {
/** @noinspection PhpComposerExtensionStubsInspection */
$memcached = new \Memcached();
/** @noinspection PhpUsageOfSilenceOperatorInspection */
$isMemcachedAvailable = @$memcached->addServer('127.0.0.1', 11211);
}
if (!$isMemcachedAvailable) {
$memcached = null;
}
return $memcached;
}
);
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterMemcache::class,
static function () {
$memcache = null;
$isMemcacheAvailable = false;
/** @noinspection ClassConstantCanBeUsedInspection */
if (\class_exists('\Memcache')) {
/** @noinspection PhpComposerExtensionStubsInspection */
$memcache = new \Memcache();
/** @noinspection PhpUsageOfSilenceOperatorInspection */
$isMemcacheAvailable = @$memcache->connect('127.0.0.1', 11211);
}
if (!$isMemcacheAvailable) {
$memcache = null;
}
return $memcache;
}
);
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterPredis::class,
static function () {
$redis = null;
$isRedisAvailable = false;
if (
\extension_loaded('redis')
&&
\class_exists('\Predis\Client')
) {
/** @noinspection PhpUndefinedNamespaceInspection */
/** @noinspection PhpUndefinedClassInspection */
$redis = new \Predis\Client(
[
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
'timeout' => '2.0',
]
);
try {
/** @noinspection PhpUndefinedMethodInspection */
$redis->connect();
/** @noinspection PhpUndefinedMethodInspection */
$isRedisAvailable = $redis->getConnection()->isConnected();
} catch (\Exception $e) {
// nothing
}
}
if ($isRedisAvailable === false) {
$redis = null;
}
return $redis;
}
);
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterXcache::class
);
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterApcu::class
);
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterApc::class
);
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterOpCache::class,
static function () {
return \realpath(\sys_get_temp_dir()) . '/simple_php_cache';
}
);
/** @noinspection PhpUnhandledExceptionInspection */
$cacheAdapterManager->addAdapter(
AdapterArray::class
);
return $cacheAdapterManager;
}
}

View File

@ -4,9 +4,6 @@ declare(strict_types=1);
namespace voku\cache;
/**
* CacheChain: global-cache-chain class
*/
class CacheChain implements iCache
{
/**
@ -87,13 +84,13 @@ class CacheChain implements iCache
$results[] = $cache->setItem($key, $value, $ttl);
}
return \in_array(false, $results, true) === false;
return !\in_array(false, $results, true);
}
/**
* {@inheritdoc}
*/
public function setItemToDate(string $key, $value, \DateTime $date): bool
public function setItemToDate(string $key, $value, \DateTimeInterface $date): bool
{
// init
$results = [];
@ -103,7 +100,7 @@ class CacheChain implements iCache
$results[] = $cache->setItemToDate($key, $value, $date);
}
return \in_array(false, $results, true) === false;
return !\in_array(false, $results, true);
}
/**
@ -118,7 +115,7 @@ class CacheChain implements iCache
$results[] = $cache->removeItem($key);
}
return \in_array(false, $results, true) === false;
return !\in_array(false, $results, true);
}
/**
@ -147,6 +144,6 @@ class CacheChain implements iCache
$results[] = $cache->removeAll();
}
return \in_array(false, $results, true) === false;
return !\in_array(false, $results, true);
}
}

View File

@ -40,7 +40,7 @@ class CachePsr16 extends Cache implements CacheInterface
/**
* Deletes multiple cache items in a single operation.
*
* @param \iterable $keys a list of string-based keys to be deleted
* @param iterable $keys a list of string-based keys to be deleted
*
* @throws InvalidArgumentException
*
@ -48,16 +48,20 @@ class CachePsr16 extends Cache implements CacheInterface
*/
public function deleteMultiple($keys): bool
{
if (!\is_array($keys) && !($keys instanceof \Traversable)) {
if (
!\is_array($keys)
&&
!($keys instanceof \Traversable)
) {
throw new InvalidArgumentException('$keys is not iterable:' . \print_r($keys, true));
}
$results = [];
foreach ((array) $keys as $key) {
$results = $this->delete($key);
$results[] = $this->delete($key);
}
return \in_array(false, $results, true) === false;
return !\in_array(false, $results, true);
}
/**
@ -72,7 +76,7 @@ class CachePsr16 extends Cache implements CacheInterface
*/
public function get($key, $default = null)
{
if ($this->has($key) === true) {
if ($this->has($key)) {
return $this->getItem($key);
}
@ -82,17 +86,21 @@ class CachePsr16 extends Cache implements CacheInterface
/**
* Obtains multiple cache items by their unique keys.
*
* @param \iterable $keys a list of keys that can obtained in a single operation
* @param mixed $default default value to return for keys that do not exist
* @param iterable $keys a list of keys that can obtained in a single operation
* @param mixed $default default value to return for keys that do not exist
*
* @throws InvalidArgumentException
*
* @return \iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as
* value.
* @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as
* value.
*/
public function getMultiple($keys, $default = null)
{
if (!\is_array($keys) && !($keys instanceof \Traversable)) {
if (
!\is_array($keys)
&&
!($keys instanceof \Traversable)
) {
throw new InvalidArgumentException('$keys is not iterable:' . \print_r($keys, true));
}
@ -152,7 +160,7 @@ class CachePsr16 extends Cache implements CacheInterface
/**
* Persists a set of key => value pairs in the cache, with an optional TTL.
*
* @param \iterable $values a list of key => value pairs for a multiple-set operation
* @param iterable $values a list of key => value pairs for a multiple-set operation
* @param \DateInterval|int|null $ttl Optional. The TTL value of this item. If no value is sent and
* the driver supports TTL then the library may set a default value
* for it or let the driver take care of that.
@ -163,15 +171,19 @@ class CachePsr16 extends Cache implements CacheInterface
*/
public function setMultiple($values, $ttl = null): bool
{
if (!\is_array($values) && !($values instanceof \Traversable)) {
if (
!\is_array($values)
&&
!($values instanceof \Traversable)
) {
throw new InvalidArgumentException('$values is not iterable:' . \print_r($values, true));
}
$results = [];
foreach ((array) $values as $key => $value) {
$results = $this->set($key, $value, $ttl);
$results[] = $this->set($key, $value, $ttl);
}
return \in_array(false, $results, true) === false;
return !\in_array(false, $results, true);
}
}

View File

@ -32,13 +32,13 @@ interface iCache
/**
* set item a special expire-date
*
* @param string $key
* @param mixed $value
* @param \DateTime $date
* @param string $key
* @param mixed $value
* @param \DateTimeInterface $date
*
* @return bool
*/
public function setItemToDate(string $key, $value, \DateTime $date): bool;
public function setItemToDate(string $key, $value, \DateTimeInterface $date): bool;
/**
* remove item

View File

@ -12,18 +12,14 @@ interface iSerializer
/**
* serialize
*
* @param $value
*
* @return mixed
* @param mixed $value
*/
public function serialize($value);
/**
* unserialize
*
* @param $value
*
* @return mixed
* @param string $value
*/
public function unserialize($value);
}