实现请求缓存1天。
This commit is contained in:
@@ -11,6 +11,16 @@ use think\facade\Cache;
|
|||||||
use think\facade\Log;
|
use think\facade\Log;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
use GuzzleHttp\HandlerStack;
|
||||||
|
use Kevinrob\GuzzleCache\CacheMiddleware;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcher;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\GreedyCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\Psr6CacheStorage;
|
||||||
|
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Zm extends Command
|
class Zm extends Command
|
||||||
{
|
{
|
||||||
private $aria2 = null;
|
private $aria2 = null;
|
||||||
@@ -35,10 +45,13 @@ class Zm extends Command
|
|||||||
$this->aria2->setOption('enable-http-pipelining', true); //设置http管道化
|
$this->aria2->setOption('enable-http-pipelining', true); //设置http管道化
|
||||||
$this->aria2->setOption('enable-http-keep-alive', true); //设置http保持连接
|
$this->aria2->setOption('enable-http-keep-alive', true); //设置http保持连接
|
||||||
|
|
||||||
// Disable SSL verification for cURL requests
|
|
||||||
|
|
||||||
|
$cachePath = runtime_path() . 'guzzle_cache';
|
||||||
$this->client = new Client([
|
$this->client = new Client([
|
||||||
'verify' => false,
|
'verify' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->completed = false;
|
$this->completed = false;
|
||||||
while($this->completed === false){
|
while($this->completed === false){
|
||||||
try {
|
try {
|
||||||
@@ -69,14 +82,19 @@ class Zm extends Command
|
|||||||
$curr_total++;
|
$curr_total++;
|
||||||
$curr_item = $value;
|
$curr_item = $value;
|
||||||
$curr_item_title = $curr_item['title'];
|
$curr_item_title = $curr_item['title'];
|
||||||
$c = $savepath . '/' . $lable_title . '/' . $curr_item_title;
|
$c = trim($savepath . '/' . $lable_title . '/' . $curr_item_title);
|
||||||
// dump('存储路径',$c);
|
// dump('存储路径',$c);
|
||||||
|
try {
|
||||||
is_dir($c) || mkdir($c,0755,true);
|
is_dir($c) || mkdir($c,0755,true);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
dump('创建目录失败:',$c,$e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
$author = $savepath . '/' . $lable_title . '/' . $curr_item_title . '/' . '作者.txt';
|
$author = $c . '/' . '作者.txt';
|
||||||
$this->savetxt($author,$curr_item['author']);
|
$this->savetxt($author,$curr_item['author']);
|
||||||
|
|
||||||
$introduce = $savepath . '/' . $lable_title . '/' . $curr_item_title . '/' . '介绍.txt';
|
$introduce = $c . '/' . '介绍.txt';
|
||||||
$this->savetxt($introduce,$curr_item['introduce']);
|
$this->savetxt($introduce,$curr_item['introduce']);
|
||||||
|
|
||||||
$cover = $curr_item['cover'];
|
$cover = $curr_item['cover'];
|
||||||
@@ -87,8 +105,13 @@ class Zm extends Command
|
|||||||
$this->savetxt($cover_path,$cover);
|
$this->savetxt($cover_path,$cover);
|
||||||
$curr_details = $this->getCurrDetails($curr_item['id']);
|
$curr_details = $this->getCurrDetails($curr_item['id']);
|
||||||
foreach($curr_details['data']['curriculum']['allClassSectionTrue'] as $class){
|
foreach($curr_details['data']['curriculum']['allClassSectionTrue'] as $class){
|
||||||
$class_path = $c . '/' . $class['title'];
|
$class_path = trim($c . '/' . $class['title']);
|
||||||
|
try {
|
||||||
is_dir($class_path) || mkdir($class_path,0755,true);
|
is_dir($class_path) || mkdir($class_path,0755,true);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
dump('创建目录失败:',$class_path,$e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
$class_details = $this->getClassDetails($class['id']);
|
$class_details = $this->getClassDetails($class['id']);
|
||||||
if($class_details == false){
|
if($class_details == false){
|
||||||
dump('课程未授权,跳过下载',$class['title']);
|
dump('课程未授权,跳过下载',$class['title']);
|
||||||
@@ -265,11 +288,16 @@ class Zm extends Command
|
|||||||
if($result['code'] == -3){
|
if($result['code'] == -3){
|
||||||
return $this->getLable($result['sign']);
|
return $this->getLable($result['sign']);
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function getCurr($label_id,$page,$sign = 'null')
|
public function getCurr($label_id,$page,$sign = 'null')
|
||||||
{
|
{
|
||||||
|
$cacheKey = 'zm_curr_' . $label_id . '_' . $page;
|
||||||
|
if(Cache::has($cacheKey)){
|
||||||
|
return Cache::get($cacheKey);
|
||||||
|
}
|
||||||
$options = [
|
$options = [
|
||||||
'headers'=>[
|
'headers'=>[
|
||||||
'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.489.400 QQBrowser/13.7.6351.400',
|
'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.489.400 QQBrowser/13.7.6351.400',
|
||||||
@@ -289,6 +317,7 @@ class Zm extends Command
|
|||||||
if($result['code'] == -3){
|
if($result['code'] == -3){
|
||||||
return $this->getCurr($label_id,$page,$result['sign']);
|
return $this->getCurr($label_id,$page,$result['sign']);
|
||||||
}else if($result['code'] == 0){
|
}else if($result['code'] == 0){
|
||||||
|
Cache::set($cacheKey,$result,86400);
|
||||||
return $result;
|
return $result;
|
||||||
}else{
|
}else{
|
||||||
dump($result['msg']);
|
dump($result['msg']);
|
||||||
@@ -299,6 +328,10 @@ class Zm extends Command
|
|||||||
|
|
||||||
public function getCurrDetails($curr_id,$sign = 'null')
|
public function getCurrDetails($curr_id,$sign = 'null')
|
||||||
{
|
{
|
||||||
|
$cacheKey = 'zm_curr_details_' . $curr_id;
|
||||||
|
if(Cache::has($cacheKey)){
|
||||||
|
return Cache::get($cacheKey);
|
||||||
|
}
|
||||||
$options = [
|
$options = [
|
||||||
'headers'=>[
|
'headers'=>[
|
||||||
'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.489.400 QQBrowser/13.7.6351.400',
|
'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.489.400 QQBrowser/13.7.6351.400',
|
||||||
@@ -318,6 +351,7 @@ class Zm extends Command
|
|||||||
if($result['code'] == -3){
|
if($result['code'] == -3){
|
||||||
return $this->getCurrDetails($curr_id,$result['sign']);
|
return $this->getCurrDetails($curr_id,$result['sign']);
|
||||||
}else if($result['code'] == 0){
|
}else if($result['code'] == 0){
|
||||||
|
Cache::set($cacheKey,$result,86400);
|
||||||
return $result;
|
return $result;
|
||||||
}else{
|
}else{
|
||||||
dump($result['msg']);
|
dump($result['msg']);
|
||||||
@@ -326,6 +360,10 @@ class Zm extends Command
|
|||||||
}
|
}
|
||||||
public function getClassDetails($curr_id,$sign = 'null')
|
public function getClassDetails($curr_id,$sign = 'null')
|
||||||
{
|
{
|
||||||
|
$cacheKey = 'zm_class_details_' . $curr_id;
|
||||||
|
if(Cache::has($cacheKey)){
|
||||||
|
return Cache::get($cacheKey);
|
||||||
|
}
|
||||||
$options = [
|
$options = [
|
||||||
'headers'=>[
|
'headers'=>[
|
||||||
'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.489.400 QQBrowser/13.7.6351.400',
|
'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36 Core/1.116.489.400 QQBrowser/13.7.6351.400',
|
||||||
@@ -346,6 +384,7 @@ class Zm extends Command
|
|||||||
if($result['code'] == -3){
|
if($result['code'] == -3){
|
||||||
return $this->getClassDetails($curr_id,$result['sign']);
|
return $this->getClassDetails($curr_id,$result['sign']);
|
||||||
}else if($result['code'] == 0){
|
}else if($result['code'] == 0){
|
||||||
|
Cache::set($cacheKey,$result,86400);
|
||||||
return $result;
|
return $result;
|
||||||
}else if($result['code'] == -5){
|
}else if($result['code'] == -5){
|
||||||
return false;
|
return false;
|
||||||
@@ -355,8 +394,6 @@ class Zm extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private function savetxt(string $file,string $content)
|
private function savetxt(string $file,string $content)
|
||||||
{
|
{
|
||||||
if(is_file($file)){
|
if(is_file($file)){
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
"guzzlehttp/guzzle": "~6.0",
|
"guzzlehttp/guzzle": "~6.0",
|
||||||
"php-curl-class/php-curl-class": "^9.14",
|
"php-curl-class/php-curl-class": "^9.14",
|
||||||
"topthink/think-helper": "^3.1",
|
"topthink/think-helper": "^3.1",
|
||||||
"daijie/aria2": "^1.1"
|
"daijie/aria2": "^1.1",
|
||||||
|
"symfony/cache": "^5.4",
|
||||||
|
"kevinrob/guzzle-cache-middleware": "^5.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/var-dumper": "^4.2",
|
"symfony/var-dumper": "^4.2",
|
||||||
|
|||||||
1270
composer.lock
generated
1270
composer.lock
generated
File diff suppressed because it is too large
Load Diff
1
vendor/composer/autoload_classmap.php
vendored
1
vendor/composer/autoload_classmap.php
vendored
@@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir);
|
|||||||
return array(
|
return array(
|
||||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
|
||||||
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||||
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||||
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||||
|
|||||||
11
vendor/composer/autoload_files.php
vendored
11
vendor/composer/autoload_files.php
vendored
@@ -7,15 +7,16 @@ $baseDir = dirname($vendorDir);
|
|||||||
|
|
||||||
return array(
|
return array(
|
||||||
'9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
|
'9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
|
||||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
|
||||||
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
|
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
|
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||||
|
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||||
|
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
|
||||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
|
||||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||||
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
|
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
);
|
);
|
||||||
|
|||||||
9
vendor/composer/autoload_psr4.php
vendored
9
vendor/composer/autoload_psr4.php
vendored
@@ -7,14 +7,18 @@ $baseDir = dirname($vendorDir);
|
|||||||
|
|
||||||
return array(
|
return array(
|
||||||
'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'),
|
'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'),
|
||||||
'think\\' => array($vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-filesystem/src'),
|
'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-filesystem/src', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src'),
|
||||||
'app\\' => array($baseDir . '/app'),
|
'app\\' => array($baseDir . '/app'),
|
||||||
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
||||||
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'),
|
||||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
||||||
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
||||||
|
'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
|
||||||
|
'Symfony\\Contracts\\Cache\\' => array($vendorDir . '/symfony/cache-contracts'),
|
||||||
|
'Symfony\\Component\\VarExporter\\' => array($vendorDir . '/symfony/var-exporter'),
|
||||||
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
|
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
|
||||||
|
'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'),
|
||||||
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
|
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
|
||||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||||
@@ -23,6 +27,7 @@ return array(
|
|||||||
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
||||||
'League\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/src'),
|
'League\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/src'),
|
||||||
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
|
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
|
||||||
|
'Kevinrob\\GuzzleCache\\' => array($vendorDir . '/kevinrob/guzzle-cache-middleware/src'),
|
||||||
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
||||||
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
|
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
|
||||||
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
|
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
|
||||||
|
|||||||
54
vendor/composer/autoload_static.php
vendored
54
vendor/composer/autoload_static.php
vendored
@@ -8,16 +8,17 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
{
|
{
|
||||||
public static $files = array (
|
public static $files = array (
|
||||||
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
|
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
|
||||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
|
||||||
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
|
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
||||||
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
|
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||||
|
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||||
|
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
|
||||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
|
||||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
|
||||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||||
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
|
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -34,11 +35,15 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
'S' =>
|
'S' =>
|
||||||
array (
|
array (
|
||||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||||
'Symfony\\Polyfill\\Php72\\' => 23,
|
'Symfony\\Polyfill\\Php73\\' => 23,
|
||||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
||||||
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
||||||
|
'Symfony\\Contracts\\Service\\' => 26,
|
||||||
|
'Symfony\\Contracts\\Cache\\' => 24,
|
||||||
|
'Symfony\\Component\\VarExporter\\' => 30,
|
||||||
'Symfony\\Component\\VarDumper\\' => 28,
|
'Symfony\\Component\\VarDumper\\' => 28,
|
||||||
|
'Symfony\\Component\\Cache\\' => 24,
|
||||||
),
|
),
|
||||||
'P' =>
|
'P' =>
|
||||||
array (
|
array (
|
||||||
@@ -54,6 +59,10 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
'League\\Flysystem\\Cached\\' => 24,
|
'League\\Flysystem\\Cached\\' => 24,
|
||||||
'League\\Flysystem\\' => 17,
|
'League\\Flysystem\\' => 17,
|
||||||
),
|
),
|
||||||
|
'K' =>
|
||||||
|
array (
|
||||||
|
'Kevinrob\\GuzzleCache\\' => 21,
|
||||||
|
),
|
||||||
'G' =>
|
'G' =>
|
||||||
array (
|
array (
|
||||||
'GuzzleHttp\\Psr7\\' => 16,
|
'GuzzleHttp\\Psr7\\' => 16,
|
||||||
@@ -73,10 +82,10 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
),
|
),
|
||||||
'think\\' =>
|
'think\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/topthink/think-helper/src',
|
0 => __DIR__ . '/..' . '/topthink/framework/src/think',
|
||||||
1 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
1 => __DIR__ . '/..' . '/topthink/think-filesystem/src',
|
||||||
2 => __DIR__ . '/..' . '/topthink/framework/src/think',
|
2 => __DIR__ . '/..' . '/topthink/think-helper/src',
|
||||||
3 => __DIR__ . '/..' . '/topthink/think-filesystem/src',
|
3 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
||||||
),
|
),
|
||||||
'app\\' =>
|
'app\\' =>
|
||||||
array (
|
array (
|
||||||
@@ -86,9 +95,9 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||||
),
|
),
|
||||||
'Symfony\\Polyfill\\Php72\\' =>
|
'Symfony\\Polyfill\\Php73\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
|
0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
|
||||||
),
|
),
|
||||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||||
array (
|
array (
|
||||||
@@ -102,10 +111,26 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
|
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
|
||||||
),
|
),
|
||||||
|
'Symfony\\Contracts\\Service\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/service-contracts',
|
||||||
|
),
|
||||||
|
'Symfony\\Contracts\\Cache\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/cache-contracts',
|
||||||
|
),
|
||||||
|
'Symfony\\Component\\VarExporter\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/var-exporter',
|
||||||
|
),
|
||||||
'Symfony\\Component\\VarDumper\\' =>
|
'Symfony\\Component\\VarDumper\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/symfony/var-dumper',
|
0 => __DIR__ . '/..' . '/symfony/var-dumper',
|
||||||
),
|
),
|
||||||
|
'Symfony\\Component\\Cache\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/cache',
|
||||||
|
),
|
||||||
'Psr\\SimpleCache\\' =>
|
'Psr\\SimpleCache\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
|
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
|
||||||
@@ -138,6 +163,10 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
||||||
),
|
),
|
||||||
|
'Kevinrob\\GuzzleCache\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/kevinrob/guzzle-cache-middleware/src',
|
||||||
|
),
|
||||||
'GuzzleHttp\\Psr7\\' =>
|
'GuzzleHttp\\Psr7\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||||
@@ -167,6 +196,7 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
|||||||
public static $classMap = array (
|
public static $classMap = array (
|
||||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
|
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
|
||||||
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||||
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||||
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||||
|
|||||||
1073
vendor/composer/installed.json
vendored
1073
vendor/composer/installed.json
vendored
File diff suppressed because it is too large
Load Diff
151
vendor/composer/installed.php
vendored
151
vendor/composer/installed.php
vendored
@@ -29,23 +29,32 @@
|
|||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/promises' => array(
|
'guzzlehttp/promises' => array(
|
||||||
'pretty_version' => '1.5.2',
|
'pretty_version' => '1.5.3',
|
||||||
'version' => '1.5.2.0',
|
'version' => '1.5.3.0',
|
||||||
'reference' => 'b94b2807d85443f9719887892882d0329d1e2598',
|
'reference' => '67ab6e18aaa14d753cc148911d273f6e6cb6721e',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../guzzlehttp/promises',
|
'install_path' => __DIR__ . '/../guzzlehttp/promises',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/psr7' => array(
|
'guzzlehttp/psr7' => array(
|
||||||
'pretty_version' => '1.9.0',
|
'pretty_version' => '1.9.1',
|
||||||
'version' => '1.9.0.0',
|
'version' => '1.9.1.0',
|
||||||
'reference' => 'e98e3e6d4f86621a9b75f623996e6bbdeb4b9318',
|
'reference' => 'e4490cabc77465aaee90b20cfc9a770f8c04be6b',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
|
'kevinrob/guzzle-cache-middleware' => array(
|
||||||
|
'pretty_version' => 'v5.1.0',
|
||||||
|
'version' => '5.1.0.0',
|
||||||
|
'reference' => '6bd64dbbe5155107d84a0f67140a8822a709c6d0',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../kevinrob/guzzle-cache-middleware',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
'league/flysystem' => array(
|
'league/flysystem' => array(
|
||||||
'pretty_version' => '1.1.10',
|
'pretty_version' => '1.1.10',
|
||||||
'version' => '1.1.10.0',
|
'version' => '1.1.10.0',
|
||||||
@@ -65,18 +74,18 @@
|
|||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'league/mime-type-detection' => array(
|
'league/mime-type-detection' => array(
|
||||||
'pretty_version' => '1.11.0',
|
'pretty_version' => '1.12.0',
|
||||||
'version' => '1.11.0.0',
|
'version' => '1.12.0.0',
|
||||||
'reference' => 'ff6248ea87a9f116e78edd6002e39e5128a0d4dd',
|
'reference' => 'c7f2872fb273bf493811473dafc88d60ae829f48',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../league/mime-type-detection',
|
'install_path' => __DIR__ . '/../league/mime-type-detection',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'php-curl-class/php-curl-class' => array(
|
'php-curl-class/php-curl-class' => array(
|
||||||
'pretty_version' => '9.14.3',
|
'pretty_version' => '9.19.2',
|
||||||
'version' => '9.14.3.0',
|
'version' => '9.19.2.0',
|
||||||
'reference' => '5d87676a3a7f83dd33d65f3c8d97f36679305193',
|
'reference' => 'c41efeb4ea2dc3cf8f90f8f967b0fcf45a41e294',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../php-curl-class/php-curl-class',
|
'install_path' => __DIR__ . '/../php-curl-class/php-curl-class',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
@@ -91,6 +100,12 @@
|
|||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
|
'psr/cache-implementation' => array(
|
||||||
|
'dev_requirement' => false,
|
||||||
|
'provided' => array(
|
||||||
|
0 => '1.0|2.0',
|
||||||
|
),
|
||||||
|
),
|
||||||
'psr/container' => array(
|
'psr/container' => array(
|
||||||
'pretty_version' => '1.1.1',
|
'pretty_version' => '1.1.1',
|
||||||
'version' => '1.1.1.0',
|
'version' => '1.1.1.0',
|
||||||
@@ -133,6 +148,12 @@
|
|||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
|
'psr/simple-cache-implementation' => array(
|
||||||
|
'dev_requirement' => false,
|
||||||
|
'provided' => array(
|
||||||
|
0 => '1.0|2.0',
|
||||||
|
),
|
||||||
|
),
|
||||||
'ralouphie/getallheaders' => array(
|
'ralouphie/getallheaders' => array(
|
||||||
'pretty_version' => '3.0.3',
|
'pretty_version' => '3.0.3',
|
||||||
'version' => '3.0.3.0',
|
'version' => '3.0.3.0',
|
||||||
@@ -142,50 +163,101 @@
|
|||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
|
'symfony/cache' => array(
|
||||||
|
'pretty_version' => 'v5.4.46',
|
||||||
|
'version' => '5.4.46.0',
|
||||||
|
'reference' => '0fe08ee32cec2748fbfea10c52d3ee02049e0f6b',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/cache',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'symfony/cache-contracts' => array(
|
||||||
|
'pretty_version' => 'v2.5.4',
|
||||||
|
'version' => '2.5.4.0',
|
||||||
|
'reference' => '517c3a3619dadfa6952c4651767fcadffb4df65e',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/cache-contracts',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'symfony/cache-implementation' => array(
|
||||||
|
'dev_requirement' => false,
|
||||||
|
'provided' => array(
|
||||||
|
0 => '1.0|2.0',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'symfony/deprecation-contracts' => array(
|
||||||
|
'pretty_version' => 'v2.5.4',
|
||||||
|
'version' => '2.5.4.0',
|
||||||
|
'reference' => '605389f2a7e5625f273b53960dc46aeaf9c62918',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
'symfony/polyfill-intl-idn' => array(
|
'symfony/polyfill-intl-idn' => array(
|
||||||
'pretty_version' => 'v1.27.0',
|
'pretty_version' => 'v1.31.0',
|
||||||
'version' => '1.27.0.0',
|
'version' => '1.31.0.0',
|
||||||
'reference' => '639084e360537a19f9ee352433b84ce831f3d2da',
|
'reference' => 'c36586dcf89a12315939e00ec9b4474adcb1d773',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
|
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-intl-normalizer' => array(
|
'symfony/polyfill-intl-normalizer' => array(
|
||||||
'pretty_version' => 'v1.27.0',
|
'pretty_version' => 'v1.31.0',
|
||||||
'version' => '1.27.0.0',
|
'version' => '1.31.0.0',
|
||||||
'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6',
|
'reference' => '3833d7255cc303546435cb650316bff708a1c75c',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
|
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-mbstring' => array(
|
'symfony/polyfill-mbstring' => array(
|
||||||
'pretty_version' => 'v1.27.0',
|
'pretty_version' => 'v1.31.0',
|
||||||
'version' => '1.27.0.0',
|
'version' => '1.31.0.0',
|
||||||
'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
|
'reference' => '85181ba99b2345b0ef10ce42ecac37612d9fd341',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => true,
|
'dev_requirement' => true,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-php72' => array(
|
'symfony/polyfill-php72' => array(
|
||||||
'pretty_version' => 'v1.27.0',
|
'pretty_version' => 'v1.31.0',
|
||||||
'version' => '1.27.0.0',
|
'version' => '1.31.0.0',
|
||||||
'reference' => '869329b1e9894268a8a61dabb69153029b7a8c97',
|
'reference' => 'fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce',
|
||||||
|
'type' => 'metapackage',
|
||||||
|
'install_path' => NULL,
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => true,
|
||||||
|
),
|
||||||
|
'symfony/polyfill-php73' => array(
|
||||||
|
'pretty_version' => 'v1.31.0',
|
||||||
|
'version' => '1.31.0.0',
|
||||||
|
'reference' => '0f68c03565dcaaf25a890667542e8bd75fe7e5bb',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-php72',
|
'install_path' => __DIR__ . '/../symfony/polyfill-php73',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/polyfill-php80' => array(
|
'symfony/polyfill-php80' => array(
|
||||||
'pretty_version' => 'v1.27.0',
|
'pretty_version' => 'v1.31.0',
|
||||||
'version' => '1.27.0.0',
|
'version' => '1.31.0.0',
|
||||||
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
|
'reference' => '60328e362d4c2c802a54fcbf04f9d3fb892b4cf8',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => true,
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'symfony/service-contracts' => array(
|
||||||
|
'pretty_version' => 'v2.5.4',
|
||||||
|
'version' => '2.5.4.0',
|
||||||
|
'reference' => 'f37b419f7aea2e9abf10abd261832cace12e3300',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/service-contracts',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'symfony/var-dumper' => array(
|
'symfony/var-dumper' => array(
|
||||||
'pretty_version' => 'v4.4.47',
|
'pretty_version' => 'v4.4.47',
|
||||||
@@ -196,6 +268,15 @@
|
|||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => true,
|
'dev_requirement' => true,
|
||||||
),
|
),
|
||||||
|
'symfony/var-exporter' => array(
|
||||||
|
'pretty_version' => 'v5.4.45',
|
||||||
|
'version' => '5.4.45.0',
|
||||||
|
'reference' => '862700068db0ddfd8c5b850671e029a90246ec75',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../symfony/var-exporter',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
'topthink/framework' => array(
|
'topthink/framework' => array(
|
||||||
'pretty_version' => 'v6.1.5',
|
'pretty_version' => 'v6.1.5',
|
||||||
'version' => '6.1.5.0',
|
'version' => '6.1.5.0',
|
||||||
@@ -224,18 +305,18 @@
|
|||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'topthink/think-helper' => array(
|
'topthink/think-helper' => array(
|
||||||
'pretty_version' => 'v3.1.6',
|
'pretty_version' => 'v3.1.11',
|
||||||
'version' => '3.1.6.0',
|
'version' => '3.1.11.0',
|
||||||
'reference' => '769acbe50a4274327162f9c68ec2e89a38eb2aff',
|
'reference' => '1d6ada9b9f3130046bf6922fe1bd159c8d88a33c',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../topthink/think-helper',
|
'install_path' => __DIR__ . '/../topthink/think-helper',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'topthink/think-orm' => array(
|
'topthink/think-orm' => array(
|
||||||
'pretty_version' => 'v2.0.60',
|
'pretty_version' => 'v2.0.62',
|
||||||
'version' => '2.0.60.0',
|
'version' => '2.0.62.0',
|
||||||
'reference' => '8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46',
|
'reference' => 'e53bfea572a133039ad687077120de5521af617f',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../topthink/think-orm',
|
'install_path' => __DIR__ . '/../topthink/think-orm',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
|||||||
6
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
6
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
@@ -1,5 +1,11 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 1.5.3 - 2023-05-21
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Removed remaining usage of deprecated functions
|
||||||
|
|
||||||
## 1.5.2 - 2022-08-07
|
## 1.5.2 - 2022-08-07
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
5
vendor/guzzlehttp/promises/composer.json
vendored
5
vendor/guzzlehttp/promises/composer.json
vendored
@@ -46,11 +46,6 @@
|
|||||||
"test": "vendor/bin/simple-phpunit",
|
"test": "vendor/bin/simple-phpunit",
|
||||||
"test-ci": "vendor/bin/simple-phpunit --coverage-text"
|
"test-ci": "vendor/bin/simple-phpunit --coverage-text"
|
||||||
},
|
},
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.5-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"config": {
|
"config": {
|
||||||
"preferred-install": "dist",
|
"preferred-install": "dist",
|
||||||
"sort-packages": true
|
"sort-packages": true
|
||||||
|
|||||||
2
vendor/guzzlehttp/promises/src/Each.php
vendored
2
vendor/guzzlehttp/promises/src/Each.php
vendored
@@ -78,7 +78,7 @@ final class Each
|
|||||||
$concurrency,
|
$concurrency,
|
||||||
callable $onFulfilled = null
|
callable $onFulfilled = null
|
||||||
) {
|
) {
|
||||||
return each_limit(
|
return self::ofLimit(
|
||||||
$iterable,
|
$iterable,
|
||||||
$concurrency,
|
$concurrency,
|
||||||
$onFulfilled,
|
$onFulfilled,
|
||||||
|
|||||||
2
vendor/guzzlehttp/promises/src/Utils.php
vendored
2
vendor/guzzlehttp/promises/src/Utils.php
vendored
@@ -107,7 +107,7 @@ final class Utils
|
|||||||
{
|
{
|
||||||
$results = [];
|
$results = [];
|
||||||
foreach ($promises as $key => $promise) {
|
foreach ($promises as $key => $promise) {
|
||||||
$results[$key] = inspect($promise);
|
$results[$key] = self::inspect($promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
strategy:
|
strategy:
|
||||||
max-parallel: 10
|
max-parallel: 10
|
||||||
matrix:
|
matrix:
|
||||||
@@ -21,11 +21,7 @@ jobs:
|
|||||||
extensions: mbstring
|
extensions: mbstring
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Mimic PHP 8.0
|
|
||||||
run: composer config platform.php 8.0.999
|
|
||||||
if: matrix.php > 8
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: composer update --no-interaction --no-progress
|
run: composer update --no-interaction --no-progress
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Test
|
name: Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
strategy:
|
strategy:
|
||||||
max-parallel: 10
|
max-parallel: 10
|
||||||
matrix:
|
matrix:
|
||||||
php: ['7.2', '7.3', '7.4', '8.0']
|
php: ['7.2', '7.3', '7.4', '8.0', '8.1']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set up PHP
|
- name: Set up PHP
|
||||||
@@ -21,7 +20,7 @@ jobs:
|
|||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Download dependencies
|
- name: Download dependencies
|
||||||
uses: ramsey/composer-install@v1
|
uses: ramsey/composer-install@v1
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
php-cs-fixer:
|
php-cs-fixer:
|
||||||
name: PHP-CS-Fixer
|
name: PHP-CS-Fixer
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
|
|||||||
6
vendor/guzzlehttp/psr7/CHANGELOG.md
vendored
6
vendor/guzzlehttp/psr7/CHANGELOG.md
vendored
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 1.9.1 - 2023-04-17
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed header validation issue
|
||||||
|
|
||||||
## 1.9.0 - 2022-06-20
|
## 1.9.0 - 2022-06-20
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
5
vendor/guzzlehttp/psr7/composer.json
vendored
5
vendor/guzzlehttp/psr7/composer.json
vendored
@@ -61,11 +61,6 @@
|
|||||||
"GuzzleHttp\\Tests\\Psr7\\": "tests/"
|
"GuzzleHttp\\Tests\\Psr7\\": "tests/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.9-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"config": {
|
"config": {
|
||||||
"preferred-install": "dist",
|
"preferred-install": "dist",
|
||||||
"sort-packages": true,
|
"sort-packages": true,
|
||||||
|
|||||||
13
vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
13
vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
@@ -226,12 +226,9 @@ trait MessageTrait
|
|||||||
throw new \InvalidArgumentException('Header name can not be empty.');
|
throw new \InvalidArgumentException('Header name can not be empty.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) {
|
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
sprintf(
|
sprintf('"%s" is not valid header name.', $header)
|
||||||
'"%s" is not valid header name',
|
|
||||||
$header
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,8 +260,10 @@ trait MessageTrait
|
|||||||
// Clients must not send a request with line folding and a server sending folded headers is
|
// Clients must not send a request with line folding and a server sending folded headers is
|
||||||
// likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
|
// likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
|
||||||
// folding is not likely to break any legitimate use case.
|
// folding is not likely to break any legitimate use case.
|
||||||
if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/', $value)) {
|
if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
|
||||||
throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value));
|
throw new \InvalidArgumentException(
|
||||||
|
sprintf('"%s" is not valid header value.', $value)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
vendor/kevinrob/guzzle-cache-middleware/LICENSE
vendored
Normal file
22
vendor/kevinrob/guzzle-cache-middleware/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Kevin Robatel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
309
vendor/kevinrob/guzzle-cache-middleware/README.md
vendored
Normal file
309
vendor/kevinrob/guzzle-cache-middleware/README.md
vendored
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
# guzzle-cache-middleware
|
||||||
|
|
||||||
|
[](https://packagist.org/packages/kevinrob/guzzle-cache-middleware) [](https://packagist.org/packages/kevinrob/guzzle-cache-middleware) [](https://packagist.org/packages/kevinrob/guzzle-cache-middleware)
|
||||||
|
 [](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/?branch=master) [](https://scrutinizer-ci.com/g/Kevinrob/guzzle-cache-middleware/?branch=master)
|
||||||
|
|
||||||
|
|
||||||
|
A HTTP Cache for [Guzzle](https://github.com/guzzle/guzzle) 6+. It's a simple Middleware to be added in the HandlerStack.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
- RFC 7234 compliance
|
||||||
|
- Performance and transparency
|
||||||
|
- Assured compatibility with PSR-7
|
||||||
|
|
||||||
|
## Built-in storage interfaces
|
||||||
|
- [Doctrine cache](https://github.com/doctrine/cache)
|
||||||
|
- [Laravel cache](https://laravel.com/docs/5.2/cache)
|
||||||
|
- [Flysystem](https://github.com/thephpleague/flysystem)
|
||||||
|
- [PSR6](https://github.com/php-fig/cache)
|
||||||
|
- [WordPress Object Cache](https://codex.wordpress.org/Class_Reference/WP_Object_Cache)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`composer require kevinrob/guzzle-cache-middleware`
|
||||||
|
|
||||||
|
or add it the your `composer.json` and run `composer update kevinrob/guzzle-cache-middleware`.
|
||||||
|
|
||||||
|
# Why?
|
||||||
|
Performance. It's very common to do some HTTP calls to an API for rendering a page and it takes times to do it.
|
||||||
|
|
||||||
|
# How?
|
||||||
|
With a simple Middleware added at the top of the `HandlerStack` of Guzzle.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\HandlerStack;
|
||||||
|
use Kevinrob\GuzzleCache\CacheMiddleware;
|
||||||
|
|
||||||
|
// Create default HandlerStack
|
||||||
|
$stack = HandlerStack::create();
|
||||||
|
|
||||||
|
// Add this middleware to the top with `push`
|
||||||
|
$stack->push(new CacheMiddleware(), 'cache');
|
||||||
|
|
||||||
|
// Initialize the client with the handler option
|
||||||
|
$client = new Client(['handler' => $stack]);
|
||||||
|
```
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
## Doctrine/Cache
|
||||||
|
You can use a cache from `Doctrine/Cache`:
|
||||||
|
```php
|
||||||
|
[...]
|
||||||
|
use Doctrine\Common\Cache\FilesystemCache;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
$stack->push(
|
||||||
|
new CacheMiddleware(
|
||||||
|
new PrivateCacheStrategy(
|
||||||
|
new DoctrineCacheStorage(
|
||||||
|
new FilesystemCache('/tmp/')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'cache'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use `ChainCache` for using multiple `CacheProvider` instances. With that provider, you have to sort the different caches from the faster to the slower. Like that, you can have a very fast cache.
|
||||||
|
```php
|
||||||
|
[...]
|
||||||
|
use Doctrine\Common\Cache\ChainCache;
|
||||||
|
use Doctrine\Common\Cache\ArrayCache;
|
||||||
|
use Doctrine\Common\Cache\FilesystemCache;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
$stack->push(new CacheMiddleware(
|
||||||
|
new PrivateCacheStrategy(
|
||||||
|
new DoctrineCacheStorage(
|
||||||
|
new ChainCache([
|
||||||
|
new ArrayCache(),
|
||||||
|
new FilesystemCache('/tmp/'),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
), 'cache');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Laravel cache
|
||||||
|
You can use a cache with Laravel, e.g. Redis, Memcache etc.:
|
||||||
|
```php
|
||||||
|
[...]
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\LaravelCacheStorage;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
$stack->push(
|
||||||
|
new CacheMiddleware(
|
||||||
|
new PrivateCacheStrategy(
|
||||||
|
new LaravelCacheStorage(
|
||||||
|
Cache::store('redis')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'cache'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flysystem
|
||||||
|
```php
|
||||||
|
[...]
|
||||||
|
use League\Flysystem\Adapter\Local;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\FlysystemStorage;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
$stack->push(
|
||||||
|
new CacheMiddleware(
|
||||||
|
new PrivateCacheStrategy(
|
||||||
|
new FlysystemStorage(
|
||||||
|
new Local('/path/to/cache')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'cache'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## WordPress Object Cache
|
||||||
|
```php
|
||||||
|
[...]
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\WordPressObjectCacheStorage;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
$stack->push(
|
||||||
|
new CacheMiddleware(
|
||||||
|
new PrivateCacheStrategy(
|
||||||
|
new WordPressObjectCacheStorage()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'cache'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Public and shared
|
||||||
|
It's possible to add a public shared cache to the stack:
|
||||||
|
```php
|
||||||
|
[...]
|
||||||
|
use Doctrine\Common\Cache\FilesystemCache;
|
||||||
|
use Doctrine\Common\Cache\PredisCache;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PublicCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
// Private caching
|
||||||
|
$stack->push(
|
||||||
|
new CacheMiddleware(
|
||||||
|
new PrivateCacheStrategy(
|
||||||
|
new DoctrineCacheStorage(
|
||||||
|
new FilesystemCache('/tmp/')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'private-cache'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Public caching
|
||||||
|
$stack->push(
|
||||||
|
new CacheMiddleware(
|
||||||
|
new PublicCacheStrategy(
|
||||||
|
new DoctrineCacheStorage(
|
||||||
|
new PredisCache(
|
||||||
|
new Predis\Client('tcp://10.0.0.1:6379')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'shared-cache'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Greedy caching
|
||||||
|
In some cases servers might send insufficient or no caching headers at all.
|
||||||
|
Using the greedy caching strategy allows defining an expiry TTL on your own while
|
||||||
|
disregarding any possibly present caching headers:
|
||||||
|
```php
|
||||||
|
[...]
|
||||||
|
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\GreedyCacheStrategy;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||||
|
use Doctrine\Common\Cache\FilesystemCache;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
// Greedy caching
|
||||||
|
$stack->push(
|
||||||
|
new CacheMiddleware(
|
||||||
|
new GreedyCacheStrategy(
|
||||||
|
new DoctrineCacheStorage(
|
||||||
|
new FilesystemCache('/tmp/')
|
||||||
|
),
|
||||||
|
1800, // the TTL in seconds
|
||||||
|
new KeyValueHttpHeader(['Authorization']) // Optional - specify the headers that can change the cache key
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'greedy-cache'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Delegate caching
|
||||||
|
Because your client may call different apps, on different domains, you may need to define which strategy is suitable to your requests.
|
||||||
|
|
||||||
|
To solve this, all you have to do is to define a default cache strategy, and override it by implementing your own Request Matchers.
|
||||||
|
|
||||||
|
Here's an example:
|
||||||
|
```php
|
||||||
|
namespace App\RequestMatcher;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcherInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
class ExampleOrgRequestMatcher implements RequestMatcherInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function matches(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return false !== strpos($request->getUri()->getHost(), 'example.org');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```php
|
||||||
|
namespace App\RequestMatcher;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\Delegate\RequestMatcherInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
class TwitterRequestMatcher implements RequestMatcherInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function matches(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return false !== strpos($request->getUri()->getHost(), 'twitter.com');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```php
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
use App\RequestMatcher\ExampleOrgRequestMatcher;
|
||||||
|
use App\RequestMatcher\TwitterRequestMatcher;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\HandlerStack;
|
||||||
|
use Kevinrob\GuzzleCache\CacheMiddleware;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy;
|
||||||
|
|
||||||
|
$strategy = new Strategy\Delegate\DelegatingCacheStrategy($defaultStrategy = new Strategy\NullCacheStrategy());
|
||||||
|
$strategy->registerRequestMatcher(new ExampleOrgRequestMatcher(), new Strategy\PublicCacheStrategy());
|
||||||
|
$strategy->registerRequestMatcher(new TwitterRequestMatcher(), new Strategy\PrivateCacheStrategy());
|
||||||
|
|
||||||
|
$stack = HandlerStack::create();
|
||||||
|
$stack->push(new CacheMiddleware($strategy));
|
||||||
|
$guzzle = new Client(['handler' => $stack]);
|
||||||
|
```
|
||||||
|
|
||||||
|
With this example:
|
||||||
|
* All requests to `example.org` will be handled by `PublicCacheStrategy`
|
||||||
|
* All requests to `twitter.com` will be handled by `PrivateCacheStrategy`
|
||||||
|
* All other requests won't be cached.
|
||||||
|
|
||||||
|
## Drupal
|
||||||
|
See [Guzzle Cache](https://www.drupal.org/project/guzzle_cache) module.
|
||||||
|
|
||||||
|
# Links that talk about the project
|
||||||
|
- [Speeding Up APIs/Apps/Smart Toasters with HTTP Response Caching](https://apisyouwonthate.com/blog/speeding-up-apis-apps-smart-toasters-with-http-response-caching)
|
||||||
|
- [Caching HTTP-Requests with Guzzle 6 and PSR-6](http://a.kabachnik.info/caching-http-requests-with-guzzle-6-and-psr-6.html)
|
||||||
|
|
||||||
|
# Development
|
||||||
|
|
||||||
|
## Docker quick start
|
||||||
|
|
||||||
|
### Initialization
|
||||||
|
```bash
|
||||||
|
make init
|
||||||
|
```
|
||||||
|
### Running test
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
### Entering container shell
|
||||||
|
```bash
|
||||||
|
make shell
|
||||||
|
```
|
||||||
58
vendor/kevinrob/guzzle-cache-middleware/composer.json
vendored
Normal file
58
vendor/kevinrob/guzzle-cache-middleware/composer.json
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"name": "kevinrob/guzzle-cache-middleware",
|
||||||
|
"type": "library",
|
||||||
|
"description": "A HTTP/1.1 Cache for Guzzle 6. It's a simple Middleware to be added in the HandlerStack. (RFC 7234)",
|
||||||
|
"keywords": ["guzzle", "guzzle6", "cache", "http", "http 1.1", "psr6", "psr7", "handler", "middleware", "cache-control", "rfc7234", "performance", "php", "promise", "expiration", "validation", "Etag", "flysystem", "doctrine"],
|
||||||
|
"homepage": "https://github.com/Kevinrob/guzzle-cache-middleware",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Kevin Robatel",
|
||||||
|
"email": "kevinrob2@gmail.com",
|
||||||
|
"homepage": "https://github.com/Kevinrob"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2.0",
|
||||||
|
"guzzlehttp/guzzle": "^6.0 || ^7.0",
|
||||||
|
"guzzlehttp/promises": "^1.4 || ^2.0",
|
||||||
|
"guzzlehttp/psr7": "^1.7.0 || ^2.0.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^8.5.15 || ^9.5",
|
||||||
|
"doctrine/cache": "^1.10",
|
||||||
|
"league/flysystem": "^2.5",
|
||||||
|
"psr/cache": "^1.0",
|
||||||
|
"cache/array-adapter": "^0.4 || ^0.5 || ^1.0",
|
||||||
|
"illuminate/cache": "^5.0",
|
||||||
|
"cache/simple-cache-bridge": "^0.1 || ^1.0",
|
||||||
|
"symfony/phpunit-bridge": "^4.4 || ^5.0",
|
||||||
|
"symfony/cache": "^4.4 || ^5.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Kevinrob\\GuzzleCache\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Kevinrob\\GuzzleCache\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"guzzlehttp/guzzle": "For using this library. It was created for Guzzle6 (but you can use it with any PSR-7 HTTP client).",
|
||||||
|
"doctrine/cache": "This library has a lot of ready-to-use cache storage (to be used with Kevinrob\\GuzzleCache\\Storage\\DoctrineCacheStorage). Use only versions >=1.4.0 < 2.0.0",
|
||||||
|
"league/flysystem": "To be used with Kevinrob\\GuzzleCache\\Storage\\FlysystemStorage",
|
||||||
|
"psr/cache": "To be used with Kevinrob\\GuzzleCache\\Storage\\Psr6CacheStorage",
|
||||||
|
"psr/simple-cache": "To be used with Kevinrob\\GuzzleCache\\Storage\\Psr16CacheStorage",
|
||||||
|
"laravel/framework": "To be used with Kevinrob\\GuzzleCache\\Storage\\LaravelCacheStorage"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "vendor/bin/phpunit"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"kylekatarnls/update-helper": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
vendor/kevinrob/guzzle-cache-middleware/src/BodyStore.php
vendored
Normal file
46
vendor/kevinrob/guzzle-cache-middleware/src/BodyStore.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This object is only meant to provide a callable to `GuzzleHttp\Psr7\PumpStream`.
|
||||||
|
*
|
||||||
|
* @internal don't use it in your project.
|
||||||
|
*/
|
||||||
|
class BodyStore
|
||||||
|
{
|
||||||
|
private $body;
|
||||||
|
|
||||||
|
private $read = 0;
|
||||||
|
|
||||||
|
private $toRead;
|
||||||
|
|
||||||
|
public function __construct(string $body)
|
||||||
|
{
|
||||||
|
$this->body = $body;
|
||||||
|
$this->toRead = mb_strlen($this->body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $length
|
||||||
|
* @return false|string
|
||||||
|
*/
|
||||||
|
public function __invoke(int $length)
|
||||||
|
{
|
||||||
|
if ($this->toRead <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$length = min($length, $this->toRead);
|
||||||
|
|
||||||
|
$body = mb_substr(
|
||||||
|
$this->body,
|
||||||
|
$this->read,
|
||||||
|
$length
|
||||||
|
);
|
||||||
|
$this->toRead -= $length;
|
||||||
|
$this->read += $length;
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
}
|
||||||
333
vendor/kevinrob/guzzle-cache-middleware/src/CacheEntry.php
vendored
Normal file
333
vendor/kevinrob/guzzle-cache-middleware/src/CacheEntry.php
vendored
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\PumpStream;
|
||||||
|
use Psr\Http\Message\MessageInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class CacheEntry implements \Serializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var RequestInterface
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ResponseInterface
|
||||||
|
*/
|
||||||
|
protected $response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
protected $staleAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
protected $staleIfErrorTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
protected $staleWhileRevalidateTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
protected $dateCreated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached timestamp of staleAt variable.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $timestampStale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @param \DateTime $staleAt
|
||||||
|
* @param \DateTime|null $staleIfErrorTo if null, detected with the headers (RFC 5861)
|
||||||
|
* @param \DateTime|null $staleWhileRevalidateTo
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
RequestInterface $request,
|
||||||
|
ResponseInterface $response,
|
||||||
|
\DateTime $staleAt,
|
||||||
|
\DateTime $staleIfErrorTo = null,
|
||||||
|
\DateTime $staleWhileRevalidateTo = null
|
||||||
|
) {
|
||||||
|
$this->dateCreated = new \DateTime();
|
||||||
|
|
||||||
|
$this->request = $request;
|
||||||
|
$this->response = $response;
|
||||||
|
$this->staleAt = $staleAt;
|
||||||
|
|
||||||
|
$values = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
|
||||||
|
|
||||||
|
if ($staleIfErrorTo === null && $values->has('stale-if-error')) {
|
||||||
|
$this->staleIfErrorTo = (new \DateTime(
|
||||||
|
'@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-if-error'))
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$this->staleIfErrorTo = $staleIfErrorTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($staleWhileRevalidateTo === null && $values->has('stale-while-revalidate')) {
|
||||||
|
$this->staleWhileRevalidateTo = new \DateTime(
|
||||||
|
'@'.($this->staleAt->getTimestamp() + (int) $values->get('stale-while-revalidate'))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->staleWhileRevalidateTo = $staleWhileRevalidateTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function getResponse()
|
||||||
|
{
|
||||||
|
return $this->response
|
||||||
|
->withHeader('Age', $this->getAge());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function getOriginalResponse()
|
||||||
|
{
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return RequestInterface
|
||||||
|
*/
|
||||||
|
public function getOriginalRequest()
|
||||||
|
{
|
||||||
|
return $this->request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isVaryEquals(RequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($this->response->hasHeader('Vary')) {
|
||||||
|
if ($this->request === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->getVaryHeaders() as $key => $value) {
|
||||||
|
if (!$this->request->hasHeader($key)
|
||||||
|
&& !$request->hasHeader($key)
|
||||||
|
) {
|
||||||
|
// Absent from both
|
||||||
|
continue;
|
||||||
|
} elseif ($this->request->getHeaderLine($key)
|
||||||
|
== $request->getHeaderLine($key)
|
||||||
|
) {
|
||||||
|
// Same content
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the vary headers that should be honoured by the cache.
|
||||||
|
*
|
||||||
|
* @return KeyValueHttpHeader
|
||||||
|
*/
|
||||||
|
public function getVaryHeaders()
|
||||||
|
{
|
||||||
|
return new KeyValueHttpHeader($this->response->getHeader('Vary'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getStaleAt()
|
||||||
|
{
|
||||||
|
return $this->staleAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isFresh()
|
||||||
|
{
|
||||||
|
return !$this->isStale();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isStale()
|
||||||
|
{
|
||||||
|
return $this->getStaleAge() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int positive value equal staled
|
||||||
|
*/
|
||||||
|
public function getStaleAge()
|
||||||
|
{
|
||||||
|
// This object is immutable
|
||||||
|
if ($this->timestampStale === null) {
|
||||||
|
$this->timestampStale = $this->staleAt->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
return time() - $this->timestampStale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function serveStaleIfError()
|
||||||
|
{
|
||||||
|
return $this->staleIfErrorTo !== null
|
||||||
|
&& $this->staleIfErrorTo->getTimestamp() >= (new \DateTime())->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function staleWhileValidate()
|
||||||
|
{
|
||||||
|
return $this->staleWhileRevalidateTo !== null
|
||||||
|
&& $this->staleWhileRevalidateTo->getTimestamp() >= (new \DateTime())->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasValidationInformation()
|
||||||
|
{
|
||||||
|
return $this->response->hasHeader('Etag') || $this->response->hasHeader('Last-Modified');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time in seconds how long the entry should be kept in the cache
|
||||||
|
*
|
||||||
|
* This will not give the time (in seconds) that the response will still be fresh for
|
||||||
|
* from the HTTP point of view, but an upper bound on how long it is necessary and
|
||||||
|
* reasonable to keep the response in a cache (to re-use it or re-validate it later on).
|
||||||
|
*
|
||||||
|
* @return int TTL in seconds (0 = infinite)
|
||||||
|
*/
|
||||||
|
public function getTTL()
|
||||||
|
{
|
||||||
|
if ($this->hasValidationInformation()) {
|
||||||
|
// No TTL if we have a way to re-validate the cache
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ttl = 0;
|
||||||
|
|
||||||
|
// Keep it when stale if error
|
||||||
|
if ($this->staleIfErrorTo !== null) {
|
||||||
|
$ttl = max($ttl, $this->staleIfErrorTo->getTimestamp() - time());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep it when stale-while-revalidate
|
||||||
|
if ($this->staleWhileRevalidateTo !== null) {
|
||||||
|
$ttl = max($ttl, $this->staleWhileRevalidateTo->getTimestamp() - time());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep it until it become stale
|
||||||
|
$ttl = max($ttl, $this->staleAt->getTimestamp() - time());
|
||||||
|
|
||||||
|
// Don't return 0, it's reserved for infinite TTL
|
||||||
|
return $ttl !== 0 ? (int) $ttl : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int Age in seconds
|
||||||
|
*/
|
||||||
|
public function getAge()
|
||||||
|
{
|
||||||
|
return time() - $this->dateCreated->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __serialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'request' => self::toSerializeableMessage($this->request),
|
||||||
|
'response' => $this->response !== null ? self::toSerializeableMessage($this->response) : null,
|
||||||
|
'staleAt' => $this->staleAt,
|
||||||
|
'staleIfErrorTo' => $this->staleIfErrorTo,
|
||||||
|
'staleWhileRevalidateTo' => $this->staleWhileRevalidateTo,
|
||||||
|
'dateCreated' => $this->dateCreated,
|
||||||
|
'timestampStale' => $this->timestampStale,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unserialize(array $data): void
|
||||||
|
{
|
||||||
|
$prefix = '';
|
||||||
|
if (isset($data["\0*\0request"])) {
|
||||||
|
// We are unserializing a cache entry which was serialized with a version < 4.1.1
|
||||||
|
$prefix = "\0*\0";
|
||||||
|
}
|
||||||
|
$this->request = self::restoreStreamBody($data[$prefix.'request']);
|
||||||
|
$this->response = $data[$prefix.'response'] !== null ? self::restoreStreamBody($data[$prefix.'response']) : null;
|
||||||
|
$this->staleAt = $data[$prefix.'staleAt'];
|
||||||
|
$this->staleIfErrorTo = $data[$prefix.'staleIfErrorTo'];
|
||||||
|
$this->staleWhileRevalidateTo = $data[$prefix.'staleWhileRevalidateTo'];
|
||||||
|
$this->dateCreated = $data[$prefix.'dateCreated'];
|
||||||
|
$this->timestampStale = $data[$prefix.'timestampStale'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream/Resource can't be serialized... So we copy the content into an implementation of `Psr\Http\Message\StreamInterface`
|
||||||
|
*
|
||||||
|
* @template T of MessageInterface
|
||||||
|
*
|
||||||
|
* @param T $message
|
||||||
|
* @return T
|
||||||
|
*/
|
||||||
|
private static function toSerializeableMessage(MessageInterface $message): MessageInterface
|
||||||
|
{
|
||||||
|
$bodyString = (string)$message->getBody();
|
||||||
|
|
||||||
|
return $message->withBody(
|
||||||
|
new PumpStream(
|
||||||
|
new BodyStore($bodyString),
|
||||||
|
[
|
||||||
|
'size' => mb_strlen($bodyString),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T of MessageInterface
|
||||||
|
*
|
||||||
|
* @param T $message
|
||||||
|
* @return T
|
||||||
|
*/
|
||||||
|
private static function restoreStreamBody(MessageInterface $message): MessageInterface
|
||||||
|
{
|
||||||
|
return $message->withBody(
|
||||||
|
\GuzzleHttp\Psr7\Utils::streamFor((string) $message->getBody())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serialize()
|
||||||
|
{
|
||||||
|
return serialize($this->__serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unserialize($data)
|
||||||
|
{
|
||||||
|
$this->__unserialize(unserialize($data));
|
||||||
|
}
|
||||||
|
}
|
||||||
400
vendor/kevinrob/guzzle-cache-middleware/src/CacheMiddleware.php
vendored
Normal file
400
vendor/kevinrob/guzzle-cache-middleware/src/CacheMiddleware.php
vendored
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\TransferException;
|
||||||
|
use GuzzleHttp\Promise\FulfilledPromise;
|
||||||
|
use GuzzleHttp\Promise\Promise;
|
||||||
|
use GuzzleHttp\Promise\RejectedPromise;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\CacheStrategyInterface;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CacheMiddleware.
|
||||||
|
*/
|
||||||
|
class CacheMiddleware
|
||||||
|
{
|
||||||
|
const HEADER_RE_VALIDATION = 'X-Kevinrob-GuzzleCache-ReValidation';
|
||||||
|
const HEADER_INVALIDATION = 'X-Kevinrob-GuzzleCache-Invalidation';
|
||||||
|
const HEADER_CACHE_INFO = 'X-Kevinrob-Cache';
|
||||||
|
const HEADER_CACHE_HIT = 'HIT';
|
||||||
|
const HEADER_CACHE_MISS = 'MISS';
|
||||||
|
const HEADER_CACHE_STALE = 'STALE';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array of Promise
|
||||||
|
*/
|
||||||
|
protected $waitingRevalidate = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheStrategyInterface
|
||||||
|
*/
|
||||||
|
protected $cacheStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of allowed HTTP methods to cache
|
||||||
|
* Key = method name (upscaling)
|
||||||
|
* Value = true.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $httpMethods = ['GET' => true];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of safe methods
|
||||||
|
*
|
||||||
|
* https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $safeMethods = ['GET' => true, 'HEAD' => true, 'OPTIONS' => true, 'TRACE' => true];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheStrategyInterface|null $cacheStrategy
|
||||||
|
*/
|
||||||
|
public function __construct(CacheStrategyInterface $cacheStrategy = null)
|
||||||
|
{
|
||||||
|
$this->cacheStorage = $cacheStrategy !== null ? $cacheStrategy : new PrivateCacheStrategy();
|
||||||
|
|
||||||
|
register_shutdown_function([$this, 'purgeReValidation']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Client $client
|
||||||
|
*/
|
||||||
|
public function setClient(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheStrategyInterface $cacheStorage
|
||||||
|
*/
|
||||||
|
public function setCacheStorage(CacheStrategyInterface $cacheStorage)
|
||||||
|
{
|
||||||
|
$this->cacheStorage = $cacheStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CacheStrategyInterface
|
||||||
|
*/
|
||||||
|
public function getCacheStorage()
|
||||||
|
{
|
||||||
|
return $this->cacheStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $methods
|
||||||
|
*/
|
||||||
|
public function setHttpMethods(array $methods)
|
||||||
|
{
|
||||||
|
$this->httpMethods = $methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHttpMethods()
|
||||||
|
{
|
||||||
|
return $this->httpMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will be called at the end of the script.
|
||||||
|
*/
|
||||||
|
public function purgeReValidation()
|
||||||
|
{
|
||||||
|
\GuzzleHttp\Promise\Utils::inspectAll($this->waitingRevalidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $handler
|
||||||
|
*
|
||||||
|
* @return callable
|
||||||
|
*/
|
||||||
|
public function __invoke(callable $handler)
|
||||||
|
{
|
||||||
|
return function (RequestInterface $request, array $options) use (&$handler) {
|
||||||
|
if (!isset($this->httpMethods[strtoupper($request->getMethod())])) {
|
||||||
|
// No caching for this method allowed
|
||||||
|
|
||||||
|
return $handler($request, $options)->then(
|
||||||
|
function (ResponseInterface $response) use ($request) {
|
||||||
|
if (!isset($this->safeMethods[$request->getMethod()])) {
|
||||||
|
// Invalidate cache after a call of non-safe method on the same URI
|
||||||
|
$response = $this->invalidateCache($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_MISS);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->hasHeader(self::HEADER_RE_VALIDATION)) {
|
||||||
|
// It's a re-validation request, so bypass the cache!
|
||||||
|
return $handler($request->withoutHeader(self::HEADER_RE_VALIDATION), $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve information from request (Cache-Control)
|
||||||
|
$reqCacheControl = new KeyValueHttpHeader($request->getHeader('Cache-Control'));
|
||||||
|
$onlyFromCache = $reqCacheControl->has('only-if-cached');
|
||||||
|
$staleResponse = $reqCacheControl->has('max-stale')
|
||||||
|
&& $reqCacheControl->get('max-stale') === '';
|
||||||
|
$maxStaleCache = $reqCacheControl->get('max-stale', null);
|
||||||
|
$minFreshCache = $reqCacheControl->get('min-fresh', null);
|
||||||
|
|
||||||
|
// If cache => return new FulfilledPromise(...) with response
|
||||||
|
$cacheEntry = $this->cacheStorage->fetch($request);
|
||||||
|
if ($cacheEntry instanceof CacheEntry) {
|
||||||
|
$body = $cacheEntry->getResponse()->getBody();
|
||||||
|
if ($body->tell() > 0) {
|
||||||
|
$body->rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cacheEntry->isFresh()
|
||||||
|
&& ($minFreshCache === null || $cacheEntry->getStaleAge() + (int)$minFreshCache <= 0)
|
||||||
|
) {
|
||||||
|
// Cache HIT!
|
||||||
|
return new FulfilledPromise(
|
||||||
|
$cacheEntry->getResponse()->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_HIT)
|
||||||
|
);
|
||||||
|
} elseif ($staleResponse
|
||||||
|
|| ($maxStaleCache !== null && $cacheEntry->getStaleAge() <= $maxStaleCache)
|
||||||
|
) {
|
||||||
|
// Staled cache!
|
||||||
|
return new FulfilledPromise(
|
||||||
|
$cacheEntry->getResponse()->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_HIT)
|
||||||
|
);
|
||||||
|
} elseif ($cacheEntry->hasValidationInformation() && !$onlyFromCache) {
|
||||||
|
// Re-validation header
|
||||||
|
$request = static::getRequestWithReValidationHeader($request, $cacheEntry);
|
||||||
|
|
||||||
|
if ($cacheEntry->staleWhileValidate()) {
|
||||||
|
static::addReValidationRequest($request, $this->cacheStorage, $cacheEntry);
|
||||||
|
|
||||||
|
return new FulfilledPromise(
|
||||||
|
$cacheEntry->getResponse()
|
||||||
|
->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_STALE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$cacheEntry = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cacheEntry === null && $onlyFromCache) {
|
||||||
|
// Explicit asking of a cached response => 504
|
||||||
|
return new FulfilledPromise(
|
||||||
|
new Response(504)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Promise $promise */
|
||||||
|
$promise = $handler($request, $options);
|
||||||
|
|
||||||
|
return $promise->then(
|
||||||
|
function (ResponseInterface $response) use ($request, $cacheEntry) {
|
||||||
|
// Check if error and looking for a staled content
|
||||||
|
if ($response->getStatusCode() >= 500) {
|
||||||
|
$responseStale = static::getStaleResponse($cacheEntry);
|
||||||
|
if ($responseStale instanceof ResponseInterface) {
|
||||||
|
return $responseStale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$update = false;
|
||||||
|
|
||||||
|
if ($response->getStatusCode() == 304 && $cacheEntry instanceof CacheEntry) {
|
||||||
|
// Not modified => cache entry is re-validate
|
||||||
|
/** @var ResponseInterface $response */
|
||||||
|
$response = $response
|
||||||
|
->withStatus($cacheEntry->getResponse()->getStatusCode())
|
||||||
|
->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_HIT);
|
||||||
|
$response = $response->withBody($cacheEntry->getResponse()->getBody());
|
||||||
|
|
||||||
|
// Merge headers of the "304 Not Modified" and the cache entry
|
||||||
|
/**
|
||||||
|
* @var string $headerName
|
||||||
|
* @var string[] $headerValue
|
||||||
|
*/
|
||||||
|
foreach ($cacheEntry->getOriginalResponse()->getHeaders() as $headerName => $headerValue) {
|
||||||
|
if (!$response->hasHeader($headerName) && $headerName !== self::HEADER_CACHE_INFO) {
|
||||||
|
$response = $response->withHeader($headerName, $headerValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$update = true;
|
||||||
|
} else {
|
||||||
|
$response = $response->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_MISS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::addToCache($this->cacheStorage, $request, $response, $update);
|
||||||
|
},
|
||||||
|
function ($reason) use ($cacheEntry) {
|
||||||
|
$response = static::getStaleResponse($cacheEntry);
|
||||||
|
if ($response instanceof ResponseInterface) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RejectedPromise($reason);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheStrategyInterface $cache
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @param bool $update cache
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
protected static function addToCache(
|
||||||
|
CacheStrategyInterface $cache,
|
||||||
|
RequestInterface $request,
|
||||||
|
ResponseInterface $response,
|
||||||
|
$update = false
|
||||||
|
) {
|
||||||
|
$body = $response->getBody();
|
||||||
|
|
||||||
|
// If the body is not seekable, we have to replace it by a seekable one
|
||||||
|
if (!$body->isSeekable()) {
|
||||||
|
$response = $response->withBody(
|
||||||
|
\GuzzleHttp\Psr7\Utils::streamFor($body->getContents())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($update) {
|
||||||
|
$cache->update($request, $response);
|
||||||
|
} else {
|
||||||
|
$cache->cache($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// always rewind back to the start otherwise other middlewares may get empty "content"
|
||||||
|
if ($body->isSeekable()) {
|
||||||
|
$response->getBody()->rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param CacheStrategyInterface $cacheStorage
|
||||||
|
* @param CacheEntry $cacheEntry
|
||||||
|
*
|
||||||
|
* @return bool if added
|
||||||
|
*/
|
||||||
|
protected function addReValidationRequest(
|
||||||
|
RequestInterface $request,
|
||||||
|
CacheStrategyInterface &$cacheStorage,
|
||||||
|
CacheEntry $cacheEntry
|
||||||
|
) {
|
||||||
|
// Add the promise for revalidate
|
||||||
|
if ($this->client !== null) {
|
||||||
|
/** @var RequestInterface $request */
|
||||||
|
$request = $request->withHeader(self::HEADER_RE_VALIDATION, '1');
|
||||||
|
$this->waitingRevalidate[] = $this->client
|
||||||
|
->sendAsync($request)
|
||||||
|
->then(function (ResponseInterface $response) use ($request, &$cacheStorage, $cacheEntry) {
|
||||||
|
$update = false;
|
||||||
|
|
||||||
|
if ($response->getStatusCode() == 304) {
|
||||||
|
// Not modified => cache entry is re-validate
|
||||||
|
/** @var ResponseInterface $response */
|
||||||
|
$response = $response->withStatus($cacheEntry->getResponse()->getStatusCode());
|
||||||
|
$response = $response->withBody($cacheEntry->getResponse()->getBody());
|
||||||
|
|
||||||
|
// Merge headers of the "304 Not Modified" and the cache entry
|
||||||
|
foreach ($cacheEntry->getResponse()->getHeaders() as $headerName => $headerValue) {
|
||||||
|
if (!$response->hasHeader($headerName)) {
|
||||||
|
$response = $response->withHeader($headerName, $headerValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static::addToCache($cacheStorage, $request, $response, $update);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheEntry|null $cacheEntry
|
||||||
|
*
|
||||||
|
* @return null|ResponseInterface
|
||||||
|
*/
|
||||||
|
protected static function getStaleResponse(CacheEntry $cacheEntry = null)
|
||||||
|
{
|
||||||
|
// Return staled cache entry if we can
|
||||||
|
if ($cacheEntry instanceof CacheEntry && $cacheEntry->serveStaleIfError()) {
|
||||||
|
return $cacheEntry->getResponse()
|
||||||
|
->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_STALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param CacheEntry $cacheEntry
|
||||||
|
*
|
||||||
|
* @return RequestInterface
|
||||||
|
*/
|
||||||
|
protected static function getRequestWithReValidationHeader(RequestInterface $request, CacheEntry $cacheEntry)
|
||||||
|
{
|
||||||
|
if ($cacheEntry->getResponse()->hasHeader('Last-Modified')) {
|
||||||
|
$request = $request->withHeader(
|
||||||
|
'If-Modified-Since',
|
||||||
|
$cacheEntry->getResponse()->getHeader('Last-Modified')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($cacheEntry->getResponse()->hasHeader('Etag')) {
|
||||||
|
$request = $request->withHeader(
|
||||||
|
'If-None-Match',
|
||||||
|
$cacheEntry->getResponse()->getHeader('Etag')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheStrategyInterface|null $cacheStorage
|
||||||
|
*
|
||||||
|
* @return CacheMiddleware the Middleware for Guzzle HandlerStack
|
||||||
|
*
|
||||||
|
* @deprecated Use constructor => `new CacheMiddleware()`
|
||||||
|
*/
|
||||||
|
public static function getMiddleware(CacheStrategyInterface $cacheStorage = null)
|
||||||
|
{
|
||||||
|
return new self($cacheStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
*
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
private function invalidateCache(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
foreach (array_keys($this->httpMethods) as $method) {
|
||||||
|
$this->cacheStorage->delete($request->withMethod($method));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response->withHeader(self::HEADER_INVALIDATION, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
131
vendor/kevinrob/guzzle-cache-middleware/src/KeyValueHttpHeader.php
vendored
Normal file
131
vendor/kevinrob/guzzle-cache-middleware/src/KeyValueHttpHeader.php
vendored
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache;
|
||||||
|
|
||||||
|
class KeyValueHttpHeader implements \Iterator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Take from https://github.com/hapijs/wreck.
|
||||||
|
*/
|
||||||
|
const REGEX_SPLIT = '/(?:^|(?:\s*\,\s*))([^\x00-\x20\(\)<>@\,;\:\\\\"\/\[\]\?\=\{\}\x7F]+)(?:\=(?:([^\x00-\x20\(\)<>@\,;\:\\\\"\/\[\]\?\=\{\}\x7F]+)|(?:\"((?:[^"\\\\]|\\\\.)*)\")))?/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $values = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $values
|
||||||
|
*/
|
||||||
|
public function __construct(array $values)
|
||||||
|
{
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$matches = [];
|
||||||
|
if (preg_match_all(self::REGEX_SPLIT, $value, $matches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
$val = '';
|
||||||
|
if (count($match) == 3) {
|
||||||
|
$val = $match[2];
|
||||||
|
} elseif (count($match) > 3) {
|
||||||
|
$val = $match[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->values[$match[1]] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has($key)
|
||||||
|
{
|
||||||
|
// For performance, we can use isset,
|
||||||
|
// but it will not match if value == 0
|
||||||
|
return isset($this->values[$key]) || array_key_exists($key, $this->values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param string $default the value to return if don't exist
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get($key, $default = '')
|
||||||
|
{
|
||||||
|
if ($this->has($key)) {
|
||||||
|
return $this->values[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEmpty()
|
||||||
|
{
|
||||||
|
return count($this->values) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current element
|
||||||
|
* @link http://php.net/manual/en/iterator.current.php
|
||||||
|
* @return mixed Can return any type.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return current($this->values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move forward to next element
|
||||||
|
* @link http://php.net/manual/en/iterator.next.php
|
||||||
|
* @return void Any returned value is ignored.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function next(): void
|
||||||
|
{
|
||||||
|
next($this->values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key of the current element
|
||||||
|
* @link http://php.net/manual/en/iterator.key.php
|
||||||
|
* @return mixed scalar on success, or null on failure.
|
||||||
|
* @since 5.0.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
return key($this->values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if current position is valid
|
||||||
|
* @link http://php.net/manual/en/iterator.valid.php
|
||||||
|
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||||
|
* Returns true on success or false on failure.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function valid(): bool
|
||||||
|
{
|
||||||
|
return key($this->values) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewind the Iterator to the first element
|
||||||
|
* @link http://php.net/manual/en/iterator.rewind.php
|
||||||
|
* @return void Any returned value is ignored.
|
||||||
|
* @since 5.0.0
|
||||||
|
*/
|
||||||
|
public function rewind(): void
|
||||||
|
{
|
||||||
|
reset($this->values);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CacheStorageInterface.php
vendored
Normal file
30
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CacheStorageInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
|
||||||
|
interface CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return CacheEntry|null the data or false
|
||||||
|
*/
|
||||||
|
public function fetch($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param CacheEntry $data
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function delete($key);
|
||||||
|
}
|
||||||
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CompressedDoctrineCacheStorage.php
vendored
Normal file
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/CompressedDoctrineCacheStorage.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Doctrine\Common\Cache\Cache;
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
|
||||||
|
class CompressedDoctrineCacheStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Cache
|
||||||
|
*/
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Cache $cache
|
||||||
|
*/
|
||||||
|
public function __construct(Cache $cache)
|
||||||
|
{
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$cache = unserialize(gzuncompress($this->cache->fetch($key)));
|
||||||
|
if ($cache instanceof CacheEntry) {
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$lifeTime = $data->getTTL();
|
||||||
|
if ($lifeTime >= 0) {
|
||||||
|
return $this->cache->save(
|
||||||
|
$key,
|
||||||
|
gzcompress(serialize($data)),
|
||||||
|
$lifeTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// No fail if we can't save it the storage
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->cache->delete($key);
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// Don't fail if we can't delete it
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/DoctrineCacheStorage.php
vendored
Normal file
74
vendor/kevinrob/guzzle-cache-middleware/src/Storage/DoctrineCacheStorage.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Doctrine\Common\Cache\Cache;
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
|
||||||
|
class DoctrineCacheStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Cache
|
||||||
|
*/
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Cache $cache
|
||||||
|
*/
|
||||||
|
public function __construct(Cache $cache)
|
||||||
|
{
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$cache = unserialize($this->cache->fetch($key));
|
||||||
|
if ($cache instanceof CacheEntry) {
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$lifeTime = $data->getTTL();
|
||||||
|
if ($lifeTime >= 0) {
|
||||||
|
return $this->cache->save(
|
||||||
|
$key,
|
||||||
|
serialize($data),
|
||||||
|
$lifeTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// No fail if we can't save it the storage
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->cache->delete($key);
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// Don't fail if we can't delete it
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
61
vendor/kevinrob/guzzle-cache-middleware/src/Storage/FlysystemStorage.php
vendored
Normal file
61
vendor/kevinrob/guzzle-cache-middleware/src/Storage/FlysystemStorage.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
use League\Flysystem\Filesystem;
|
||||||
|
use League\Flysystem\FilesystemAdapter;
|
||||||
|
use League\Flysystem\FilesystemException;
|
||||||
|
|
||||||
|
class FlysystemStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Filesystem
|
||||||
|
*/
|
||||||
|
protected $filesystem;
|
||||||
|
|
||||||
|
public function __construct(FilesystemAdapter $adapter)
|
||||||
|
{
|
||||||
|
$this->filesystem = new Filesystem($adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
if ($this->filesystem->fileExists($key)) {
|
||||||
|
// The file exist, read it!
|
||||||
|
$data = @unserialize(
|
||||||
|
$this->filesystem->read($key)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($data instanceof CacheEntry) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
$this->filesystem->write($key, serialize($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->filesystem->delete($key);
|
||||||
|
} catch (FilesystemException $ex) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
vendor/kevinrob/guzzle-cache-middleware/src/Storage/LaravelCacheStorage.php
vendored
Normal file
84
vendor/kevinrob/guzzle-cache-middleware/src/Storage/LaravelCacheStorage.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Cache\Repository as Cache;
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
|
||||||
|
class LaravelCacheStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Cache
|
||||||
|
*/
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Cache $cache
|
||||||
|
*/
|
||||||
|
public function __construct(Cache $cache)
|
||||||
|
{
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$cache = unserialize($this->cache->get($key, ''));
|
||||||
|
if ($cache instanceof CacheEntry) {
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$lifeTime = $this->getLifeTime($data);
|
||||||
|
if ($lifeTime === 0) {
|
||||||
|
return $this->cache->forever(
|
||||||
|
$key,
|
||||||
|
serialize($data)
|
||||||
|
);
|
||||||
|
} else if ($lifeTime > 0) {
|
||||||
|
return $this->cache->add(
|
||||||
|
$key,
|
||||||
|
serialize($data),
|
||||||
|
$lifeTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// No fail if we can't save it the storage
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
return $this->cache->forget($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getLifeTime(CacheEntry $data)
|
||||||
|
{
|
||||||
|
$version = app()->version();
|
||||||
|
if (preg_match('/^\d+(\.\d+)?(\.\d+)?/', $version) && version_compare($version, '5.8.0') < 0) {
|
||||||
|
// getTTL returns seconds, Laravel needs minutes before v5.8
|
||||||
|
return $data->getTTL() / 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data->getTTL();
|
||||||
|
}
|
||||||
|
}
|
||||||
52
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr16CacheStorage.php
vendored
Normal file
52
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr16CacheStorage.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
|
||||||
|
class Psr16CacheStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CacheInterface
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
public function __construct(CacheInterface $cache)
|
||||||
|
{
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
$data = $this->cache->get($key);
|
||||||
|
if ($data instanceof CacheEntry) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
$ttl = $data->getTTL();
|
||||||
|
if ($ttl === 0) {
|
||||||
|
return $this->cache->set($key, $data);
|
||||||
|
}
|
||||||
|
return $this->cache->set($key, $data, $data->getTTL());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
return $this->cache->delete($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
92
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr6CacheStorage.php
vendored
Normal file
92
vendor/kevinrob/guzzle-cache-middleware/src/Storage/Psr6CacheStorage.php
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
|
||||||
|
class Psr6CacheStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The cache pool.
|
||||||
|
*
|
||||||
|
* @var CacheItemPoolInterface
|
||||||
|
*/
|
||||||
|
protected $cachePool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last item retrieved from the cache.
|
||||||
|
*
|
||||||
|
* This item is transiently stored so that save() can reuse the cache item
|
||||||
|
* usually retrieved by fetch() beforehand, instead of requesting it a second time.
|
||||||
|
*
|
||||||
|
* @var CacheItemInterface|null
|
||||||
|
*/
|
||||||
|
protected $lastItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheItemPoolInterface $cachePool
|
||||||
|
*/
|
||||||
|
public function __construct(CacheItemPoolInterface $cachePool)
|
||||||
|
{
|
||||||
|
$this->cachePool = $cachePool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
$item = $this->cachePool->getItem($key);
|
||||||
|
$this->lastItem = $item;
|
||||||
|
|
||||||
|
$cache = $item->get();
|
||||||
|
|
||||||
|
if ($cache instanceof CacheEntry) {
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
if ($this->lastItem && $this->lastItem->getKey() == $key) {
|
||||||
|
$item = $this->lastItem;
|
||||||
|
} else {
|
||||||
|
$item = $this->cachePool->getItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->lastItem = null;
|
||||||
|
|
||||||
|
$item->set($data);
|
||||||
|
|
||||||
|
$ttl = $data->getTTL();
|
||||||
|
if ($ttl === 0) {
|
||||||
|
// No expiration
|
||||||
|
$item->expiresAfter(null);
|
||||||
|
} else {
|
||||||
|
$item->expiresAfter($ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cachePool->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
if (null !== $this->lastItem && $this->lastItem->getKey() === $key) {
|
||||||
|
$this->lastItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cachePool->deleteItem($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
vendor/kevinrob/guzzle-cache-middleware/src/Storage/VolatileRuntimeStorage.php
vendored
Normal file
60
vendor/kevinrob/guzzle-cache-middleware/src/Storage/VolatileRuntimeStorage.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This cache class is backed by a PHP Array.
|
||||||
|
*/
|
||||||
|
class VolatileRuntimeStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheEntry[]
|
||||||
|
*/
|
||||||
|
protected $cache = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return CacheEntry|null the data or false
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
if (isset($this->cache[$key])) {
|
||||||
|
return $this->cache[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param CacheEntry $data
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
$this->cache[$key] = $data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
if (true === array_key_exists($key, $this->cache)) {
|
||||||
|
unset($this->cache[$key]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
vendor/kevinrob/guzzle-cache-middleware/src/Storage/WordPressObjectCacheStorage.php
vendored
Normal file
73
vendor/kevinrob/guzzle-cache-middleware/src/Storage/WordPressObjectCacheStorage.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Storage;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
|
||||||
|
class WordPressObjectCacheStorage implements CacheStorageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $group;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $group
|
||||||
|
*/
|
||||||
|
public function __construct($group = 'guzzle')
|
||||||
|
{
|
||||||
|
$this->group = $group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return CacheEntry|null the data or false
|
||||||
|
*/
|
||||||
|
public function fetch($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$cache = unserialize(wp_cache_get($key, $this->group));
|
||||||
|
if ($cache instanceof CacheEntry) {
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// Don't fail if we can't load it
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param CacheEntry $data
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save($key, CacheEntry $data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return wp_cache_set($key, serialize($data), $this->group, $data->getTTL());
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// Don't fail if we can't save it
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function delete($key)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return wp_cache_delete($key, $this->group);
|
||||||
|
} catch (\Exception $ignored) {
|
||||||
|
// Don't fail if we can't delete it
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/CacheStrategyInterface.php
vendored
Normal file
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/CacheStrategyInterface.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Strategy;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
interface CacheStrategyInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return a CacheEntry or null if no cache.
|
||||||
|
*
|
||||||
|
* @param RequestInterface $request
|
||||||
|
*
|
||||||
|
* @return CacheEntry|null
|
||||||
|
*/
|
||||||
|
public function fetch(RequestInterface $request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return bool true if success
|
||||||
|
*/
|
||||||
|
public function cache(RequestInterface $request, ResponseInterface $response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return bool true if success
|
||||||
|
*/
|
||||||
|
public function update(RequestInterface $request, ResponseInterface $response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function delete(RequestInterface $request);
|
||||||
|
}
|
||||||
101
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/DelegatingCacheStrategy.php
vendored
Normal file
101
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/DelegatingCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Strategy\Delegate;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\CacheStrategyInterface;
|
||||||
|
use Kevinrob\GuzzleCache\Strategy\NullCacheStrategy;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class DelegatingCacheStrategy implements CacheStrategyInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $requestMatchers = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheStrategyInterface
|
||||||
|
*/
|
||||||
|
private $defaultCacheStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DelegatingCacheStrategy constructor.
|
||||||
|
*/
|
||||||
|
public function __construct(CacheStrategyInterface $defaultCacheStrategy = null)
|
||||||
|
{
|
||||||
|
$this->defaultCacheStrategy = $defaultCacheStrategy ?: new NullCacheStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheStrategyInterface $defaultCacheStrategy
|
||||||
|
*/
|
||||||
|
public function setDefaultCacheStrategy(CacheStrategyInterface $defaultCacheStrategy)
|
||||||
|
{
|
||||||
|
$this->defaultCacheStrategy = $defaultCacheStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestMatcherInterface $requestMatcher
|
||||||
|
* @param CacheStrategyInterface $cacheStrategy
|
||||||
|
*/
|
||||||
|
final public function registerRequestMatcher(RequestMatcherInterface $requestMatcher, CacheStrategyInterface $cacheStrategy)
|
||||||
|
{
|
||||||
|
$this->requestMatchers[] = [
|
||||||
|
$requestMatcher,
|
||||||
|
$cacheStrategy,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @return CacheStrategyInterface
|
||||||
|
*/
|
||||||
|
private function getStrategyFor(RequestInterface $request)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var RequestMatcherInterface $requestMatcher
|
||||||
|
* @var CacheStrategyInterface $cacheStrategy
|
||||||
|
*/
|
||||||
|
foreach ($this->requestMatchers as $requestMatcher) {
|
||||||
|
list($requestMatcher, $cacheStrategy) = $requestMatcher;
|
||||||
|
if ($requestMatcher->matches($request)) {
|
||||||
|
return $cacheStrategy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->defaultCacheStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function fetch(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return $this->getStrategyFor($request)->fetch($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return $this->getStrategyFor($request)->cache($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function update(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return $this->getStrategyFor($request)->update($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return $this->getStrategyFor($request)->delete($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/RequestMatcherInterface.php
vendored
Normal file
15
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/Delegate/RequestMatcherInterface.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Strategy\Delegate;
|
||||||
|
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
interface RequestMatcherInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function matches(RequestInterface $request);
|
||||||
|
}
|
||||||
119
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/GreedyCacheStrategy.php
vendored
Normal file
119
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/GreedyCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Strategy;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\CacheStorageInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This strategy represents a "greedy" HTTP client.
|
||||||
|
*
|
||||||
|
* It can be used to cache responses in spite of any cache related response headers,
|
||||||
|
* but it SHOULDN'T be used unless absolutely necessary, e.g. when accessing
|
||||||
|
* badly designed APIs without Cache control.
|
||||||
|
*
|
||||||
|
* Obviously, this follows no RFC :(.
|
||||||
|
*/
|
||||||
|
class GreedyCacheStrategy extends PrivateCacheStrategy
|
||||||
|
{
|
||||||
|
const HEADER_TTL = 'X-Kevinrob-GuzzleCache-TTL';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $defaultTtl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var KeyValueHttpHeader
|
||||||
|
*/
|
||||||
|
private $varyHeaders;
|
||||||
|
|
||||||
|
public function __construct(CacheStorageInterface $cache = null, $defaultTtl, KeyValueHttpHeader $varyHeaders = null)
|
||||||
|
{
|
||||||
|
$this->defaultTtl = $defaultTtl;
|
||||||
|
$this->varyHeaders = $varyHeaders;
|
||||||
|
parent::__construct($cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCacheKey(RequestInterface $request, KeyValueHttpHeader $varyHeaders = null)
|
||||||
|
{
|
||||||
|
if (null === $varyHeaders || $varyHeaders->isEmpty()) {
|
||||||
|
return hash(
|
||||||
|
'sha256',
|
||||||
|
'greedy'.$request->getMethod().$request->getUri()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheHeaders = [];
|
||||||
|
foreach ($varyHeaders as $key => $value) {
|
||||||
|
if ($request->hasHeader($key)) {
|
||||||
|
$cacheHeaders[$key] = $request->getHeader($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash(
|
||||||
|
'sha256',
|
||||||
|
'greedy'.$request->getMethod().$request->getUri().json_encode($cacheHeaders)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$warningMessage = sprintf('%d - "%s" "%s"',
|
||||||
|
299,
|
||||||
|
'Cached although the response headers indicate not to do it!',
|
||||||
|
(new \DateTime())->format(\DateTime::RFC1123)
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $response->withAddedHeader('Warning', $warningMessage);
|
||||||
|
|
||||||
|
if ($cacheObject = $this->getCacheObject($request, $response)) {
|
||||||
|
return $this->storage->save(
|
||||||
|
$this->getCacheKey($request, $this->varyHeaders),
|
||||||
|
$cacheObject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCacheObject(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
if (!array_key_exists($response->getStatusCode(), $this->statusAccepted)) {
|
||||||
|
// Don't cache it
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->varyHeaders && $this->varyHeaders->has('*')) {
|
||||||
|
// This will never match with a request
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $response->withoutHeader('Etag')->withoutHeader('Last-Modified');
|
||||||
|
|
||||||
|
$ttl = $this->defaultTtl;
|
||||||
|
if ($request->hasHeader(self::HEADER_TTL)) {
|
||||||
|
$ttlHeaderValues = $request->getHeader(self::HEADER_TTL);
|
||||||
|
$ttl = (int)reset($ttlHeaderValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CacheEntry($request->withoutHeader(self::HEADER_TTL), $response, new \DateTime(sprintf('+%d seconds', $ttl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetch(RequestInterface $request)
|
||||||
|
{
|
||||||
|
$cache = $this->storage->fetch($this->getCacheKey($request, $this->varyHeaders));
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return $this->storage->delete($this->getCacheKey($request));
|
||||||
|
}
|
||||||
|
}
|
||||||
43
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/NullCacheStrategy.php
vendored
Normal file
43
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/NullCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Strategy;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class NullCacheStrategy implements CacheStrategyInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function fetch(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function update(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
256
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PrivateCacheStrategy.php
vendored
Normal file
256
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PrivateCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Strategy;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\CacheEntry;
|
||||||
|
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\CacheStorageInterface;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\VolatileRuntimeStorage;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This strategy represents a "private" HTTP client.
|
||||||
|
* Pay attention to share storage between application with caution!
|
||||||
|
*
|
||||||
|
* For example, a response with cache-control header "private, max-age=60"
|
||||||
|
* will be cached by this strategy.
|
||||||
|
*
|
||||||
|
* The rules applied are from RFC 7234.
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc7234
|
||||||
|
*/
|
||||||
|
class PrivateCacheStrategy implements CacheStrategyInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CacheStorageInterface
|
||||||
|
*/
|
||||||
|
protected $storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int[]
|
||||||
|
*/
|
||||||
|
protected $statusAccepted = [
|
||||||
|
200 => 200,
|
||||||
|
203 => 203,
|
||||||
|
204 => 204,
|
||||||
|
300 => 300,
|
||||||
|
301 => 301,
|
||||||
|
404 => 404,
|
||||||
|
405 => 405,
|
||||||
|
410 => 410,
|
||||||
|
414 => 414,
|
||||||
|
418 => 418,
|
||||||
|
501 => 501,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $ageKey = [
|
||||||
|
'max-age',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(CacheStorageInterface $cache = null)
|
||||||
|
{
|
||||||
|
$this->storage = $cache !== null ? $cache : new VolatileRuntimeStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @return CacheEntry|null entry to save, null if can't cache it
|
||||||
|
*/
|
||||||
|
protected function getCacheObject(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
if (!isset($this->statusAccepted[$response->getStatusCode()])) {
|
||||||
|
// Don't cache it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheControl = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
|
||||||
|
$varyHeader = new KeyValueHttpHeader($response->getHeader('Vary'));
|
||||||
|
|
||||||
|
if ($varyHeader->has('*')) {
|
||||||
|
// This will never match with a request
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cacheControl->has('no-store')) {
|
||||||
|
// No store allowed (maybe some sensitives data...)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cacheControl->has('no-cache')) {
|
||||||
|
// Stale response see RFC7234 section 5.2.1.4
|
||||||
|
$entry = new CacheEntry($request, $response, new \DateTime('-1 seconds'));
|
||||||
|
|
||||||
|
return $entry->hasValidationInformation() ? $entry : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->ageKey as $key) {
|
||||||
|
if ($cacheControl->has($key)) {
|
||||||
|
return new CacheEntry(
|
||||||
|
$request,
|
||||||
|
$response,
|
||||||
|
new \DateTime('+'.(int) $cacheControl->get($key).'seconds')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response->hasHeader('Expires')) {
|
||||||
|
$expireAt = \DateTime::createFromFormat(\DateTime::RFC1123, $response->getHeaderLine('Expires'));
|
||||||
|
if ($expireAt !== false) {
|
||||||
|
return new CacheEntry(
|
||||||
|
$request,
|
||||||
|
$response,
|
||||||
|
$expireAt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CacheEntry($request, $response, new \DateTime('-1 seconds'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a key for the response cache.
|
||||||
|
*
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param null|KeyValueHttpHeader $varyHeaders The vary headers which should be honoured by the cache (optional)
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getCacheKey(RequestInterface $request, KeyValueHttpHeader $varyHeaders = null)
|
||||||
|
{
|
||||||
|
if (!$varyHeaders) {
|
||||||
|
return hash('sha256', $request->getMethod().$request->getUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheHeaders = [];
|
||||||
|
|
||||||
|
foreach ($varyHeaders as $key => $value) {
|
||||||
|
if ($request->hasHeader($key)) {
|
||||||
|
$cacheHeaders[$key] = $request->getHeader($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash('sha256', $request->getMethod().$request->getUri().json_encode($cacheHeaders));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a CacheEntry or null if no cache.
|
||||||
|
*
|
||||||
|
* @param RequestInterface $request
|
||||||
|
*
|
||||||
|
* @return CacheEntry|null
|
||||||
|
*/
|
||||||
|
public function fetch(RequestInterface $request)
|
||||||
|
{
|
||||||
|
/** @var int|null $maxAge */
|
||||||
|
$maxAge = null;
|
||||||
|
|
||||||
|
if ($request->hasHeader('Cache-Control')) {
|
||||||
|
$reqCacheControl = new KeyValueHttpHeader($request->getHeader('Cache-Control'));
|
||||||
|
if ($reqCacheControl->has('no-cache')) {
|
||||||
|
// Can't return cache
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$maxAge = $reqCacheControl->get('max-age', null);
|
||||||
|
} elseif ($request->hasHeader('Pragma')) {
|
||||||
|
$pragma = new KeyValueHttpHeader($request->getHeader('Pragma'));
|
||||||
|
if ($pragma->has('no-cache')) {
|
||||||
|
// Can't return cache
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache = $this->storage->fetch($this->getCacheKey($request));
|
||||||
|
if ($cache !== null) {
|
||||||
|
$varyHeaders = $cache->getVaryHeaders();
|
||||||
|
|
||||||
|
// vary headers exist from a previous response, check if we have a cache that matches those headers
|
||||||
|
if (!$varyHeaders->isEmpty()) {
|
||||||
|
$cache = $this->storage->fetch($this->getCacheKey($request, $varyHeaders));
|
||||||
|
|
||||||
|
if (!$cache) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string)$cache->getOriginalRequest()->getUri() !== (string)$request->getUri()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($maxAge !== null) {
|
||||||
|
if ($cache->getAge() > $maxAge) {
|
||||||
|
// Cache entry is too old for the request requirements!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$cache->isVaryEquals($request)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return bool true if success
|
||||||
|
*/
|
||||||
|
public function cache(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$reqCacheControl = new KeyValueHttpHeader($request->getHeader('Cache-Control'));
|
||||||
|
if ($reqCacheControl->has('no-store')) {
|
||||||
|
// No caching allowed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheObject = $this->getCacheObject($request, $response);
|
||||||
|
if ($cacheObject !== null) {
|
||||||
|
// store the cache against the URI-only key
|
||||||
|
$success = $this->storage->save(
|
||||||
|
$this->getCacheKey($request),
|
||||||
|
$cacheObject
|
||||||
|
);
|
||||||
|
|
||||||
|
$varyHeaders = $cacheObject->getVaryHeaders();
|
||||||
|
|
||||||
|
if (!$varyHeaders->isEmpty()) {
|
||||||
|
// also store the cache against the vary headers based key
|
||||||
|
$success = $this->storage->save(
|
||||||
|
$this->getCacheKey($request, $varyHeaders),
|
||||||
|
$cacheObject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return bool true if success
|
||||||
|
*/
|
||||||
|
public function update(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return $this->cache($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(RequestInterface $request)
|
||||||
|
{
|
||||||
|
return $this->storage->delete($this->getCacheKey($request));
|
||||||
|
}
|
||||||
|
}
|
||||||
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PublicCacheStrategy.php
vendored
Normal file
42
vendor/kevinrob/guzzle-cache-middleware/src/Strategy/PublicCacheStrategy.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kevinrob\GuzzleCache\Strategy;
|
||||||
|
|
||||||
|
use Kevinrob\GuzzleCache\KeyValueHttpHeader;
|
||||||
|
use Kevinrob\GuzzleCache\Storage\CacheStorageInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This strategy represents a "public" or "shared" HTTP client.
|
||||||
|
* You can share the storage between applications.
|
||||||
|
*
|
||||||
|
* For example, a response with cache-control header "private, max-age=60"
|
||||||
|
* will be NOT cached by this strategy.
|
||||||
|
*
|
||||||
|
* The rules applied are from RFC 7234.
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc7234
|
||||||
|
*/
|
||||||
|
class PublicCacheStrategy extends PrivateCacheStrategy
|
||||||
|
{
|
||||||
|
public function __construct(CacheStorageInterface $cache = null)
|
||||||
|
{
|
||||||
|
parent::__construct($cache);
|
||||||
|
|
||||||
|
array_unshift($this->ageKey, 's-maxage');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getCacheObject(RequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$cacheControl = new KeyValueHttpHeader($response->getHeader('Cache-Control'));
|
||||||
|
if ($cacheControl->has('private')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getCacheObject($request, $response);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
vendor/league/mime-type-detection/CHANGELOG.md
vendored
12
vendor/league/mime-type-detection/CHANGELOG.md
vendored
@@ -1,5 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.12.0 - 2022-08-03
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
|
||||||
|
- Updated lookup
|
||||||
|
|
||||||
|
## 1.11.0 - 2022-04-17
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
|
||||||
|
- Updated lookup
|
||||||
|
|
||||||
## 1.10.0 - 2022-04-11
|
## 1.10.0 - 2022-04-11
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
2
vendor/league/mime-type-detection/LICENSE
vendored
2
vendor/league/mime-type-detection/LICENSE
vendored
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2013-2022 Frank de Jonge
|
Copyright (c) 2013-2023 Frank de Jonge
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'acu' => 'application/vnd.acucobol',
|
'acu' => 'application/vnd.acucobol',
|
||||||
'acutc' => 'application/vnd.acucorp',
|
'acutc' => 'application/vnd.acucorp',
|
||||||
'adp' => 'audio/adpcm',
|
'adp' => 'audio/adpcm',
|
||||||
|
'adts' => 'audio/aac',
|
||||||
'aep' => 'application/vnd.audiograph',
|
'aep' => 'application/vnd.audiograph',
|
||||||
'afm' => 'application/x-font-type1',
|
'afm' => 'application/x-font-type1',
|
||||||
'afp' => 'application/vnd.ibm.modcap',
|
'afp' => 'application/vnd.ibm.modcap',
|
||||||
@@ -46,11 +47,16 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'air' => 'application/vnd.adobe.air-application-installer-package+zip',
|
'air' => 'application/vnd.adobe.air-application-installer-package+zip',
|
||||||
'ait' => 'application/vnd.dvb.ait',
|
'ait' => 'application/vnd.dvb.ait',
|
||||||
'ami' => 'application/vnd.amiga.ami',
|
'ami' => 'application/vnd.amiga.ami',
|
||||||
|
'aml' => 'application/automationml-aml+xml',
|
||||||
|
'amlx' => 'application/automationml-amlx+zip',
|
||||||
'amr' => 'audio/amr',
|
'amr' => 'audio/amr',
|
||||||
'apk' => 'application/vnd.android.package-archive',
|
'apk' => 'application/vnd.android.package-archive',
|
||||||
'apng' => 'image/apng',
|
'apng' => 'image/apng',
|
||||||
'appcache' => 'text/cache-manifest',
|
'appcache' => 'text/cache-manifest',
|
||||||
|
'appinstaller' => 'application/appinstaller',
|
||||||
'application' => 'application/x-ms-application',
|
'application' => 'application/x-ms-application',
|
||||||
|
'appx' => 'application/appx',
|
||||||
|
'appxbundle' => 'application/appxbundle',
|
||||||
'apr' => 'application/vnd.lotus-approach',
|
'apr' => 'application/vnd.lotus-approach',
|
||||||
'arc' => 'application/x-freearc',
|
'arc' => 'application/x-freearc',
|
||||||
'arj' => 'application/x-arj',
|
'arj' => 'application/x-arj',
|
||||||
@@ -95,6 +101,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'bpk' => 'application/octet-stream',
|
'bpk' => 'application/octet-stream',
|
||||||
'bpmn' => 'application/octet-stream',
|
'bpmn' => 'application/octet-stream',
|
||||||
'bsp' => 'model/vnd.valve.source.compiled-map',
|
'bsp' => 'model/vnd.valve.source.compiled-map',
|
||||||
|
'btf' => 'image/prs.btif',
|
||||||
'btif' => 'image/prs.btif',
|
'btif' => 'image/prs.btif',
|
||||||
'buffer' => 'application/octet-stream',
|
'buffer' => 'application/octet-stream',
|
||||||
'bz' => 'application/x-bzip',
|
'bz' => 'application/x-bzip',
|
||||||
@@ -146,6 +153,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'cjs' => 'application/node',
|
'cjs' => 'application/node',
|
||||||
'cla' => 'application/vnd.claymore',
|
'cla' => 'application/vnd.claymore',
|
||||||
'class' => 'application/octet-stream',
|
'class' => 'application/octet-stream',
|
||||||
|
'cld' => 'model/vnd.cld',
|
||||||
'clkk' => 'application/vnd.crick.clicker.keyboard',
|
'clkk' => 'application/vnd.crick.clicker.keyboard',
|
||||||
'clkp' => 'application/vnd.crick.clicker.palette',
|
'clkp' => 'application/vnd.crick.clicker.palette',
|
||||||
'clkt' => 'application/vnd.crick.clicker.template',
|
'clkt' => 'application/vnd.crick.clicker.template',
|
||||||
@@ -180,6 +188,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'csv' => 'text/csv',
|
'csv' => 'text/csv',
|
||||||
'cu' => 'application/cu-seeme',
|
'cu' => 'application/cu-seeme',
|
||||||
'curl' => 'text/vnd.curl',
|
'curl' => 'text/vnd.curl',
|
||||||
|
'cwl' => 'application/cwl',
|
||||||
'cww' => 'application/prs.cww',
|
'cww' => 'application/prs.cww',
|
||||||
'cxt' => 'application/x-director',
|
'cxt' => 'application/x-director',
|
||||||
'cxx' => 'text/x-c',
|
'cxx' => 'text/x-c',
|
||||||
@@ -202,6 +211,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'der' => 'application/x-x509-ca-cert',
|
'der' => 'application/x-x509-ca-cert',
|
||||||
'dfac' => 'application/vnd.dreamfactory',
|
'dfac' => 'application/vnd.dreamfactory',
|
||||||
'dgc' => 'application/x-dgc-compressed',
|
'dgc' => 'application/x-dgc-compressed',
|
||||||
|
'dib' => 'image/bmp',
|
||||||
'dic' => 'text/x-c',
|
'dic' => 'text/x-c',
|
||||||
'dir' => 'application/x-director',
|
'dir' => 'application/x-director',
|
||||||
'dis' => 'application/vnd.mobius.dis',
|
'dis' => 'application/vnd.mobius.dis',
|
||||||
@@ -224,6 +234,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||||
'dp' => 'application/vnd.osgi.dp',
|
'dp' => 'application/vnd.osgi.dp',
|
||||||
'dpg' => 'application/vnd.dpgraph',
|
'dpg' => 'application/vnd.dpgraph',
|
||||||
|
'dpx' => 'image/dpx',
|
||||||
'dra' => 'audio/vnd.dra',
|
'dra' => 'audio/vnd.dra',
|
||||||
'drle' => 'image/dicom-rle',
|
'drle' => 'image/dicom-rle',
|
||||||
'dsc' => 'text/prs.lines.tag',
|
'dsc' => 'text/prs.lines.tag',
|
||||||
@@ -260,7 +271,6 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'eot' => 'application/vnd.ms-fontobject',
|
'eot' => 'application/vnd.ms-fontobject',
|
||||||
'eps' => 'application/postscript',
|
'eps' => 'application/postscript',
|
||||||
'epub' => 'application/epub+zip',
|
'epub' => 'application/epub+zip',
|
||||||
'es' => 'application/ecmascript',
|
|
||||||
'es3' => 'application/vnd.eszigno3+xml',
|
'es3' => 'application/vnd.eszigno3+xml',
|
||||||
'esa' => 'application/vnd.osgi.subsystem',
|
'esa' => 'application/vnd.osgi.subsystem',
|
||||||
'esf' => 'application/vnd.epson.esf',
|
'esf' => 'application/vnd.epson.esf',
|
||||||
@@ -453,6 +463,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'jsonld' => 'application/ld+json',
|
'jsonld' => 'application/ld+json',
|
||||||
'jsonml' => 'application/jsonml+json',
|
'jsonml' => 'application/jsonml+json',
|
||||||
'jsx' => 'text/jsx',
|
'jsx' => 'text/jsx',
|
||||||
|
'jt' => 'model/jt',
|
||||||
'jxr' => 'image/jxr',
|
'jxr' => 'image/jxr',
|
||||||
'jxra' => 'image/jxra',
|
'jxra' => 'image/jxra',
|
||||||
'jxrs' => 'image/jxrs',
|
'jxrs' => 'image/jxrs',
|
||||||
@@ -557,7 +568,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'mime' => 'message/rfc822',
|
'mime' => 'message/rfc822',
|
||||||
'mj2' => 'video/mj2',
|
'mj2' => 'video/mj2',
|
||||||
'mjp2' => 'video/mj2',
|
'mjp2' => 'video/mj2',
|
||||||
'mjs' => 'application/javascript',
|
'mjs' => 'text/javascript',
|
||||||
'mk3d' => 'video/x-matroska',
|
'mk3d' => 'video/x-matroska',
|
||||||
'mka' => 'audio/x-matroska',
|
'mka' => 'audio/x-matroska',
|
||||||
'mkd' => 'text/x-markdown',
|
'mkd' => 'text/x-markdown',
|
||||||
@@ -607,6 +618,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'msg' => 'application/vnd.ms-outlook',
|
'msg' => 'application/vnd.ms-outlook',
|
||||||
'msh' => 'model/mesh',
|
'msh' => 'model/mesh',
|
||||||
'msi' => 'application/x-msdownload',
|
'msi' => 'application/x-msdownload',
|
||||||
|
'msix' => 'application/msix',
|
||||||
|
'msixbundle' => 'application/msixbundle',
|
||||||
'msl' => 'application/vnd.mobius.msl',
|
'msl' => 'application/vnd.mobius.msl',
|
||||||
'msm' => 'application/octet-stream',
|
'msm' => 'application/octet-stream',
|
||||||
'msp' => 'application/octet-stream',
|
'msp' => 'application/octet-stream',
|
||||||
@@ -780,6 +793,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'pvb' => 'application/vnd.3gpp.pic-bw-var',
|
'pvb' => 'application/vnd.3gpp.pic-bw-var',
|
||||||
'pwn' => 'application/vnd.3m.post-it-notes',
|
'pwn' => 'application/vnd.3m.post-it-notes',
|
||||||
'pya' => 'audio/vnd.ms-playready.media.pya',
|
'pya' => 'audio/vnd.ms-playready.media.pya',
|
||||||
|
'pyo' => 'model/vnd.pytha.pyox',
|
||||||
|
'pyox' => 'model/vnd.pytha.pyox',
|
||||||
'pyv' => 'video/vnd.ms-playready.media.pyv',
|
'pyv' => 'video/vnd.ms-playready.media.pyv',
|
||||||
'qam' => 'application/vnd.epson.quickanime',
|
'qam' => 'application/vnd.epson.quickanime',
|
||||||
'qbo' => 'application/vnd.intu.qbo',
|
'qbo' => 'application/vnd.intu.qbo',
|
||||||
@@ -928,10 +943,12 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'st' => 'application/vnd.sailingtracker.track',
|
'st' => 'application/vnd.sailingtracker.track',
|
||||||
'stc' => 'application/vnd.sun.xml.calc.template',
|
'stc' => 'application/vnd.sun.xml.calc.template',
|
||||||
'std' => 'application/vnd.sun.xml.draw.template',
|
'std' => 'application/vnd.sun.xml.draw.template',
|
||||||
|
'step' => 'application/STEP',
|
||||||
'stf' => 'application/vnd.wt.stf',
|
'stf' => 'application/vnd.wt.stf',
|
||||||
'sti' => 'application/vnd.sun.xml.impress.template',
|
'sti' => 'application/vnd.sun.xml.impress.template',
|
||||||
'stk' => 'application/hyperstudio',
|
'stk' => 'application/hyperstudio',
|
||||||
'stl' => 'model/stl',
|
'stl' => 'model/stl',
|
||||||
|
'stp' => 'application/STEP',
|
||||||
'stpx' => 'model/step+xml',
|
'stpx' => 'model/step+xml',
|
||||||
'stpxz' => 'model/step-xml+zip',
|
'stpxz' => 'model/step-xml+zip',
|
||||||
'stpz' => 'model/step+zip',
|
'stpz' => 'model/step+zip',
|
||||||
@@ -1018,10 +1035,12 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'ulx' => 'application/x-glulx',
|
'ulx' => 'application/x-glulx',
|
||||||
'umj' => 'application/vnd.umajin',
|
'umj' => 'application/vnd.umajin',
|
||||||
'unityweb' => 'application/vnd.unity',
|
'unityweb' => 'application/vnd.unity',
|
||||||
|
'uo' => 'application/vnd.uoml+xml',
|
||||||
'uoml' => 'application/vnd.uoml+xml',
|
'uoml' => 'application/vnd.uoml+xml',
|
||||||
'uri' => 'text/uri-list',
|
'uri' => 'text/uri-list',
|
||||||
'uris' => 'text/uri-list',
|
'uris' => 'text/uri-list',
|
||||||
'urls' => 'text/uri-list',
|
'urls' => 'text/uri-list',
|
||||||
|
'usda' => 'model/vnd.usda',
|
||||||
'usdz' => 'model/vnd.usdz+zip',
|
'usdz' => 'model/vnd.usdz+zip',
|
||||||
'ustar' => 'application/x-ustar',
|
'ustar' => 'application/x-ustar',
|
||||||
'utz' => 'application/vnd.uiq.theme',
|
'utz' => 'application/vnd.uiq.theme',
|
||||||
@@ -1101,6 +1120,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'webmanifest' => 'application/manifest+json',
|
'webmanifest' => 'application/manifest+json',
|
||||||
'webp' => 'image/webp',
|
'webp' => 'image/webp',
|
||||||
'wg' => 'application/vnd.pmi.widget',
|
'wg' => 'application/vnd.pmi.widget',
|
||||||
|
'wgsl' => 'text/wgsl',
|
||||||
'wgt' => 'application/widget',
|
'wgt' => 'application/widget',
|
||||||
'wif' => 'application/watcherinfo+xml',
|
'wif' => 'application/watcherinfo+xml',
|
||||||
'wks' => 'application/vnd.ms-works',
|
'wks' => 'application/vnd.ms-works',
|
||||||
@@ -1155,9 +1175,10 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'xel' => 'application/xcap-el+xml',
|
'xel' => 'application/xcap-el+xml',
|
||||||
'xenc' => 'application/xenc+xml',
|
'xenc' => 'application/xenc+xml',
|
||||||
'xer' => 'application/patch-ops-error+xml',
|
'xer' => 'application/patch-ops-error+xml',
|
||||||
'xfdf' => 'application/vnd.adobe.xfdf',
|
'xfdf' => 'application/xfdf',
|
||||||
'xfdl' => 'application/vnd.xfdl',
|
'xfdl' => 'application/vnd.xfdl',
|
||||||
'xht' => 'application/xhtml+xml',
|
'xht' => 'application/xhtml+xml',
|
||||||
|
'xhtm' => 'application/vnd.pwg-xhtml-print+xml',
|
||||||
'xhtml' => 'application/xhtml+xml',
|
'xhtml' => 'application/xhtml+xml',
|
||||||
'xhvml' => 'application/xv+xml',
|
'xhvml' => 'application/xv+xml',
|
||||||
'xif' => 'image/vnd.xiff',
|
'xif' => 'image/vnd.xiff',
|
||||||
@@ -1188,6 +1209,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
|||||||
'xpw' => 'application/vnd.intercon.formnet',
|
'xpw' => 'application/vnd.intercon.formnet',
|
||||||
'xpx' => 'application/vnd.intercon.formnet',
|
'xpx' => 'application/vnd.intercon.formnet',
|
||||||
'xsd' => 'application/xml',
|
'xsd' => 'application/xml',
|
||||||
|
'xsf' => 'application/prs.xsf+xml',
|
||||||
'xsl' => 'application/xml',
|
'xsl' => 'application/xml',
|
||||||
'xslt' => 'application/xslt+xml',
|
'xslt' => 'application/xslt+xml',
|
||||||
'xsm' => 'application/vnd.syncml+xml',
|
'xsm' => 'application/vnd.syncml+xml',
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*.php]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
@@ -6,6 +6,82 @@ backwards-incompatible changes that will affect existing usage.
|
|||||||
|
|
||||||
<!-- CHANGELOG_PLACEHOLDER -->
|
<!-- CHANGELOG_PLACEHOLDER -->
|
||||||
|
|
||||||
|
## 9.19.2 - 2024-04-09
|
||||||
|
|
||||||
|
- Fix CI: Use nullable type declaration ([#859](https://github.com/php-curl-class/php-curl-class/pull/859))
|
||||||
|
|
||||||
|
## 9.19.1 - 2024-02-27
|
||||||
|
|
||||||
|
- Fix afterSend not being called ([#848](https://github.com/php-curl-class/php-curl-class/pull/848))
|
||||||
|
|
||||||
|
## 9.19.0 - 2024-01-18
|
||||||
|
|
||||||
|
- Allow displaying curl option value without specifying value ([#837](https://github.com/php-curl-class/php-curl-class/pull/837))
|
||||||
|
|
||||||
|
## 9.18.2 - 2023-09-11
|
||||||
|
|
||||||
|
- Fix use of mb_strpos() causing error when polyfill is used ([#813](https://github.com/php-curl-class/php-curl-class/pull/813))
|
||||||
|
|
||||||
|
## 9.18.1 - 2023-08-29
|
||||||
|
|
||||||
|
- Add additional check for decoding gzip-encoded responses ([#808](https://github.com/php-curl-class/php-curl-class/pull/808))
|
||||||
|
|
||||||
|
## 9.18.0 - 2023-08-28
|
||||||
|
|
||||||
|
- Implement Curl::setError() and MultiCurl::setError() ([#805](https://github.com/php-curl-class/php-curl-class/pull/805))
|
||||||
|
- Rename ::setError() to ::afterSend() ([#807](https://github.com/php-curl-class/php-curl-class/pull/807))
|
||||||
|
|
||||||
|
## 9.17.4 - 2023-07-10
|
||||||
|
|
||||||
|
- Add coding standards rule to use the null coalescing operator ?? where possible ([#801](https://github.com/php-curl-class/php-curl-class/pull/801))
|
||||||
|
- Replace isset with null coalescing operator ([#800](https://github.com/php-curl-class/php-curl-class/pull/800))
|
||||||
|
|
||||||
|
## 9.17.3 - 2023-07-04
|
||||||
|
|
||||||
|
- Update PHP_CodeSniffer ruleset: PSR2 → PSR12 ([#797](https://github.com/php-curl-class/php-curl-class/pull/797))
|
||||||
|
- Add additional coding standard checks ([#796](https://github.com/php-curl-class/php-curl-class/pull/796))
|
||||||
|
|
||||||
|
## 9.17.2 - 2023-06-27
|
||||||
|
|
||||||
|
- Use short array syntax ([#793](https://github.com/php-curl-class/php-curl-class/pull/793))
|
||||||
|
- Add PHP-CS-Fixer to check for unused imports ([#794](https://github.com/php-curl-class/php-curl-class/pull/794))
|
||||||
|
- Replace `uniqid` by `random_bytes` ([#792](https://github.com/php-curl-class/php-curl-class/pull/792))
|
||||||
|
|
||||||
|
## 9.17.1 - 2023-06-14
|
||||||
|
|
||||||
|
- Improve and add tests for Curl::fastDownload() ([#791](https://github.com/php-curl-class/php-curl-class/pull/791))
|
||||||
|
|
||||||
|
## 9.17.0 - 2023-06-13
|
||||||
|
|
||||||
|
- Make method to display curl option value public ([#790](https://github.com/php-curl-class/php-curl-class/pull/790))
|
||||||
|
|
||||||
|
## 9.16.1 - 2023-06-12
|
||||||
|
|
||||||
|
- Differentiate between internal options and user-set options ([#788](https://github.com/php-curl-class/php-curl-class/pull/788))
|
||||||
|
- Create method to display a curl option value ([#785](https://github.com/php-curl-class/php-curl-class/pull/785))
|
||||||
|
- Fix existing header overwritten after using MultiCurl::addCurl() ([#787](https://github.com/php-curl-class/php-curl-class/pull/787))
|
||||||
|
|
||||||
|
## 9.16.0 - 2023-05-25
|
||||||
|
|
||||||
|
- Graduate Curl::fastDownload() ([#783](https://github.com/php-curl-class/php-curl-class/pull/783))
|
||||||
|
|
||||||
|
## 9.15.1 - 2023-05-24
|
||||||
|
|
||||||
|
- Fix PHP CodeSniffer errors ([#782](https://github.com/php-curl-class/php-curl-class/pull/782))
|
||||||
|
|
||||||
|
## 9.15.0 - 2023-05-22
|
||||||
|
|
||||||
|
- Update Curl::diagnose() to detect bit flags with negative values ([#781](https://github.com/php-curl-class/php-curl-class/pull/781))
|
||||||
|
- Display bit flags in use when calling Curl::diagnose() ([#779](https://github.com/php-curl-class/php-curl-class/pull/779))
|
||||||
|
|
||||||
|
## 9.14.5 - 2023-05-16
|
||||||
|
|
||||||
|
- Handle missing content-type response header in Curl::diagnose() ([#778](https://github.com/php-curl-class/php-curl-class/pull/778))
|
||||||
|
|
||||||
|
## 9.14.4 - 2023-05-08
|
||||||
|
|
||||||
|
- Update article in Curl::diagnose() Allow header warning ([#776](https://github.com/php-curl-class/php-curl-class/pull/776))
|
||||||
|
|
||||||
## 9.14.3 - 2023-03-13
|
## 9.14.3 - 2023-03-13
|
||||||
|
|
||||||
- Remove use of array_merge() inside loop ([#774](https://github.com/php-curl-class/php-curl-class/pull/774))
|
- Remove use of array_merge() inside loop ([#774](https://github.com/php-curl-class/php-curl-class/pull/774))
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Installation instructions to use the `composer` command can be found on https://
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
PHP Curl Class works with PHP 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, and 8.2.
|
PHP Curl Class works with PHP 8.3, 8.2, 8.1, 8.0, 7.4, 7.3, 7.2, 7.1, and 7.0.
|
||||||
|
|
||||||
### Quick Start and Examples
|
### Quick Start and Examples
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ More examples are available under [/examples](https://github.com/php-curl-class/
|
|||||||
Curl::__construct($base_url = null, $options = [])
|
Curl::__construct($base_url = null, $options = [])
|
||||||
Curl::__destruct()
|
Curl::__destruct()
|
||||||
Curl::__get($name)
|
Curl::__get($name)
|
||||||
Curl::_fastDownload($url, $filename, $connections = 4)
|
Curl::afterSend($callback)
|
||||||
Curl::attemptRetry()
|
Curl::attemptRetry()
|
||||||
Curl::beforeSend($callback)
|
Curl::beforeSend($callback)
|
||||||
Curl::buildPostData($data)
|
Curl::buildPostData($data)
|
||||||
@@ -209,10 +209,12 @@ Curl::complete($callback)
|
|||||||
Curl::delete($url, $query_parameters = [], $data = [])
|
Curl::delete($url, $query_parameters = [], $data = [])
|
||||||
Curl::diagnose($return = false)
|
Curl::diagnose($return = false)
|
||||||
Curl::disableTimeout()
|
Curl::disableTimeout()
|
||||||
|
Curl::displayCurlOptionValue($option, $value = null)
|
||||||
Curl::download($url, $mixed_filename)
|
Curl::download($url, $mixed_filename)
|
||||||
Curl::error($callback)
|
Curl::error($callback)
|
||||||
Curl::exec($ch = null)
|
Curl::exec($ch = null)
|
||||||
Curl::execDone()
|
Curl::execDone()
|
||||||
|
Curl::fastDownload($url, $filename, $connections = 4)
|
||||||
Curl::get($url, $data = [])
|
Curl::get($url, $data = [])
|
||||||
Curl::getAttempts()
|
Curl::getAttempts()
|
||||||
Curl::getBeforeSendCallback()
|
Curl::getBeforeSendCallback()
|
||||||
@@ -233,6 +235,7 @@ Curl::getId()
|
|||||||
Curl::getInfo($opt = null)
|
Curl::getInfo($opt = null)
|
||||||
Curl::getJsonDecoder()
|
Curl::getJsonDecoder()
|
||||||
Curl::getOpt($option)
|
Curl::getOpt($option)
|
||||||
|
Curl::getOptions()
|
||||||
Curl::getRawResponse()
|
Curl::getRawResponse()
|
||||||
Curl::getRawResponseHeaders()
|
Curl::getRawResponseHeaders()
|
||||||
Curl::getRemainingRetries()
|
Curl::getRemainingRetries()
|
||||||
@@ -245,6 +248,7 @@ Curl::getRetries()
|
|||||||
Curl::getRetryDecider()
|
Curl::getRetryDecider()
|
||||||
Curl::getSuccessCallback()
|
Curl::getSuccessCallback()
|
||||||
Curl::getUrl()
|
Curl::getUrl()
|
||||||
|
Curl::getUserSetOptions()
|
||||||
Curl::getXmlDecoder()
|
Curl::getXmlDecoder()
|
||||||
Curl::head($url, $data = [])
|
Curl::head($url, $data = [])
|
||||||
Curl::isChildOfMultiCurl()
|
Curl::isChildOfMultiCurl()
|
||||||
@@ -319,6 +323,7 @@ MultiCurl::addPatch($url, $data = [])
|
|||||||
MultiCurl::addPost($url, $data = '', $follow_303_with_post = false)
|
MultiCurl::addPost($url, $data = '', $follow_303_with_post = false)
|
||||||
MultiCurl::addPut($url, $data = [])
|
MultiCurl::addPut($url, $data = [])
|
||||||
MultiCurl::addSearch($url, $data = [])
|
MultiCurl::addSearch($url, $data = [])
|
||||||
|
MultiCurl::afterSend($callback)
|
||||||
MultiCurl::beforeSend($callback)
|
MultiCurl::beforeSend($callback)
|
||||||
MultiCurl::close()
|
MultiCurl::close()
|
||||||
MultiCurl::complete($callback)
|
MultiCurl::complete($callback)
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Zach Borboa"
|
"name": "Zach Borboa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Contributors",
|
||||||
|
"homepage": "https://github.com/php-curl-class/php-curl-class/graphs/contributors"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
@@ -20,11 +24,12 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": "*",
|
"dealerdirect/phpcodesniffer-composer-installer": "*",
|
||||||
"ext-gd": "*",
|
"ext-gd": "*",
|
||||||
|
"friendsofphp/php-cs-fixer": "*",
|
||||||
"phpcompatibility/php-compatibility": "dev-develop",
|
"phpcompatibility/php-compatibility": "dev-develop",
|
||||||
"phpcsstandards/phpcsutils": "@alpha",
|
"phpcsstandards/phpcsutils": "@alpha",
|
||||||
"phpunit/phpunit": "*",
|
"phpunit/phpunit": "*",
|
||||||
"squizlabs/php_codesniffer": "*",
|
"squizlabs/php_codesniffer": "*",
|
||||||
"vimeo/psalm": "*"
|
"vimeo/psalm": ">=0.3.63"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-mbstring": "*"
|
"ext-mbstring": "*"
|
||||||
|
|||||||
2
vendor/php-curl-class/php-curl-class/setup.cfg
vendored
Normal file
2
vendor/php-curl-class/php-curl-class/setup.cfg
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[flake8]
|
||||||
|
max-line-length = 100
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
use Curl\CaseInsensitiveArray;
|
|
||||||
|
|
||||||
class ArrayUtil
|
class ArrayUtil
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Is Array Assoc
|
* Is Array Assoc
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
*
|
* @return bool
|
||||||
* @return boolean
|
|
||||||
*/
|
*/
|
||||||
public static function isArrayAssoc($array)
|
public static function isArrayAssoc($array)
|
||||||
{
|
{
|
||||||
@@ -26,10 +24,8 @@ class ArrayUtil
|
|||||||
* Is Array Assoc
|
* Is Array Assoc
|
||||||
*
|
*
|
||||||
* @deprecated Use ArrayUtil::isArrayAssoc().
|
* @deprecated Use ArrayUtil::isArrayAssoc().
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
*
|
* @return bool
|
||||||
* @return boolean
|
|
||||||
*/
|
*/
|
||||||
public static function is_array_assoc($array)
|
public static function is_array_assoc($array)
|
||||||
{
|
{
|
||||||
@@ -39,10 +35,8 @@ class ArrayUtil
|
|||||||
/**
|
/**
|
||||||
* Is Array Multidim
|
* Is Array Multidim
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
*
|
* @return bool
|
||||||
* @return boolean
|
|
||||||
*/
|
*/
|
||||||
public static function isArrayMultidim($array)
|
public static function isArrayMultidim($array)
|
||||||
{
|
{
|
||||||
@@ -57,10 +51,8 @@ class ArrayUtil
|
|||||||
* Is Array Multidim
|
* Is Array Multidim
|
||||||
*
|
*
|
||||||
* @deprecated Use ArrayUtil::isArrayMultidim().
|
* @deprecated Use ArrayUtil::isArrayMultidim().
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
*
|
* @return bool
|
||||||
* @return boolean
|
|
||||||
*/
|
*/
|
||||||
public static function is_array_multidim($array)
|
public static function is_array_multidim($array)
|
||||||
{
|
{
|
||||||
@@ -70,10 +62,8 @@ class ArrayUtil
|
|||||||
/**
|
/**
|
||||||
* Array Flatten Multidim
|
* Array Flatten Multidim
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
* @param $prefix
|
* @param $prefix
|
||||||
*
|
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function arrayFlattenMultidim($array, $prefix = false)
|
public static function arrayFlattenMultidim($array, $prefix = false)
|
||||||
@@ -124,10 +114,8 @@ class ArrayUtil
|
|||||||
* Array Flatten Multidim
|
* Array Flatten Multidim
|
||||||
*
|
*
|
||||||
* @deprecated Use ArrayUtil::arrayFlattenMultidim().
|
* @deprecated Use ArrayUtil::arrayFlattenMultidim().
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
* @param $prefix
|
* @param $prefix
|
||||||
*
|
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function array_flatten_multidim($array, $prefix = false)
|
public static function array_flatten_multidim($array, $prefix = false)
|
||||||
@@ -138,9 +126,7 @@ class ArrayUtil
|
|||||||
/**
|
/**
|
||||||
* Array Random
|
* Array Random
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
*
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function arrayRandom($array)
|
public static function arrayRandom($array)
|
||||||
@@ -151,10 +137,8 @@ class ArrayUtil
|
|||||||
/**
|
/**
|
||||||
* Array Random Index
|
* Array Random Index
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
*
|
* @return int
|
||||||
* @return integer
|
|
||||||
*/
|
*/
|
||||||
public static function arrayRandomIndex($array)
|
public static function arrayRandomIndex($array)
|
||||||
{
|
{
|
||||||
@@ -165,9 +149,7 @@ class ArrayUtil
|
|||||||
* Array Random
|
* Array Random
|
||||||
*
|
*
|
||||||
* @deprecated Use ArrayUtil::arrayRandom().
|
* @deprecated Use ArrayUtil::arrayRandom().
|
||||||
* @access public
|
|
||||||
* @param $array
|
* @param $array
|
||||||
*
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function array_random($array)
|
public static function array_random($array)
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
abstract class BaseCurl
|
abstract class BaseCurl
|
||||||
{
|
{
|
||||||
public $beforeSendCallback = null;
|
public $beforeSendCallback = null;
|
||||||
|
public $afterSendCallback = null;
|
||||||
public $successCallback = null;
|
public $successCallback = null;
|
||||||
public $errorCallback = null;
|
public $errorCallback = null;
|
||||||
public $completeCallback = null;
|
public $completeCallback = null;
|
||||||
|
|
||||||
protected $options = [];
|
protected $options = [];
|
||||||
|
protected $userSetOptions = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Before Send
|
* Before Send
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $callback callable|null
|
* @param $callback callable|null
|
||||||
*/
|
*/
|
||||||
public function beforeSend($callback)
|
public function beforeSend($callback)
|
||||||
@@ -27,7 +30,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Complete
|
* Complete
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $callback callable|null
|
* @param $callback callable|null
|
||||||
*/
|
*/
|
||||||
public function complete($callback)
|
public function complete($callback)
|
||||||
@@ -37,8 +39,6 @@ abstract class BaseCurl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable Timeout
|
* Disable Timeout
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
public function disableTimeout()
|
public function disableTimeout()
|
||||||
{
|
{
|
||||||
@@ -48,7 +48,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Error
|
* Error
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $callback callable|null
|
* @param $callback callable|null
|
||||||
*/
|
*/
|
||||||
public function error($callback)
|
public function error($callback)
|
||||||
@@ -59,14 +58,12 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Get Opt
|
* Get Opt
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $option
|
* @param $option
|
||||||
*
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getOpt($option)
|
public function getOpt($option)
|
||||||
{
|
{
|
||||||
return isset($this->options[$option]) ? $this->options[$option] : null;
|
return $this->options[$option] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,7 +72,6 @@ abstract class BaseCurl
|
|||||||
* Remove an internal header from the request.
|
* Remove an internal header from the request.
|
||||||
* Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');.
|
* Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $key
|
* @param $key
|
||||||
*/
|
*/
|
||||||
public function removeHeader($key)
|
public function removeHeader($key)
|
||||||
@@ -86,7 +82,7 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set auto referer
|
* Set auto referer
|
||||||
*
|
*
|
||||||
* @access public
|
* @param mixed $auto_referer
|
||||||
*/
|
*/
|
||||||
public function setAutoReferer($auto_referer = true)
|
public function setAutoReferer($auto_referer = true)
|
||||||
{
|
{
|
||||||
@@ -96,7 +92,7 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set auto referrer
|
* Set auto referrer
|
||||||
*
|
*
|
||||||
* @access public
|
* @param mixed $auto_referrer
|
||||||
*/
|
*/
|
||||||
public function setAutoReferrer($auto_referrer = true)
|
public function setAutoReferrer($auto_referrer = true)
|
||||||
{
|
{
|
||||||
@@ -106,7 +102,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Basic Authentication
|
* Set Basic Authentication
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $username
|
* @param $username
|
||||||
* @param $password
|
* @param $password
|
||||||
*/
|
*/
|
||||||
@@ -119,7 +114,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Connect Timeout
|
* Set Connect Timeout
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $seconds
|
* @param $seconds
|
||||||
*/
|
*/
|
||||||
public function setConnectTimeout($seconds)
|
public function setConnectTimeout($seconds)
|
||||||
@@ -136,7 +130,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Digest Authentication
|
* Set Digest Authentication
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $username
|
* @param $username
|
||||||
* @param $password
|
* @param $password
|
||||||
*/
|
*/
|
||||||
@@ -146,10 +139,31 @@ abstract class BaseCurl
|
|||||||
$this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
|
$this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After Send
|
||||||
|
*
|
||||||
|
* This function is called after the request has been sent.
|
||||||
|
*
|
||||||
|
* It can be used to override whether or not the request errored. The
|
||||||
|
* instance is passed as the first argument to the function and the instance
|
||||||
|
* has attributes like $instance->httpStatusCode and $instance->response to
|
||||||
|
* help decide if the request errored. Set $instance->error to true or false
|
||||||
|
* within the function.
|
||||||
|
*
|
||||||
|
* When $instance->error is true indicating a request error, the error
|
||||||
|
* callback set by Curl::error() is called. When $instance->error is false,
|
||||||
|
* the success callback set by Curl::success() is called.
|
||||||
|
*
|
||||||
|
* @param $callback callable|null
|
||||||
|
*/
|
||||||
|
public function afterSend($callback)
|
||||||
|
{
|
||||||
|
$this->afterSendCallback = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set File
|
* Set File
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $file
|
* @param $file
|
||||||
*/
|
*/
|
||||||
public function setFile($file)
|
public function setFile($file)
|
||||||
@@ -157,10 +171,16 @@ abstract class BaseCurl
|
|||||||
$this->setOpt(CURLOPT_FILE, $file);
|
$this->setOpt(CURLOPT_FILE, $file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setFileInternal($file)
|
||||||
|
{
|
||||||
|
$this->setOptInternal(CURLOPT_FILE, $file);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set follow location
|
* Set follow location
|
||||||
*
|
*
|
||||||
* @access public
|
* @param mixed $follow_location
|
||||||
|
* @see Curl::setMaximumRedirects()
|
||||||
*/
|
*/
|
||||||
public function setFollowLocation($follow_location = true)
|
public function setFollowLocation($follow_location = true)
|
||||||
{
|
{
|
||||||
@@ -170,7 +190,7 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set forbid reuse
|
* Set forbid reuse
|
||||||
*
|
*
|
||||||
* @access public
|
* @param mixed $forbid_reuse
|
||||||
*/
|
*/
|
||||||
public function setForbidReuse($forbid_reuse = true)
|
public function setForbidReuse($forbid_reuse = true)
|
||||||
{
|
{
|
||||||
@@ -186,7 +206,6 @@ abstract class BaseCurl
|
|||||||
* The name of the outgoing network interface to use.
|
* The name of the outgoing network interface to use.
|
||||||
* This can be an interface name, an IP address or a host name.
|
* This can be an interface name, an IP address or a host name.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $interface
|
* @param $interface
|
||||||
*/
|
*/
|
||||||
public function setInterface($interface)
|
public function setInterface($interface)
|
||||||
@@ -199,7 +218,8 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set maximum redirects
|
* Set maximum redirects
|
||||||
*
|
*
|
||||||
* @access public
|
* @param mixed $maximum_redirects
|
||||||
|
* @see Curl::setFollowLocation()
|
||||||
*/
|
*/
|
||||||
public function setMaximumRedirects($maximum_redirects)
|
public function setMaximumRedirects($maximum_redirects)
|
||||||
{
|
{
|
||||||
@@ -207,12 +227,16 @@ abstract class BaseCurl
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract public function setOpt($option, $value);
|
abstract public function setOpt($option, $value);
|
||||||
|
|
||||||
|
protected function setOptInternal($option, $value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
abstract public function setOpts($options);
|
abstract public function setOpts($options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set Port
|
* Set Port
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $port
|
* @param $port
|
||||||
*/
|
*/
|
||||||
public function setPort($port)
|
public function setPort($port)
|
||||||
@@ -225,7 +249,6 @@ abstract class BaseCurl
|
|||||||
*
|
*
|
||||||
* Set an HTTP proxy to tunnel requests through.
|
* Set an HTTP proxy to tunnel requests through.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $proxy - The HTTP proxy to tunnel requests through. May include port number.
|
* @param $proxy - The HTTP proxy to tunnel requests through. May include port number.
|
||||||
* @param $port - The port number of the proxy to connect to. This port number can also be set in $proxy.
|
* @param $port - The port number of the proxy to connect to. This port number can also be set in $proxy.
|
||||||
* @param $username - The username to use for the connection to the proxy.
|
* @param $username - The username to use for the connection to the proxy.
|
||||||
@@ -247,7 +270,6 @@ abstract class BaseCurl
|
|||||||
*
|
*
|
||||||
* Set the HTTP authentication method(s) to use for the proxy connection.
|
* Set the HTTP authentication method(s) to use for the proxy connection.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $auth
|
* @param $auth
|
||||||
*/
|
*/
|
||||||
public function setProxyAuth($auth)
|
public function setProxyAuth($auth)
|
||||||
@@ -260,7 +282,6 @@ abstract class BaseCurl
|
|||||||
*
|
*
|
||||||
* Set the proxy to tunnel through HTTP proxy.
|
* Set the proxy to tunnel through HTTP proxy.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $tunnel boolean
|
* @param $tunnel boolean
|
||||||
*/
|
*/
|
||||||
public function setProxyTunnel($tunnel = true)
|
public function setProxyTunnel($tunnel = true)
|
||||||
@@ -273,7 +294,6 @@ abstract class BaseCurl
|
|||||||
*
|
*
|
||||||
* Set the proxy protocol type.
|
* Set the proxy protocol type.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $type
|
* @param $type
|
||||||
*/
|
*/
|
||||||
public function setProxyType($type)
|
public function setProxyType($type)
|
||||||
@@ -284,7 +304,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Range
|
* Set Range
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $range
|
* @param $range
|
||||||
*/
|
*/
|
||||||
public function setRange($range)
|
public function setRange($range)
|
||||||
@@ -292,10 +311,14 @@ abstract class BaseCurl
|
|||||||
$this->setOpt(CURLOPT_RANGE, $range);
|
$this->setOpt(CURLOPT_RANGE, $range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setRangeInternal($range)
|
||||||
|
{
|
||||||
|
$this->setOptInternal(CURLOPT_RANGE, $range);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set Referer
|
* Set Referer
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $referer
|
* @param $referer
|
||||||
*/
|
*/
|
||||||
public function setReferer($referer)
|
public function setReferer($referer)
|
||||||
@@ -306,7 +329,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Referrer
|
* Set Referrer
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $referrer
|
* @param $referrer
|
||||||
*/
|
*/
|
||||||
public function setReferrer($referrer)
|
public function setReferrer($referrer)
|
||||||
@@ -319,7 +341,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Timeout
|
* Set Timeout
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $seconds
|
* @param $seconds
|
||||||
*/
|
*/
|
||||||
public function setTimeout($seconds)
|
public function setTimeout($seconds)
|
||||||
@@ -327,12 +348,16 @@ abstract class BaseCurl
|
|||||||
$this->setOpt(CURLOPT_TIMEOUT, $seconds);
|
$this->setOpt(CURLOPT_TIMEOUT, $seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setTimeoutInternal($seconds)
|
||||||
|
{
|
||||||
|
$this->setOptInternal(CURLOPT_TIMEOUT, $seconds);
|
||||||
|
}
|
||||||
|
|
||||||
abstract public function setUrl($url, $mixed_data = '');
|
abstract public function setUrl($url, $mixed_data = '');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set User Agent
|
* Set User Agent
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $user_agent
|
* @param $user_agent
|
||||||
*/
|
*/
|
||||||
public function setUserAgent($user_agent)
|
public function setUserAgent($user_agent)
|
||||||
@@ -340,13 +365,17 @@ abstract class BaseCurl
|
|||||||
$this->setOpt(CURLOPT_USERAGENT, $user_agent);
|
$this->setOpt(CURLOPT_USERAGENT, $user_agent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setUserAgentInternal($user_agent)
|
||||||
|
{
|
||||||
|
$this->setOptInternal(CURLOPT_USERAGENT, $user_agent);
|
||||||
|
}
|
||||||
|
|
||||||
abstract public function setXmlDecoder($mixed);
|
abstract public function setXmlDecoder($mixed);
|
||||||
abstract public function stop();
|
abstract public function stop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Success
|
* Success
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $callback callable|null
|
* @param $callback callable|null
|
||||||
*/
|
*/
|
||||||
public function success($callback)
|
public function success($callback)
|
||||||
@@ -360,8 +389,6 @@ abstract class BaseCurl
|
|||||||
* Unset Proxy
|
* Unset Proxy
|
||||||
*
|
*
|
||||||
* Disable use of the proxy.
|
* Disable use of the proxy.
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
public function unsetProxy()
|
public function unsetProxy()
|
||||||
{
|
{
|
||||||
@@ -371,7 +398,6 @@ abstract class BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Verbose
|
* Verbose
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param bool $on
|
* @param bool $on
|
||||||
* @param resource|string $output
|
* @param resource|string $output
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
@@ -7,9 +9,9 @@ namespace Curl;
|
|||||||
*/
|
*/
|
||||||
class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed[] Data storage with lowercase keys.
|
* @var mixed[] Data storage with lowercase keys.
|
||||||
|
*
|
||||||
* @see offsetSet()
|
* @see offsetSet()
|
||||||
* @see offsetExists()
|
* @see offsetExists()
|
||||||
* @see offsetUnset()
|
* @see offsetUnset()
|
||||||
@@ -23,6 +25,7 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string[] Case-sensitive keys.
|
* @var string[] Case-sensitive keys.
|
||||||
|
*
|
||||||
* @see offsetSet()
|
* @see offsetSet()
|
||||||
* @see offsetUnset()
|
* @see offsetUnset()
|
||||||
* @see key()
|
* @see key()
|
||||||
@@ -37,12 +40,14 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
* case-sensitive arrays to case-insensitive arrays.
|
* case-sensitive arrays to case-insensitive arrays.
|
||||||
*
|
*
|
||||||
* @param mixed[] $initial (optional) Existing array to convert.
|
* @param mixed[] $initial (optional) Existing array to convert.
|
||||||
*
|
|
||||||
* @return CaseInsensitiveArray
|
* @return CaseInsensitiveArray
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $initial = null)
|
// TODO: Use a nullable type declaration when supported versions >= PHP 7.1.
|
||||||
|
// Trying to use the nullable type declaration on PHP 7.0:
|
||||||
|
// public function __construct(?array $initial = null)
|
||||||
|
// results in:
|
||||||
|
// ParseError: syntax error, unexpected '?', expecting variable (T_VARIABLE)
|
||||||
|
public function __construct($initial = null)
|
||||||
{
|
{
|
||||||
if ($initial !== null) {
|
if ($initial !== null) {
|
||||||
foreach ($initial as $key => $value) {
|
foreach ($initial as $key => $value) {
|
||||||
@@ -58,14 +63,10 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
* stores the case-sensitive offset and the data at the lowercase indexes in
|
* stores the case-sensitive offset and the data at the lowercase indexes in
|
||||||
* $this->keys and @this->data.
|
* $this->keys and @this->data.
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetset.php
|
|
||||||
*
|
|
||||||
* @param string $offset The offset to store the data at (case-insensitive).
|
* @param string $offset The offset to store the data at (case-insensitive).
|
||||||
* @param mixed $value The data to store at the specified offset.
|
* @param mixed $value The data to store at the specified offset.
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*
|
* @see https://secure.php.net/manual/en/arrayaccess.offsetset.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function offsetSet($offset, $value)
|
public function offsetSet($offset, $value)
|
||||||
@@ -85,13 +86,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
* Checks if the offset exists in data storage. The index is looked up with
|
* Checks if the offset exists in data storage. The index is looked up with
|
||||||
* the lowercase version of the provided offset.
|
* the lowercase version of the provided offset.
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetexists.php
|
|
||||||
*
|
|
||||||
* @param string $offset Offset to check
|
* @param string $offset Offset to check
|
||||||
*
|
|
||||||
* @return bool If the offset exists.
|
* @return bool If the offset exists.
|
||||||
*
|
* @see https://secure.php.net/manual/en/arrayaccess.offsetexists.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function offsetExists($offset)
|
public function offsetExists($offset)
|
||||||
@@ -105,13 +102,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
* Unsets the specified offset. Converts the provided offset to lowercase,
|
* Unsets the specified offset. Converts the provided offset to lowercase,
|
||||||
* and unsets the case-sensitive key, as well as the stored data.
|
* and unsets the case-sensitive key, as well as the stored data.
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetunset.php
|
|
||||||
*
|
|
||||||
* @param string $offset The offset to unset.
|
* @param string $offset The offset to unset.
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*
|
* @see https://secure.php.net/manual/en/arrayaccess.offsetunset.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function offsetUnset($offset)
|
public function offsetUnset($offset)
|
||||||
@@ -127,31 +120,23 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
* Return the stored data at the provided offset. The offset is converted to
|
* Return the stored data at the provided offset. The offset is converted to
|
||||||
* lowercase and the lookup is done on the data store directly.
|
* lowercase and the lookup is done on the data store directly.
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetget.php
|
|
||||||
*
|
|
||||||
* @param string $offset Offset to lookup.
|
* @param string $offset Offset to lookup.
|
||||||
*
|
|
||||||
* @return mixed The data stored at the offset.
|
* @return mixed The data stored at the offset.
|
||||||
*
|
* @see https://secure.php.net/manual/en/arrayaccess.offsetget.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function offsetGet($offset)
|
public function offsetGet($offset)
|
||||||
{
|
{
|
||||||
$offsetlower = strtolower($offset);
|
$offsetlower = strtolower($offset);
|
||||||
return isset($this->data[$offsetlower]) ? $this->data[$offsetlower] : null;
|
return $this->data[$offsetlower] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count
|
* Count
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/countable.count.php
|
|
||||||
*
|
|
||||||
* @param void
|
* @param void
|
||||||
*
|
* @return int The number of elements stored in the array.
|
||||||
* @return integer The number of elements stored in the array.
|
* @see https://secure.php.net/manual/en/countable.count.php
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function count()
|
public function count()
|
||||||
@@ -162,13 +147,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
/**
|
/**
|
||||||
* Current
|
* Current
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/iterator.current.php
|
|
||||||
*
|
|
||||||
* @param void
|
* @param void
|
||||||
*
|
|
||||||
* @return mixed Data at the current position.
|
* @return mixed Data at the current position.
|
||||||
*
|
* @see https://secure.php.net/manual/en/iterator.current.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function current()
|
public function current()
|
||||||
@@ -179,13 +160,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
/**
|
/**
|
||||||
* Next
|
* Next
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/iterator.next.php
|
|
||||||
*
|
|
||||||
* @param void
|
* @param void
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*
|
* @see https://secure.php.net/manual/en/iterator.next.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function next()
|
public function next()
|
||||||
@@ -196,29 +173,22 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
/**
|
/**
|
||||||
* Key
|
* Key
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/iterator.key.php
|
|
||||||
*
|
|
||||||
* @param void
|
* @param void
|
||||||
*
|
|
||||||
* @return mixed Case-sensitive key at current position.
|
* @return mixed Case-sensitive key at current position.
|
||||||
*
|
* @see https://secure.php.net/manual/en/iterator.key.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function key()
|
public function key()
|
||||||
{
|
{
|
||||||
$key = key($this->data);
|
$key = key($this->data);
|
||||||
return isset($this->keys[$key]) ? $this->keys[$key] : $key;
|
return $this->keys[$key] ?? $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valid
|
* Valid
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/iterator.valid.php
|
|
||||||
*
|
|
||||||
* @return bool If the current position is valid.
|
* @return bool If the current position is valid.
|
||||||
*
|
* @see https://secure.php.net/manual/en/iterator.valid.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function valid()
|
public function valid()
|
||||||
@@ -229,13 +199,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
|||||||
/**
|
/**
|
||||||
* Rewind
|
* Rewind
|
||||||
*
|
*
|
||||||
* @see https://secure.php.net/manual/en/iterator.rewind.php
|
|
||||||
*
|
|
||||||
* @param void
|
* @param void
|
||||||
*
|
|
||||||
* @return void
|
* @return void
|
||||||
*
|
* @see https://secure.php.net/manual/en/iterator.rewind.php
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function rewind()
|
public function rewind()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
@@ -7,7 +9,6 @@ class Decoder
|
|||||||
/**
|
/**
|
||||||
* Decode JSON
|
* Decode JSON
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $json
|
* @param $json
|
||||||
* @param $assoc
|
* @param $assoc
|
||||||
* @param $depth
|
* @param $depth
|
||||||
@@ -26,7 +27,6 @@ class Decoder
|
|||||||
/**
|
/**
|
||||||
* Decode XML
|
* Decode XML
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $data
|
* @param $data
|
||||||
* @param $class_name
|
* @param $class_name
|
||||||
* @param $options
|
* @param $options
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
@@ -9,11 +11,9 @@ class Encoder
|
|||||||
*
|
*
|
||||||
* Wrap json_encode() to throw error when the value being encoded fails.
|
* Wrap json_encode() to throw error when the value being encoded fails.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $value
|
* @param $value
|
||||||
* @param $options
|
* @param $options
|
||||||
* @param $depth
|
* @param $depth
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \ErrorException
|
* @throws \ErrorException
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
use Curl\ArrayUtil;
|
|
||||||
use Curl\BaseCurl;
|
|
||||||
use Curl\Url;
|
|
||||||
|
|
||||||
class MultiCurl extends BaseCurl
|
class MultiCurl extends BaseCurl
|
||||||
{
|
{
|
||||||
public $baseUrl = null;
|
public $baseUrl = null;
|
||||||
@@ -44,7 +42,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Construct
|
* Construct
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $base_url
|
* @param $base_url
|
||||||
*/
|
*/
|
||||||
public function __construct($base_url = null)
|
public function __construct($base_url = null)
|
||||||
@@ -60,11 +57,9 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Delete
|
* Add Delete
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $query_parameters
|
* @param $query_parameters
|
||||||
* @param $data
|
* @param $data
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addDelete($url, $query_parameters = [], $data = [])
|
public function addDelete($url, $query_parameters = [], $data = [])
|
||||||
@@ -87,10 +82,8 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Download
|
* Add Download
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $mixed_filename
|
* @param $mixed_filename
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addDownload($url, $mixed_filename)
|
public function addDownload($url, $mixed_filename)
|
||||||
@@ -148,10 +141,8 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Get
|
* Add Get
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $data
|
* @param $data
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addGet($url, $data = [])
|
public function addGet($url, $data = [])
|
||||||
@@ -173,10 +164,8 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Head
|
* Add Head
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $data
|
* @param $data
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addHead($url, $data = [])
|
public function addHead($url, $data = [])
|
||||||
@@ -198,10 +187,8 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Options
|
* Add Options
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $data
|
* @param $data
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addOptions($url, $data = [])
|
public function addOptions($url, $data = [])
|
||||||
@@ -223,10 +210,8 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Patch
|
* Add Patch
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $data
|
* @param $data
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addPatch($url, $data = [])
|
public function addPatch($url, $data = [])
|
||||||
@@ -253,13 +238,12 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Post
|
* Add Post
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $data
|
* @param $data
|
||||||
* @param $follow_303_with_post
|
* @param $follow_303_with_post
|
||||||
* If true, will cause 303 redirections to be followed using a POST request (default: false).
|
* If true, will cause 303 redirections to be followed using a POST request
|
||||||
* Note: Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true.
|
* (default: false). Note: Redirections are only followed if the
|
||||||
*
|
* CURLOPT_FOLLOWLOCATION option is set to true.
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addPost($url, $data = '', $follow_303_with_post = false)
|
public function addPost($url, $data = '', $follow_303_with_post = false)
|
||||||
@@ -296,10 +280,8 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Put
|
* Add Put
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $data
|
* @param $data
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addPut($url, $data = [])
|
public function addPut($url, $data = [])
|
||||||
@@ -325,10 +307,8 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Add Search
|
* Add Search
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $data
|
* @param $data
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addSearch($url, $data = [])
|
public function addSearch($url, $data = [])
|
||||||
@@ -356,9 +336,7 @@ class MultiCurl extends BaseCurl
|
|||||||
*
|
*
|
||||||
* Add a Curl instance to the handle queue.
|
* Add a Curl instance to the handle queue.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $curl
|
* @param $curl
|
||||||
*
|
|
||||||
* @return object
|
* @return object
|
||||||
*/
|
*/
|
||||||
public function addCurl(Curl $curl)
|
public function addCurl(Curl $curl)
|
||||||
@@ -369,8 +347,6 @@ class MultiCurl extends BaseCurl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Close
|
* Close
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
public function close()
|
public function close()
|
||||||
{
|
{
|
||||||
@@ -387,7 +363,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Concurrency
|
* Set Concurrency
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $concurrency
|
* @param $concurrency
|
||||||
*/
|
*/
|
||||||
public function setConcurrency($concurrency)
|
public function setConcurrency($concurrency)
|
||||||
@@ -398,7 +373,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Cookie
|
* Set Cookie
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $key
|
* @param $key
|
||||||
* @param $value
|
* @param $value
|
||||||
*/
|
*/
|
||||||
@@ -410,7 +384,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Cookies
|
* Set Cookies
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $cookies
|
* @param $cookies
|
||||||
*/
|
*/
|
||||||
public function setCookies($cookies)
|
public function setCookies($cookies)
|
||||||
@@ -423,7 +396,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Cookie String
|
* Set Cookie String
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $string
|
* @param $string
|
||||||
*/
|
*/
|
||||||
public function setCookieString($string)
|
public function setCookieString($string)
|
||||||
@@ -434,7 +406,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Cookie File
|
* Set Cookie File
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $cookie_file
|
* @param $cookie_file
|
||||||
*/
|
*/
|
||||||
public function setCookieFile($cookie_file)
|
public function setCookieFile($cookie_file)
|
||||||
@@ -445,7 +416,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Cookie Jar
|
* Set Cookie Jar
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $cookie_jar
|
* @param $cookie_jar
|
||||||
*/
|
*/
|
||||||
public function setCookieJar($cookie_jar)
|
public function setCookieJar($cookie_jar)
|
||||||
@@ -458,7 +428,6 @@ class MultiCurl extends BaseCurl
|
|||||||
*
|
*
|
||||||
* Add extra header to include in the request.
|
* Add extra header to include in the request.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $key
|
* @param $key
|
||||||
* @param $value
|
* @param $value
|
||||||
*/
|
*/
|
||||||
@@ -473,7 +442,6 @@ class MultiCurl extends BaseCurl
|
|||||||
*
|
*
|
||||||
* Add extra headers to include in the request.
|
* Add extra headers to include in the request.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $headers
|
* @param $headers
|
||||||
*/
|
*/
|
||||||
public function setHeaders($headers)
|
public function setHeaders($headers)
|
||||||
@@ -499,7 +467,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set JSON Decoder
|
* Set JSON Decoder
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $mixed boolean|callable
|
* @param $mixed boolean|callable
|
||||||
*/
|
*/
|
||||||
public function setJsonDecoder($mixed)
|
public function setJsonDecoder($mixed)
|
||||||
@@ -514,7 +481,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set XML Decoder
|
* Set XML Decoder
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $mixed boolean|callable
|
* @param $mixed boolean|callable
|
||||||
*/
|
*/
|
||||||
public function setXmlDecoder($mixed)
|
public function setXmlDecoder($mixed)
|
||||||
@@ -532,7 +498,6 @@ class MultiCurl extends BaseCurl
|
|||||||
* Set proxies to tunnel requests through. When set, a random proxy will be
|
* Set proxies to tunnel requests through. When set, a random proxy will be
|
||||||
* used for the request.
|
* used for the request.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $proxies array - A list of HTTP proxies to tunnel requests
|
* @param $proxies array - A list of HTTP proxies to tunnel requests
|
||||||
* through. May include port number.
|
* through. May include port number.
|
||||||
*/
|
*/
|
||||||
@@ -544,7 +509,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Opt
|
* Set Opt
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $option
|
* @param $option
|
||||||
* @param $value
|
* @param $value
|
||||||
*/
|
*/
|
||||||
@@ -557,8 +521,10 @@ class MultiCurl extends BaseCurl
|
|||||||
// unexpectedly changing the request url after is has been specified.
|
// unexpectedly changing the request url after is has been specified.
|
||||||
if ($option === CURLOPT_URL) {
|
if ($option === CURLOPT_URL) {
|
||||||
foreach ($this->queuedCurls as $curl_id => $curl) {
|
foreach ($this->queuedCurls as $curl_id => $curl) {
|
||||||
if (!isset($this->instanceSpecificOptions[$curl_id][$option]) ||
|
if (
|
||||||
$this->instanceSpecificOptions[$curl_id][$option] === null) {
|
!isset($this->instanceSpecificOptions[$curl_id][$option]) ||
|
||||||
|
$this->instanceSpecificOptions[$curl_id][$option] === null
|
||||||
|
) {
|
||||||
$this->instanceSpecificOptions[$curl_id][$option] = $value;
|
$this->instanceSpecificOptions[$curl_id][$option] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -568,7 +534,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Opts
|
* Set Opts
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $options
|
* @param $options
|
||||||
*/
|
*/
|
||||||
public function setOpts($options)
|
public function setOpts($options)
|
||||||
@@ -581,7 +546,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Rate Limit
|
* Set Rate Limit
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $rate_limit string (e.g. "60/1m").
|
* @param $rate_limit string (e.g. "60/1m").
|
||||||
* @throws \UnexpectedValueException
|
* @throws \UnexpectedValueException
|
||||||
*/
|
*/
|
||||||
@@ -641,7 +605,6 @@ class MultiCurl extends BaseCurl
|
|||||||
* When using a callable decider, the request will be retried until the
|
* When using a callable decider, the request will be retried until the
|
||||||
* function returns a value which evaluates to false.
|
* function returns a value which evaluates to false.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $mixed
|
* @param $mixed
|
||||||
*/
|
*/
|
||||||
public function setRetry($mixed)
|
public function setRetry($mixed)
|
||||||
@@ -652,7 +615,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Set Url
|
* Set Url
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $mixed_data
|
* @param $mixed_data
|
||||||
*/
|
*/
|
||||||
@@ -672,7 +634,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Start
|
* Start
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @throws \ErrorException
|
* @throws \ErrorException
|
||||||
*/
|
*/
|
||||||
public function start()
|
public function start()
|
||||||
@@ -687,7 +648,8 @@ class MultiCurl extends BaseCurl
|
|||||||
$this->currentRequestCount = 0;
|
$this->currentRequestCount = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
while (count($this->queuedCurls) &&
|
while (
|
||||||
|
count($this->queuedCurls) &&
|
||||||
count($this->activeCurls) < $this->concurrency &&
|
count($this->activeCurls) < $this->concurrency &&
|
||||||
(!$this->rateLimitEnabled || $this->hasRequestQuota())
|
(!$this->rateLimitEnabled || $this->hasRequestQuota())
|
||||||
) {
|
) {
|
||||||
@@ -733,8 +695,10 @@ class MultiCurl extends BaseCurl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((is_resource($this->multiCurl) || $this->multiCurl instanceof \CurlMultiHandle) &&
|
while (
|
||||||
(($info_array = curl_multi_info_read($this->multiCurl)) !== false)) {
|
(is_resource($this->multiCurl) || $this->multiCurl instanceof \CurlMultiHandle) &&
|
||||||
|
(($info_array = curl_multi_info_read($this->multiCurl)) !== false)
|
||||||
|
) {
|
||||||
if ($info_array['msg'] === CURLMSG_DONE) {
|
if ($info_array['msg'] === CURLMSG_DONE) {
|
||||||
foreach ($this->activeCurls as $key => $curl) {
|
foreach ($this->activeCurls as $key => $curl) {
|
||||||
if ($curl->curl === $info_array['handle']) {
|
if ($curl->curl === $info_array['handle']) {
|
||||||
@@ -782,8 +746,6 @@ class MultiCurl extends BaseCurl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop
|
* Stop
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
public function stop()
|
public function stop()
|
||||||
{
|
{
|
||||||
@@ -810,7 +772,6 @@ class MultiCurl extends BaseCurl
|
|||||||
*
|
*
|
||||||
* Remove extra header previously set using Curl::setHeader().
|
* Remove extra header previously set using Curl::setHeader().
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $key
|
* @param $key
|
||||||
*/
|
*/
|
||||||
public function unsetHeader($key)
|
public function unsetHeader($key)
|
||||||
@@ -820,8 +781,6 @@ class MultiCurl extends BaseCurl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set request time accuracy
|
* Set request time accuracy
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
public function setRequestTimeAccuracy()
|
public function setRequestTimeAccuracy()
|
||||||
{
|
{
|
||||||
@@ -830,8 +789,6 @@ class MultiCurl extends BaseCurl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Destruct
|
* Destruct
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
*/
|
||||||
public function __destruct()
|
public function __destruct()
|
||||||
{
|
{
|
||||||
@@ -840,8 +797,6 @@ class MultiCurl extends BaseCurl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update Headers
|
* Update Headers
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
*/
|
||||||
private function updateHeaders()
|
private function updateHeaders()
|
||||||
{
|
{
|
||||||
@@ -853,7 +808,6 @@ class MultiCurl extends BaseCurl
|
|||||||
/**
|
/**
|
||||||
* Queue Handle
|
* Queue Handle
|
||||||
*
|
*
|
||||||
* @access private
|
|
||||||
* @param $curl
|
* @param $curl
|
||||||
*/
|
*/
|
||||||
private function queueHandle($curl)
|
private function queueHandle($curl)
|
||||||
@@ -863,13 +817,15 @@ class MultiCurl extends BaseCurl
|
|||||||
$curl->childOfMultiCurl = true;
|
$curl->childOfMultiCurl = true;
|
||||||
$this->queuedCurls[$curl->id] = $curl;
|
$this->queuedCurls[$curl->id] = $curl;
|
||||||
|
|
||||||
|
// Avoid overwriting any existing header.
|
||||||
|
if ($curl->getOpt(CURLOPT_HTTPHEADER) === null) {
|
||||||
$curl->setHeaders($this->headers);
|
$curl->setHeaders($this->headers);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init Handle
|
* Init Handle
|
||||||
*
|
*
|
||||||
* @access private
|
|
||||||
* @param $curl
|
* @param $curl
|
||||||
* @throws \ErrorException
|
* @throws \ErrorException
|
||||||
*/
|
*/
|
||||||
@@ -888,6 +844,9 @@ class MultiCurl extends BaseCurl
|
|||||||
if ($curl->beforeSendCallback === null) {
|
if ($curl->beforeSendCallback === null) {
|
||||||
$curl->beforeSend($this->beforeSendCallback);
|
$curl->beforeSend($this->beforeSendCallback);
|
||||||
}
|
}
|
||||||
|
if ($curl->afterSendCallback === null) {
|
||||||
|
$curl->afterSend($this->afterSendCallback);
|
||||||
|
}
|
||||||
if ($curl->successCallback === null) {
|
if ($curl->successCallback === null) {
|
||||||
$curl->success($this->successCallback);
|
$curl->success($this->successCallback);
|
||||||
}
|
}
|
||||||
@@ -934,8 +893,6 @@ class MultiCurl extends BaseCurl
|
|||||||
*
|
*
|
||||||
* Checks if there is any available quota to make additional requests while
|
* Checks if there is any available quota to make additional requests while
|
||||||
* rate limiting is enabled.
|
* rate limiting is enabled.
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
*/
|
||||||
private function hasRequestQuota()
|
private function hasRequestQuota()
|
||||||
{
|
{
|
||||||
@@ -965,8 +922,6 @@ class MultiCurl extends BaseCurl
|
|||||||
* Wait Until Request Quota Available
|
* Wait Until Request Quota Available
|
||||||
*
|
*
|
||||||
* Waits until there is available request quota available based on the rate limit.
|
* Waits until there is available request quota available based on the rate limit.
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
*/
|
||||||
private function waitUntilRequestQuotaAvailable()
|
private function waitUntilRequestQuotaAvailable()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
@@ -43,10 +45,8 @@ class StringUtil
|
|||||||
/**
|
/**
|
||||||
* Return true when $haystack starts with $needle.
|
* Return true when $haystack starts with $needle.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $haystack
|
* @param $haystack
|
||||||
* @param $needle
|
* @param $needle
|
||||||
*
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function startsWith($haystack, $needle)
|
public static function startsWith($haystack, $needle)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Curl;
|
namespace Curl;
|
||||||
|
|
||||||
use Curl\StringUtil;
|
|
||||||
|
|
||||||
class Url
|
class Url
|
||||||
{
|
{
|
||||||
private $baseUrl = null;
|
private $baseUrl = null;
|
||||||
@@ -15,7 +15,7 @@ class Url
|
|||||||
$this->relativeUrl = $relative_url;
|
$this->relativeUrl = $relative_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString() : string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
return $this->absolutizeUrl();
|
return $this->absolutizeUrl();
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,8 @@ class Url
|
|||||||
* Remove dot segments.
|
* Remove dot segments.
|
||||||
*
|
*
|
||||||
* Interpret and remove the special "." and ".." path segments from a referenced path.
|
* Interpret and remove the special "." and ".." path segments from a referenced path.
|
||||||
|
*
|
||||||
|
* @param mixed $input
|
||||||
*/
|
*/
|
||||||
public static function removeDotSegments($input)
|
public static function removeDotSegments($input)
|
||||||
{
|
{
|
||||||
@@ -87,10 +89,8 @@ class Url
|
|||||||
/**
|
/**
|
||||||
* Build Url
|
* Build Url
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @param $url
|
* @param $url
|
||||||
* @param $mixed_data
|
* @param $mixed_data
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function buildUrl($url, $mixed_data = '')
|
public static function buildUrl($url, $mixed_data = '')
|
||||||
@@ -127,29 +127,29 @@ class Url
|
|||||||
$target = [];
|
$target = [];
|
||||||
if (isset($r['scheme'])) {
|
if (isset($r['scheme'])) {
|
||||||
$target['scheme'] = $r['scheme'];
|
$target['scheme'] = $r['scheme'];
|
||||||
$target['host'] = isset($r['host']) ? $r['host'] : null;
|
$target['host'] = $r['host'] ?? null;
|
||||||
$target['port'] = isset($r['port']) ? $r['port'] : null;
|
$target['port'] = $r['port'] ?? null;
|
||||||
$target['user'] = isset($r['user']) ? $r['user'] : null;
|
$target['user'] = $r['user'] ?? null;
|
||||||
$target['pass'] = isset($r['pass']) ? $r['pass'] : null;
|
$target['pass'] = $r['pass'] ?? null;
|
||||||
$target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null;
|
$target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null;
|
||||||
$target['query'] = isset($r['query']) ? $r['query'] : null;
|
$target['query'] = $r['query'] ?? null;
|
||||||
} else {
|
} else {
|
||||||
$target['scheme'] = isset($b['scheme']) ? $b['scheme'] : null;
|
$target['scheme'] = $b['scheme'] ?? null;
|
||||||
if ($r['authorized']) {
|
if ($r['authorized']) {
|
||||||
$target['host'] = isset($r['host']) ? $r['host'] : null;
|
$target['host'] = $r['host'] ?? null;
|
||||||
$target['port'] = isset($r['port']) ? $r['port'] : null;
|
$target['port'] = $r['port'] ?? null;
|
||||||
$target['user'] = isset($r['user']) ? $r['user'] : null;
|
$target['user'] = $r['user'] ?? null;
|
||||||
$target['pass'] = isset($r['pass']) ? $r['pass'] : null;
|
$target['pass'] = $r['pass'] ?? null;
|
||||||
$target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null;
|
$target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null;
|
||||||
$target['query'] = isset($r['query']) ? $r['query'] : null;
|
$target['query'] = $r['query'] ?? null;
|
||||||
} else {
|
} else {
|
||||||
$target['host'] = isset($b['host']) ? $b['host'] : null;
|
$target['host'] = $b['host'] ?? null;
|
||||||
$target['port'] = isset($b['port']) ? $b['port'] : null;
|
$target['port'] = $b['port'] ?? null;
|
||||||
$target['user'] = isset($b['user']) ? $b['user'] : null;
|
$target['user'] = $b['user'] ?? null;
|
||||||
$target['pass'] = isset($b['pass']) ? $b['pass'] : null;
|
$target['pass'] = $b['pass'] ?? null;
|
||||||
if (!isset($r['path']) || $r['path'] === '') {
|
if (!isset($r['path']) || $r['path'] === '') {
|
||||||
$target['path'] = $b['path'];
|
$target['path'] = $b['path'];
|
||||||
$target['query'] = isset($r['query']) ? $r['query'] : (isset($b['query']) ? $b['query'] : null);
|
$target['query'] = $r['query'] ?? $b['query'] ?? null;
|
||||||
} else {
|
} else {
|
||||||
if (StringUtil::startsWith($r['path'], '/')) {
|
if (StringUtil::startsWith($r['path'], '/')) {
|
||||||
$target['path'] = self::removeDotSegments($r['path']);
|
$target['path'] = self::removeDotSegments($r['path']);
|
||||||
@@ -160,14 +160,14 @@ class Url
|
|||||||
}
|
}
|
||||||
$target['path'] = self::removeDotSegments($base . '/' . $r['path']);
|
$target['path'] = self::removeDotSegments($base . '/' . $r['path']);
|
||||||
}
|
}
|
||||||
$target['query'] = isset($r['query']) ? $r['query'] : null;
|
$target['query'] = $r['query'] ?? null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->relativeUrl === '') {
|
if ($this->relativeUrl === '') {
|
||||||
$target['fragment'] = isset($b['fragment']) ? $b['fragment'] : null;
|
$target['fragment'] = $b['fragment'] ?? null;
|
||||||
} else {
|
} else {
|
||||||
$target['fragment'] = isset($r['fragment']) ? $r['fragment'] : null;
|
$target['fragment'] = $r['fragment'] ?? null;
|
||||||
}
|
}
|
||||||
$absolutized_url = $this->unparseUrl($target);
|
$absolutized_url = $this->unparseUrl($target);
|
||||||
return $absolutized_url;
|
return $absolutized_url;
|
||||||
@@ -177,6 +177,8 @@ class Url
|
|||||||
* Parse url.
|
* Parse url.
|
||||||
*
|
*
|
||||||
* Parse url into components of a URI as specified by RFC 3986.
|
* Parse url into components of a URI as specified by RFC 3986.
|
||||||
|
*
|
||||||
|
* @param mixed $url
|
||||||
*/
|
*/
|
||||||
public static function parseUrl($url)
|
public static function parseUrl($url)
|
||||||
{
|
{
|
||||||
@@ -192,6 +194,8 @@ class Url
|
|||||||
*
|
*
|
||||||
* Percent-encode characters to represent a data octet in a component when
|
* Percent-encode characters to represent a data octet in a component when
|
||||||
* that octet's corresponding character is outside the allowed set.
|
* that octet's corresponding character is outside the allowed set.
|
||||||
|
*
|
||||||
|
* @param mixed $chars
|
||||||
*/
|
*/
|
||||||
private static function percentEncodeChars($chars)
|
private static function percentEncodeChars($chars)
|
||||||
{
|
{
|
||||||
@@ -229,16 +233,18 @@ class Url
|
|||||||
* Unparse url.
|
* Unparse url.
|
||||||
*
|
*
|
||||||
* Combine url components into a url.
|
* Combine url components into a url.
|
||||||
|
*
|
||||||
|
* @param mixed $parsed_url
|
||||||
*/
|
*/
|
||||||
private function unparseUrl($parsed_url)
|
private function unparseUrl($parsed_url)
|
||||||
{
|
{
|
||||||
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
|
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
|
||||||
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
|
$user = $parsed_url['user'] ?? '';
|
||||||
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
|
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
|
||||||
$pass = ($user || $pass) ? $pass . '@' : '';
|
$pass = ($user || $pass) ? $pass . '@' : '';
|
||||||
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
|
$host = $parsed_url['host'] ?? '';
|
||||||
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
|
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
|
||||||
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
|
$path = $parsed_url['path'] ?? '';
|
||||||
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
|
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
|
||||||
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
|
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
|
||||||
$unparsed_url = $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
|
$unparsed_url = $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
|
||||||
|
|||||||
2
vendor/services.php
vendored
2
vendor/services.php
vendored
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// This file is automatically generated at:2025-04-18 12:45:16
|
// This file is automatically generated at:2025-04-18 15:07:05
|
||||||
declare (strict_types = 1);
|
declare (strict_types = 1);
|
||||||
return array (
|
return array (
|
||||||
0 => 'think\\trace\\Service',
|
0 => 'think\\trace\\Service',
|
||||||
|
|||||||
5
vendor/symfony/cache-contracts/CHANGELOG.md
vendored
Normal file
5
vendor/symfony/cache-contracts/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
The changelog is maintained for all Symfony contracts at the following URL:
|
||||||
|
https://github.com/symfony/contracts/blob/main/CHANGELOG.md
|
||||||
57
vendor/symfony/cache-contracts/CacheInterface.php
vendored
Normal file
57
vendor/symfony/cache-contracts/CacheInterface.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Contracts\Cache;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Covers most simple to advanced caching needs.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
interface CacheInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Fetches a value from the pool or computes it if not found.
|
||||||
|
*
|
||||||
|
* On cache misses, a callback is called that should return the missing value.
|
||||||
|
* This callback is given a PSR-6 CacheItemInterface instance corresponding to the
|
||||||
|
* requested key, that could be used e.g. for expiration control. It could also
|
||||||
|
* be an ItemInterface instance when its additional features are needed.
|
||||||
|
*
|
||||||
|
* @param string $key The key of the item to retrieve from the cache
|
||||||
|
* @param callable|CallbackInterface $callback Should return the computed value for the given key/item
|
||||||
|
* @param float|null $beta A float that, as it grows, controls the likeliness of triggering
|
||||||
|
* early expiration. 0 disables it, INF forces immediate expiration.
|
||||||
|
* The default (or providing null) is implementation dependent but should
|
||||||
|
* typically be 1.0, which should provide optimal stampede protection.
|
||||||
|
* See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration
|
||||||
|
* @param array &$metadata The metadata of the cached item {@see ItemInterface::getMetadata()}
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When $key is not valid or when $beta is negative
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item from the pool.
|
||||||
|
*
|
||||||
|
* @param string $key The key to delete
|
||||||
|
*
|
||||||
|
* @return bool True if the item was successfully removed, false if there was any error
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When $key is not valid
|
||||||
|
*/
|
||||||
|
public function delete(string $key): bool;
|
||||||
|
}
|
||||||
80
vendor/symfony/cache-contracts/CacheTrait.php
vendored
Normal file
80
vendor/symfony/cache-contracts/CacheTrait.php
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Contracts\Cache;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Psr\Cache\InvalidArgumentException;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
// Help opcache.preload discover always-needed symbols
|
||||||
|
class_exists(InvalidArgumentException::class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
trait CacheTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
return $this->doGet($this, $key, $callback, $beta, $metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(string $key): bool
|
||||||
|
{
|
||||||
|
return $this->deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function doGet(CacheItemPoolInterface $pool, string $key, callable $callback, ?float $beta, ?array &$metadata = null, ?LoggerInterface $logger = null)
|
||||||
|
{
|
||||||
|
if (0 > $beta = $beta ?? 1.0) {
|
||||||
|
throw new class(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)) extends \InvalidArgumentException implements InvalidArgumentException { };
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $pool->getItem($key);
|
||||||
|
$recompute = !$item->isHit() || \INF === $beta;
|
||||||
|
$metadata = $item instanceof ItemInterface ? $item->getMetadata() : [];
|
||||||
|
|
||||||
|
if (!$recompute && $metadata) {
|
||||||
|
$expiry = $metadata[ItemInterface::METADATA_EXPIRY] ?? false;
|
||||||
|
$ctime = $metadata[ItemInterface::METADATA_CTIME] ?? false;
|
||||||
|
|
||||||
|
if ($recompute = $ctime && $expiry && $expiry <= ($now = microtime(true)) - $ctime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) {
|
||||||
|
// force applying defaultLifetime to expiry
|
||||||
|
$item->expiresAt(null);
|
||||||
|
$logger && $logger->info('Item "{key}" elected for early recomputation {delta}s before its expiration', [
|
||||||
|
'key' => $key,
|
||||||
|
'delta' => sprintf('%.1f', $expiry - $now),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recompute) {
|
||||||
|
$save = true;
|
||||||
|
$item->set($callback($item, $save));
|
||||||
|
if ($save) {
|
||||||
|
$pool->save($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
30
vendor/symfony/cache-contracts/CallbackInterface.php
vendored
Normal file
30
vendor/symfony/cache-contracts/CallbackInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Contracts\Cache;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns the cached value of an item.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
interface CallbackInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param CacheItemInterface|ItemInterface $item The item to compute the value for
|
||||||
|
* @param bool &$save Should be set to false when the value should not be saved in the pool
|
||||||
|
*
|
||||||
|
* @return mixed The computed value for the passed item
|
||||||
|
*/
|
||||||
|
public function __invoke(CacheItemInterface $item, bool &$save);
|
||||||
|
}
|
||||||
65
vendor/symfony/cache-contracts/ItemInterface.php
vendored
Normal file
65
vendor/symfony/cache-contracts/ItemInterface.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Contracts\Cache;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheException;
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Augments PSR-6's CacheItemInterface with support for tags and metadata.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
interface ItemInterface extends CacheItemInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* References the Unix timestamp stating when the item will expire.
|
||||||
|
*/
|
||||||
|
public const METADATA_EXPIRY = 'expiry';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* References the time the item took to be created, in milliseconds.
|
||||||
|
*/
|
||||||
|
public const METADATA_CTIME = 'ctime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* References the list of tags that were assigned to the item, as string[].
|
||||||
|
*/
|
||||||
|
public const METADATA_TAGS = 'tags';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserved characters that cannot be used in a key or tag.
|
||||||
|
*/
|
||||||
|
public const RESERVED_CHARACTERS = '{}()/\@:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a tag to a cache item.
|
||||||
|
*
|
||||||
|
* Tags are strings that follow the same validation rules as keys.
|
||||||
|
*
|
||||||
|
* @param string|string[] $tags A tag or array of tags
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When $tag is not valid
|
||||||
|
* @throws CacheException When the item comes from a pool that is not tag-aware
|
||||||
|
*/
|
||||||
|
public function tag($tags): self;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of metadata info that were saved alongside with the cached value.
|
||||||
|
*
|
||||||
|
* See ItemInterface::METADATA_* consts for keys potentially found in the returned array.
|
||||||
|
*/
|
||||||
|
public function getMetadata(): array;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2015-2019 Fabien Potencier
|
Copyright (c) 2018-present Fabien Potencier
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
9
vendor/symfony/cache-contracts/README.md
vendored
Normal file
9
vendor/symfony/cache-contracts/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Symfony Cache Contracts
|
||||||
|
=======================
|
||||||
|
|
||||||
|
A set of abstractions extracted out of the Symfony components.
|
||||||
|
|
||||||
|
Can be used to build on semantics that the Symfony components proved useful - and
|
||||||
|
that already have battle tested implementations.
|
||||||
|
|
||||||
|
See https://github.com/symfony/contracts/blob/main/README.md for more information.
|
||||||
38
vendor/symfony/cache-contracts/TagAwareCacheInterface.php
vendored
Normal file
38
vendor/symfony/cache-contracts/TagAwareCacheInterface.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Contracts\Cache;
|
||||||
|
|
||||||
|
use Psr\Cache\InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows invalidating cached items using tags.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
interface TagAwareCacheInterface extends CacheInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Invalidates cached items using tags.
|
||||||
|
*
|
||||||
|
* When implemented on a PSR-6 pool, invalidation should not apply
|
||||||
|
* to deferred items. Instead, they should be committed as usual.
|
||||||
|
* This allows replacing old tagged values by new ones without
|
||||||
|
* race conditions.
|
||||||
|
*
|
||||||
|
* @param string[] $tags An array of tags to invalidate
|
||||||
|
*
|
||||||
|
* @return bool True on success
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When $tags is not valid
|
||||||
|
*/
|
||||||
|
public function invalidateTags(array $tags);
|
||||||
|
}
|
||||||
38
vendor/symfony/cache-contracts/composer.json
vendored
Normal file
38
vendor/symfony/cache-contracts/composer.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "symfony/cache-contracts",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Generic abstractions related to caching",
|
||||||
|
"keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2.5",
|
||||||
|
"psr/cache": "^1.0|^2.0|^3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/cache-implementation": ""
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": { "Symfony\\Contracts\\Cache\\": "" }
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "2.5-dev"
|
||||||
|
},
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/contracts",
|
||||||
|
"url": "https://github.com/symfony/contracts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
208
vendor/symfony/cache/Adapter/AbstractAdapter.php
vendored
Normal file
208
vendor/symfony/cache/Adapter/AbstractAdapter.php
vendored
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerAwareInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\AbstractAdapterTrait;
|
||||||
|
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
abstract class AbstractAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
use AbstractAdapterTrait;
|
||||||
|
use ContractsTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected const NS_SEPARATOR = ':';
|
||||||
|
|
||||||
|
private static $apcuSupported;
|
||||||
|
private static $phpFilesSupported;
|
||||||
|
|
||||||
|
protected function __construct(string $namespace = '', int $defaultLifetime = 0)
|
||||||
|
{
|
||||||
|
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).static::NS_SEPARATOR;
|
||||||
|
$this->defaultLifetime = $defaultLifetime;
|
||||||
|
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
|
||||||
|
}
|
||||||
|
self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
|
||||||
|
static function ($key, $value, $isHit) {
|
||||||
|
$item = new CacheItem();
|
||||||
|
$item->key = $key;
|
||||||
|
$item->value = $v = $value;
|
||||||
|
$item->isHit = $isHit;
|
||||||
|
// Detect wrapped values that encode for their expiry and creation duration
|
||||||
|
// For compactness, these values are packed in the key of an array using
|
||||||
|
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
|
||||||
|
if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9D" === $k[0] && "\0" === $k[5] && "\x5F" === $k[9]) {
|
||||||
|
$item->value = $v[$k];
|
||||||
|
$v = unpack('Ve/Nc', substr($k, 1, -1));
|
||||||
|
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
|
||||||
|
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
self::$mergeByLifetime ?? self::$mergeByLifetime = \Closure::bind(
|
||||||
|
static function ($deferred, $namespace, &$expiredIds, $getId, $defaultLifetime) {
|
||||||
|
$byLifetime = [];
|
||||||
|
$now = microtime(true);
|
||||||
|
$expiredIds = [];
|
||||||
|
|
||||||
|
foreach ($deferred as $key => $item) {
|
||||||
|
$key = (string) $key;
|
||||||
|
if (null === $item->expiry) {
|
||||||
|
$ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
|
||||||
|
} elseif (!$item->expiry) {
|
||||||
|
$ttl = 0;
|
||||||
|
} elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) {
|
||||||
|
$expiredIds[] = $getId($key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) {
|
||||||
|
unset($metadata[CacheItem::METADATA_TAGS]);
|
||||||
|
}
|
||||||
|
// For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators
|
||||||
|
$byLifetime[$ttl][$getId($key)] = $metadata ? ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item->value] : $item->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $byLifetime;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the best possible adapter that your runtime supports.
|
||||||
|
*
|
||||||
|
* Using ApcuAdapter makes system caches compatible with read-only filesystems.
|
||||||
|
*
|
||||||
|
* @return AdapterInterface
|
||||||
|
*/
|
||||||
|
public static function createSystemCache(string $namespace, int $defaultLifetime, string $version, string $directory, ?LoggerInterface $logger = null)
|
||||||
|
{
|
||||||
|
$opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, true);
|
||||||
|
if (null !== $logger) {
|
||||||
|
$opcache->setLogger($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self::$apcuSupported = self::$apcuSupported ?? ApcuAdapter::isSupported()) {
|
||||||
|
return $opcache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
|
||||||
|
return $opcache;
|
||||||
|
}
|
||||||
|
|
||||||
|
$apcu = new ApcuAdapter($namespace, intdiv($defaultLifetime, 5), $version);
|
||||||
|
if (null !== $logger) {
|
||||||
|
$apcu->setLogger($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ChainAdapter([$apcu, $opcache]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createConnection(string $dsn, array $options = [])
|
||||||
|
{
|
||||||
|
if (str_starts_with($dsn, 'redis:') || str_starts_with($dsn, 'rediss:')) {
|
||||||
|
return RedisAdapter::createConnection($dsn, $options);
|
||||||
|
}
|
||||||
|
if (str_starts_with($dsn, 'memcached:')) {
|
||||||
|
return MemcachedAdapter::createConnection($dsn, $options);
|
||||||
|
}
|
||||||
|
if (0 === strpos($dsn, 'couchbase:')) {
|
||||||
|
if (CouchbaseBucketAdapter::isSupported()) {
|
||||||
|
return CouchbaseBucketAdapter::createConnection($dsn, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CouchbaseCollectionAdapter::createConnection($dsn, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException('Unsupported DSN: it does not start with "redis[s]:", "memcached:" nor "couchbase:".');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
$byLifetime = (self::$mergeByLifetime)($this->deferred, $this->namespace, $expiredIds, \Closure::fromCallable([$this, 'getId']), $this->defaultLifetime);
|
||||||
|
$retry = $this->deferred = [];
|
||||||
|
|
||||||
|
if ($expiredIds) {
|
||||||
|
try {
|
||||||
|
$this->doDelete($expiredIds);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$ok = false;
|
||||||
|
CacheItem::log($this->logger, 'Failed to delete expired items: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($byLifetime as $lifetime => $values) {
|
||||||
|
try {
|
||||||
|
$e = $this->doSave($values, $lifetime);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
if (true === $e || [] === $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (\is_array($e) || 1 === \count($values)) {
|
||||||
|
foreach (\is_array($e) ? $e : array_keys($values) as $id) {
|
||||||
|
$ok = false;
|
||||||
|
$v = $values[$id];
|
||||||
|
$type = get_debug_type($v);
|
||||||
|
$message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
|
||||||
|
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($values as $id => $v) {
|
||||||
|
$retry[$lifetime][] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When bulk-save failed, retry each item individually
|
||||||
|
foreach ($retry as $lifetime => $ids) {
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
try {
|
||||||
|
$v = $byLifetime[$lifetime][$id];
|
||||||
|
$e = $this->doSave([$id => $v], $lifetime);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
if (true === $e || [] === $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ok = false;
|
||||||
|
$type = get_debug_type($v);
|
||||||
|
$message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
|
||||||
|
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
330
vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php
vendored
Normal file
330
vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerAwareInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\AbstractAdapterTrait;
|
||||||
|
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||||
|
use Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract for native TagAware adapters.
|
||||||
|
*
|
||||||
|
* To keep info on tags, the tags are both serialized as part of cache value and provided as tag ids
|
||||||
|
* to Adapters on operations when needed for storage to doSave(), doDelete() & doInvalidate().
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
* @author André Rømcke <andre.romcke+symfony@gmail.com>
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, LoggerAwareInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
use AbstractAdapterTrait;
|
||||||
|
use ContractsTrait;
|
||||||
|
|
||||||
|
private const TAGS_PREFIX = "\0tags\0";
|
||||||
|
|
||||||
|
protected function __construct(string $namespace = '', int $defaultLifetime = 0)
|
||||||
|
{
|
||||||
|
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
|
||||||
|
$this->defaultLifetime = $defaultLifetime;
|
||||||
|
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
|
||||||
|
}
|
||||||
|
self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
|
||||||
|
static function ($key, $value, $isHit) {
|
||||||
|
$item = new CacheItem();
|
||||||
|
$item->key = $key;
|
||||||
|
$item->isTaggable = true;
|
||||||
|
// If structure does not match what we expect return item as is (no value and not a hit)
|
||||||
|
if (!\is_array($value) || !\array_key_exists('value', $value)) {
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
$item->isHit = $isHit;
|
||||||
|
// Extract value, tags and meta data from the cache value
|
||||||
|
$item->value = $value['value'];
|
||||||
|
$item->metadata[CacheItem::METADATA_TAGS] = $value['tags'] ?? [];
|
||||||
|
if (isset($value['meta'])) {
|
||||||
|
// For compactness these values are packed, & expiry is offset to reduce size
|
||||||
|
$v = unpack('Ve/Nc', $value['meta']);
|
||||||
|
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
|
||||||
|
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
self::$mergeByLifetime ?? self::$mergeByLifetime = \Closure::bind(
|
||||||
|
static function ($deferred, &$expiredIds, $getId, $tagPrefix, $defaultLifetime) {
|
||||||
|
$byLifetime = [];
|
||||||
|
$now = microtime(true);
|
||||||
|
$expiredIds = [];
|
||||||
|
|
||||||
|
foreach ($deferred as $key => $item) {
|
||||||
|
$key = (string) $key;
|
||||||
|
if (null === $item->expiry) {
|
||||||
|
$ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
|
||||||
|
} elseif (!$item->expiry) {
|
||||||
|
$ttl = 0;
|
||||||
|
} elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) {
|
||||||
|
$expiredIds[] = $getId($key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Store Value and Tags on the cache value
|
||||||
|
if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) {
|
||||||
|
$value = ['value' => $item->value, 'tags' => $metadata[CacheItem::METADATA_TAGS]];
|
||||||
|
unset($metadata[CacheItem::METADATA_TAGS]);
|
||||||
|
} else {
|
||||||
|
$value = ['value' => $item->value, 'tags' => []];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($metadata) {
|
||||||
|
// For compactness, expiry and creation duration are packed, using magic numbers as separators
|
||||||
|
$value['meta'] = pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract tag changes, these should be removed from values in doSave()
|
||||||
|
$value['tag-operations'] = ['add' => [], 'remove' => []];
|
||||||
|
$oldTags = $item->metadata[CacheItem::METADATA_TAGS] ?? [];
|
||||||
|
foreach (array_diff($value['tags'], $oldTags) as $addedTag) {
|
||||||
|
$value['tag-operations']['add'][] = $getId($tagPrefix.$addedTag);
|
||||||
|
}
|
||||||
|
foreach (array_diff($oldTags, $value['tags']) as $removedTag) {
|
||||||
|
$value['tag-operations']['remove'][] = $getId($tagPrefix.$removedTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
$byLifetime[$ttl][$getId($key)] = $value;
|
||||||
|
$item->metadata = $item->newMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $byLifetime;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists several cache items immediately.
|
||||||
|
*
|
||||||
|
* @param array $values The values to cache, indexed by their cache identifier
|
||||||
|
* @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
|
||||||
|
* @param array[] $addTagData Hash where key is tag id, and array value is list of cache id's to add to tag
|
||||||
|
* @param array[] $removeTagData Hash where key is tag id, and array value is list of cache id's to remove to tag
|
||||||
|
*
|
||||||
|
* @return array The identifiers that failed to be cached or a boolean stating if caching succeeded or not
|
||||||
|
*/
|
||||||
|
abstract protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes multiple items from the pool and their corresponding tags.
|
||||||
|
*
|
||||||
|
* @param array $ids An array of identifiers that should be removed from the pool
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
abstract protected function doDelete(array $ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes relations between tags and deleted items.
|
||||||
|
*
|
||||||
|
* @param array $tagData Array of tag => key identifiers that should be removed from the pool
|
||||||
|
*/
|
||||||
|
abstract protected function doDeleteTagRelations(array $tagData): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates cached items using tags.
|
||||||
|
*
|
||||||
|
* @param string[] $tagIds An array of tags to invalidate, key is tag and value is tag id
|
||||||
|
*/
|
||||||
|
abstract protected function doInvalidate(array $tagIds): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete items and yields the tags they were bound to.
|
||||||
|
*/
|
||||||
|
protected function doDeleteYieldTags(array $ids): iterable
|
||||||
|
{
|
||||||
|
foreach ($this->doFetch($ids) as $id => $value) {
|
||||||
|
yield $id => \is_array($value) && \is_array($value['tags'] ?? null) ? $value['tags'] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->doDelete($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function commit(): bool
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
$byLifetime = (self::$mergeByLifetime)($this->deferred, $expiredIds, \Closure::fromCallable([$this, 'getId']), self::TAGS_PREFIX, $this->defaultLifetime);
|
||||||
|
$retry = $this->deferred = [];
|
||||||
|
|
||||||
|
if ($expiredIds) {
|
||||||
|
// Tags are not cleaned up in this case, however that is done on invalidateTags().
|
||||||
|
try {
|
||||||
|
$this->doDelete($expiredIds);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$ok = false;
|
||||||
|
CacheItem::log($this->logger, 'Failed to delete expired items: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($byLifetime as $lifetime => $values) {
|
||||||
|
try {
|
||||||
|
$values = $this->extractTagData($values, $addTagData, $removeTagData);
|
||||||
|
$e = $this->doSave($values, $lifetime, $addTagData, $removeTagData);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
if (true === $e || [] === $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (\is_array($e) || 1 === \count($values)) {
|
||||||
|
foreach (\is_array($e) ? $e : array_keys($values) as $id) {
|
||||||
|
$ok = false;
|
||||||
|
$v = $values[$id];
|
||||||
|
$type = get_debug_type($v);
|
||||||
|
$message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
|
||||||
|
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($values as $id => $v) {
|
||||||
|
$retry[$lifetime][] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When bulk-save failed, retry each item individually
|
||||||
|
foreach ($retry as $lifetime => $ids) {
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
try {
|
||||||
|
$v = $byLifetime[$lifetime][$id];
|
||||||
|
$values = $this->extractTagData([$id => $v], $addTagData, $removeTagData);
|
||||||
|
$e = $this->doSave($values, $lifetime, $addTagData, $removeTagData);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
if (true === $e || [] === $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ok = false;
|
||||||
|
$type = get_debug_type($v);
|
||||||
|
$message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
|
||||||
|
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys): bool
|
||||||
|
{
|
||||||
|
if (!$keys) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ok = true;
|
||||||
|
$ids = [];
|
||||||
|
$tagData = [];
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$ids[$key] = $this->getId($key);
|
||||||
|
unset($this->deferred[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($this->doDeleteYieldTags(array_values($ids)) as $id => $tags) {
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
$tagData[$this->getId(self::TAGS_PREFIX.$tag)][] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ((!$tagData || $this->doDeleteTagRelations($tagData)) && $ok) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// When bulk-delete failed, retry each item individually
|
||||||
|
foreach ($ids as $key => $id) {
|
||||||
|
try {
|
||||||
|
$e = null;
|
||||||
|
if ($this->doDelete([$id])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
}
|
||||||
|
$message = 'Failed to delete key "{key}"'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
|
||||||
|
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
$ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function invalidateTags(array $tags)
|
||||||
|
{
|
||||||
|
if (empty($tags)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagIds = [];
|
||||||
|
foreach (array_unique($tags) as $tag) {
|
||||||
|
$tagIds[] = $this->getId(self::TAGS_PREFIX.$tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($this->doInvalidate($tagIds)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
CacheItem::log($this->logger, 'Failed to invalidate tags: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts tags operation data from $values set in mergeByLifetime, and returns values without it.
|
||||||
|
*/
|
||||||
|
private function extractTagData(array $values, ?array &$addTagData, ?array &$removeTagData): array
|
||||||
|
{
|
||||||
|
$addTagData = $removeTagData = [];
|
||||||
|
foreach ($values as $id => $value) {
|
||||||
|
foreach ($value['tag-operations']['add'] as $tag => $tagId) {
|
||||||
|
$addTagData[$tagId][] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($value['tag-operations']['remove'] as $tag => $tagId) {
|
||||||
|
$removeTagData[$tagId][] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($values[$id]['tag-operations']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
vendor/symfony/cache/Adapter/AdapterInterface.php
vendored
Normal file
47
vendor/symfony/cache/Adapter/AdapterInterface.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
|
||||||
|
// Help opcache.preload discover always-needed symbols
|
||||||
|
class_exists(CacheItem::class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for adapters managing instances of Symfony's CacheItem.
|
||||||
|
*
|
||||||
|
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||||
|
*/
|
||||||
|
interface AdapterInterface extends CacheItemPoolInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return CacheItem
|
||||||
|
*/
|
||||||
|
public function getItem($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return \Traversable<string, CacheItem>
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '');
|
||||||
|
}
|
||||||
138
vendor/symfony/cache/Adapter/ApcuAdapter.php
vendored
Normal file
138
vendor/symfony/cache/Adapter/ApcuAdapter.php
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Exception\CacheException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class ApcuAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
private $marshaller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws CacheException if APCu is not enabled
|
||||||
|
*/
|
||||||
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $version = null, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('APCu is not enabled.');
|
||||||
|
}
|
||||||
|
if ('cli' === \PHP_SAPI) {
|
||||||
|
ini_set('apc.use_request_time', 0);
|
||||||
|
}
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
|
||||||
|
if (null !== $version) {
|
||||||
|
CacheItem::validateKey($version);
|
||||||
|
|
||||||
|
if (!apcu_exists($version.'@'.$namespace)) {
|
||||||
|
$this->doClear($namespace);
|
||||||
|
apcu_add($version.'@'.$namespace, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->marshaller = $marshaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSupported()
|
||||||
|
{
|
||||||
|
return \function_exists('apcu_fetch') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
|
||||||
|
try {
|
||||||
|
$values = [];
|
||||||
|
$ids = array_flip($ids);
|
||||||
|
foreach (apcu_fetch(array_keys($ids), $ok) ?: [] as $k => $v) {
|
||||||
|
if (!isset($ids[$k])) {
|
||||||
|
// work around https://github.com/krakjoe/apcu/issues/247
|
||||||
|
$k = key($ids);
|
||||||
|
}
|
||||||
|
unset($ids[$k]);
|
||||||
|
|
||||||
|
if (null !== $v || $ok) {
|
||||||
|
$values[$k] = null !== $this->marshaller ? $this->marshaller->unmarshall($v) : $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
} catch (\Error $e) {
|
||||||
|
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||||
|
} finally {
|
||||||
|
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id)
|
||||||
|
{
|
||||||
|
return apcu_exists($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace)
|
||||||
|
{
|
||||||
|
return isset($namespace[0]) && class_exists(\APCUIterator::class, false) && ('cli' !== \PHP_SAPI || filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN))
|
||||||
|
? apcu_delete(new \APCUIterator(sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY))
|
||||||
|
: apcu_clear_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
apcu_delete($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
if (null !== $this->marshaller && (!$values = $this->marshaller->marshall($values, $failed))) {
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (false === $failures = apcu_store($values, null, $lifetime)) {
|
||||||
|
$failures = $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys($failures);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
if (1 === \count($values)) {
|
||||||
|
// Workaround https://github.com/krakjoe/apcu/issues/170
|
||||||
|
apcu_delete(array_key_first($values));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
407
vendor/symfony/cache/Adapter/ArrayAdapter.php
vendored
Normal file
407
vendor/symfony/cache/Adapter/ArrayAdapter.php
vendored
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Log\LoggerAwareInterface;
|
||||||
|
use Psr\Log\LoggerAwareTrait;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An in-memory cache storage.
|
||||||
|
*
|
||||||
|
* Acts as a least-recently-used (LRU) storage when configured with a maximum number of items.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
use LoggerAwareTrait;
|
||||||
|
|
||||||
|
private $storeSerialized;
|
||||||
|
private $values = [];
|
||||||
|
private $expiries = [];
|
||||||
|
private $defaultLifetime;
|
||||||
|
private $maxLifetime;
|
||||||
|
private $maxItems;
|
||||||
|
|
||||||
|
private static $createCacheItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
|
||||||
|
*/
|
||||||
|
public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true, float $maxLifetime = 0, int $maxItems = 0)
|
||||||
|
{
|
||||||
|
if (0 > $maxLifetime) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Argument $maxLifetime must be positive, %F passed.', $maxLifetime));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 > $maxItems) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Argument $maxItems must be a positive integer, %d passed.', $maxItems));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->defaultLifetime = $defaultLifetime;
|
||||||
|
$this->storeSerialized = $storeSerialized;
|
||||||
|
$this->maxLifetime = $maxLifetime;
|
||||||
|
$this->maxItems = $maxItems;
|
||||||
|
self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
|
||||||
|
static function ($key, $value, $isHit) {
|
||||||
|
$item = new CacheItem();
|
||||||
|
$item->key = $key;
|
||||||
|
$item->value = $value;
|
||||||
|
$item->isHit = $isHit;
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
$item = $this->getItem($key);
|
||||||
|
$metadata = $item->getMetadata();
|
||||||
|
|
||||||
|
// ArrayAdapter works in memory, we don't care about stampede protection
|
||||||
|
if (\INF === $beta || !$item->isHit()) {
|
||||||
|
$save = true;
|
||||||
|
$item->set($callback($item, $save));
|
||||||
|
if ($save) {
|
||||||
|
$this->save($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(string $key): bool
|
||||||
|
{
|
||||||
|
return $this->deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
|
||||||
|
if ($this->maxItems) {
|
||||||
|
// Move the item last in the storage
|
||||||
|
$value = $this->values[$key];
|
||||||
|
unset($this->values[$key]);
|
||||||
|
$this->values[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
\assert('' !== CacheItem::validateKey($key));
|
||||||
|
|
||||||
|
return isset($this->expiries[$key]) && !$this->deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
if (!$isHit = $this->hasItem($key)) {
|
||||||
|
$value = null;
|
||||||
|
|
||||||
|
if (!$this->maxItems) {
|
||||||
|
// Track misses in non-LRU mode only
|
||||||
|
$this->values[$key] = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (self::$createCacheItem)($key, $value, $isHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
\assert(self::validateKeys($keys));
|
||||||
|
|
||||||
|
return $this->generateItems($keys, microtime(true), self::$createCacheItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
\assert('' !== CacheItem::validateKey($key));
|
||||||
|
unset($this->values[$key], $this->expiries[$key]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$this->deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
if (!$item instanceof CacheItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$item = (array) $item;
|
||||||
|
$key = $item["\0*\0key"];
|
||||||
|
$value = $item["\0*\0value"];
|
||||||
|
$expiry = $item["\0*\0expiry"];
|
||||||
|
|
||||||
|
$now = microtime(true);
|
||||||
|
|
||||||
|
if (null !== $expiry) {
|
||||||
|
if (!$expiry) {
|
||||||
|
$expiry = \PHP_INT_MAX;
|
||||||
|
} elseif ($expiry <= $now) {
|
||||||
|
$this->deleteItem($key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null === $expiry && 0 < $this->defaultLifetime) {
|
||||||
|
$expiry = $this->defaultLifetime;
|
||||||
|
$expiry = $now + ($expiry > ($this->maxLifetime ?: $expiry) ? $this->maxLifetime : $expiry);
|
||||||
|
} elseif ($this->maxLifetime && (null === $expiry || $expiry > $now + $this->maxLifetime)) {
|
||||||
|
$expiry = $now + $this->maxLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->maxItems) {
|
||||||
|
unset($this->values[$key]);
|
||||||
|
|
||||||
|
// Iterate items and vacuum expired ones while we are at it
|
||||||
|
foreach ($this->values as $k => $v) {
|
||||||
|
if ($this->expiries[$k] > $now && \count($this->values) < $this->maxItems) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->values[$k], $this->expiries[$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->values[$key] = $value;
|
||||||
|
$this->expiries[$key] = $expiry ?? \PHP_INT_MAX;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
return $this->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
if ('' !== $prefix) {
|
||||||
|
$now = microtime(true);
|
||||||
|
|
||||||
|
foreach ($this->values as $key => $value) {
|
||||||
|
if (!isset($this->expiries[$key]) || $this->expiries[$key] <= $now || 0 === strpos($key, $prefix)) {
|
||||||
|
unset($this->values[$key], $this->expiries[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->values) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->values = $this->expiries = [];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all cached values, with cache miss as null.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getValues()
|
||||||
|
{
|
||||||
|
if (!$this->storeSerialized) {
|
||||||
|
return $this->values;
|
||||||
|
}
|
||||||
|
|
||||||
|
$values = $this->values;
|
||||||
|
foreach ($values as $k => $v) {
|
||||||
|
if (null === $v || 'N;' === $v) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
|
||||||
|
$values[$k] = serialize($v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
$this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateItems(array $keys, float $now, \Closure $f): \Generator
|
||||||
|
{
|
||||||
|
foreach ($keys as $i => $key) {
|
||||||
|
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) {
|
||||||
|
$value = null;
|
||||||
|
|
||||||
|
if (!$this->maxItems) {
|
||||||
|
// Track misses in non-LRU mode only
|
||||||
|
$this->values[$key] = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->maxItems) {
|
||||||
|
// Move the item last in the storage
|
||||||
|
$value = $this->values[$key];
|
||||||
|
unset($this->values[$key]);
|
||||||
|
$this->values[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
|
||||||
|
}
|
||||||
|
unset($keys[$i]);
|
||||||
|
|
||||||
|
yield $key => $f($key, $value, $isHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
yield $key => $f($key, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function freeze($value, string $key)
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return 'N;';
|
||||||
|
}
|
||||||
|
if (\is_string($value)) {
|
||||||
|
// Serialize strings if they could be confused with serialized objects or arrays
|
||||||
|
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
||||||
|
return serialize($value);
|
||||||
|
}
|
||||||
|
} elseif (!\is_scalar($value)) {
|
||||||
|
try {
|
||||||
|
$serialized = serialize($value);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
unset($this->values[$key]);
|
||||||
|
$type = get_debug_type($value);
|
||||||
|
$message = sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage());
|
||||||
|
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Keep value serialized if it contains any objects or any internal references
|
||||||
|
if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
|
||||||
|
return $serialized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function unfreeze(string $key, bool &$isHit)
|
||||||
|
{
|
||||||
|
if ('N;' === $value = $this->values[$key]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
|
try {
|
||||||
|
$value = unserialize($value);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
CacheItem::log($this->logger, 'Failed to unserialize key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => get_debug_type($this)]);
|
||||||
|
$value = false;
|
||||||
|
}
|
||||||
|
if (false === $value) {
|
||||||
|
$value = null;
|
||||||
|
$isHit = false;
|
||||||
|
|
||||||
|
if (!$this->maxItems) {
|
||||||
|
$this->values[$key] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateKeys(array $keys): bool
|
||||||
|
{
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (!\is_string($key) || !isset($this->expiries[$key])) {
|
||||||
|
CacheItem::validateKey($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
342
vendor/symfony/cache/Adapter/ChainAdapter.php
vendored
Normal file
342
vendor/symfony/cache/Adapter/ChainAdapter.php
vendored
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
use Symfony\Contracts\Service\ResetInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chains several adapters together.
|
||||||
|
*
|
||||||
|
* Cached items are fetched from the first adapter having them in its data store.
|
||||||
|
* They are saved and deleted in all adapters at once.
|
||||||
|
*
|
||||||
|
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||||
|
*/
|
||||||
|
class ChainAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
use ContractsTrait;
|
||||||
|
|
||||||
|
private $adapters = [];
|
||||||
|
private $adapterCount;
|
||||||
|
private $defaultLifetime;
|
||||||
|
|
||||||
|
private static $syncItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items
|
||||||
|
* @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones
|
||||||
|
*/
|
||||||
|
public function __construct(array $adapters, int $defaultLifetime = 0)
|
||||||
|
{
|
||||||
|
if (!$adapters) {
|
||||||
|
throw new InvalidArgumentException('At least one adapter must be specified.');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($adapters as $adapter) {
|
||||||
|
if (!$adapter instanceof CacheItemPoolInterface) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', get_debug_type($adapter), CacheItemPoolInterface::class));
|
||||||
|
}
|
||||||
|
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $adapter instanceof ApcuAdapter && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
|
||||||
|
continue; // skip putting APCu in the chain when the backend is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($adapter instanceof AdapterInterface) {
|
||||||
|
$this->adapters[] = $adapter;
|
||||||
|
} else {
|
||||||
|
$this->adapters[] = new ProxyAdapter($adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->adapterCount = \count($this->adapters);
|
||||||
|
$this->defaultLifetime = $defaultLifetime;
|
||||||
|
|
||||||
|
self::$syncItem ?? self::$syncItem = \Closure::bind(
|
||||||
|
static function ($sourceItem, $item, $defaultLifetime, $sourceMetadata = null) {
|
||||||
|
$sourceItem->isTaggable = false;
|
||||||
|
$sourceMetadata = $sourceMetadata ?? $sourceItem->metadata;
|
||||||
|
unset($sourceMetadata[CacheItem::METADATA_TAGS]);
|
||||||
|
|
||||||
|
$item->value = $sourceItem->value;
|
||||||
|
$item->isHit = $sourceItem->isHit;
|
||||||
|
$item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata;
|
||||||
|
|
||||||
|
if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) {
|
||||||
|
$item->expiresAt(\DateTime::createFromFormat('U.u', sprintf('%.6F', $item->metadata[CacheItem::METADATA_EXPIRY])));
|
||||||
|
} elseif (0 < $defaultLifetime) {
|
||||||
|
$item->expiresAfter($defaultLifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
$doSave = true;
|
||||||
|
$callback = static function (CacheItem $item, bool &$save) use ($callback, &$doSave) {
|
||||||
|
$value = $callback($item, $save);
|
||||||
|
$doSave = $save;
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
};
|
||||||
|
|
||||||
|
$lastItem = null;
|
||||||
|
$i = 0;
|
||||||
|
$wrap = function (?CacheItem $item = null, bool &$save = true) use ($key, $callback, $beta, &$wrap, &$i, &$doSave, &$lastItem, &$metadata) {
|
||||||
|
$adapter = $this->adapters[$i];
|
||||||
|
if (isset($this->adapters[++$i])) {
|
||||||
|
$callback = $wrap;
|
||||||
|
$beta = \INF === $beta ? \INF : 0;
|
||||||
|
}
|
||||||
|
if ($adapter instanceof CacheInterface) {
|
||||||
|
$value = $adapter->get($key, $callback, $beta, $metadata);
|
||||||
|
} else {
|
||||||
|
$value = $this->doGet($adapter, $key, $callback, $beta, $metadata);
|
||||||
|
}
|
||||||
|
if (null !== $item) {
|
||||||
|
(self::$syncItem)($lastItem = $lastItem ?? $item, $item, $this->defaultLifetime, $metadata);
|
||||||
|
}
|
||||||
|
$save = $doSave;
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
};
|
||||||
|
|
||||||
|
return $wrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
$syncItem = self::$syncItem;
|
||||||
|
$misses = [];
|
||||||
|
|
||||||
|
foreach ($this->adapters as $i => $adapter) {
|
||||||
|
$item = $adapter->getItem($key);
|
||||||
|
|
||||||
|
if ($item->isHit()) {
|
||||||
|
while (0 <= --$i) {
|
||||||
|
$this->adapters[$i]->save($syncItem($item, $misses[$i], $this->defaultLifetime));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
$misses[$i] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
return $this->generateItems($this->adapters[0]->getItems($keys), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateItems(iterable $items, int $adapterIndex): \Generator
|
||||||
|
{
|
||||||
|
$missing = [];
|
||||||
|
$misses = [];
|
||||||
|
$nextAdapterIndex = $adapterIndex + 1;
|
||||||
|
$nextAdapter = $this->adapters[$nextAdapterIndex] ?? null;
|
||||||
|
|
||||||
|
foreach ($items as $k => $item) {
|
||||||
|
if (!$nextAdapter || $item->isHit()) {
|
||||||
|
yield $k => $item;
|
||||||
|
} else {
|
||||||
|
$missing[] = $k;
|
||||||
|
$misses[$k] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($missing) {
|
||||||
|
$syncItem = self::$syncItem;
|
||||||
|
$adapter = $this->adapters[$adapterIndex];
|
||||||
|
$items = $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex);
|
||||||
|
|
||||||
|
foreach ($items as $k => $item) {
|
||||||
|
if ($item->isHit()) {
|
||||||
|
$adapter->save($syncItem($item, $misses[$k], $this->defaultLifetime));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $k => $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
foreach ($this->adapters as $adapter) {
|
||||||
|
if ($adapter->hasItem($key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
$cleared = true;
|
||||||
|
$i = $this->adapterCount;
|
||||||
|
|
||||||
|
while ($i--) {
|
||||||
|
if ($this->adapters[$i] instanceof AdapterInterface) {
|
||||||
|
$cleared = $this->adapters[$i]->clear($prefix) && $cleared;
|
||||||
|
} else {
|
||||||
|
$cleared = $this->adapters[$i]->clear() && $cleared;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cleared;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
$deleted = true;
|
||||||
|
$i = $this->adapterCount;
|
||||||
|
|
||||||
|
while ($i--) {
|
||||||
|
$deleted = $this->adapters[$i]->deleteItem($key) && $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
$deleted = true;
|
||||||
|
$i = $this->adapterCount;
|
||||||
|
|
||||||
|
while ($i--) {
|
||||||
|
$deleted = $this->adapters[$i]->deleteItems($keys) && $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
$saved = true;
|
||||||
|
$i = $this->adapterCount;
|
||||||
|
|
||||||
|
while ($i--) {
|
||||||
|
$saved = $this->adapters[$i]->save($item) && $saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
$saved = true;
|
||||||
|
$i = $this->adapterCount;
|
||||||
|
|
||||||
|
while ($i--) {
|
||||||
|
$saved = $this->adapters[$i]->saveDeferred($item) && $saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
$committed = true;
|
||||||
|
$i = $this->adapterCount;
|
||||||
|
|
||||||
|
while ($i--) {
|
||||||
|
$committed = $this->adapters[$i]->commit() && $committed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $committed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function prune()
|
||||||
|
{
|
||||||
|
$pruned = true;
|
||||||
|
|
||||||
|
foreach ($this->adapters as $adapter) {
|
||||||
|
if ($adapter instanceof PruneableInterface) {
|
||||||
|
$pruned = $adapter->prune() && $pruned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pruned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
foreach ($this->adapters as $adapter) {
|
||||||
|
if ($adapter instanceof ResetInterface) {
|
||||||
|
$adapter->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
252
vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php
vendored
Normal file
252
vendor/symfony/cache/Adapter/CouchbaseBucketAdapter.php
vendored
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Exception\CacheException;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Antonio Jose Cerezo Aranda <aj.cerezo@gmail.com>
|
||||||
|
*/
|
||||||
|
class CouchbaseBucketAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
private const THIRTY_DAYS_IN_SECONDS = 2592000;
|
||||||
|
private const MAX_KEY_LENGTH = 250;
|
||||||
|
private const KEY_NOT_FOUND = 13;
|
||||||
|
private const VALID_DSN_OPTIONS = [
|
||||||
|
'operationTimeout',
|
||||||
|
'configTimeout',
|
||||||
|
'configNodeTimeout',
|
||||||
|
'n1qlTimeout',
|
||||||
|
'httpTimeout',
|
||||||
|
'configDelay',
|
||||||
|
'htconfigIdleTimeout',
|
||||||
|
'durabilityInterval',
|
||||||
|
'durabilityTimeout',
|
||||||
|
];
|
||||||
|
|
||||||
|
private $bucket;
|
||||||
|
private $marshaller;
|
||||||
|
|
||||||
|
public function __construct(\CouchbaseBucket $bucket, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->maxIdLength = static::MAX_KEY_LENGTH;
|
||||||
|
|
||||||
|
$this->bucket = $bucket;
|
||||||
|
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
$this->enableVersioning();
|
||||||
|
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|string $servers
|
||||||
|
*/
|
||||||
|
public static function createConnection($servers, array $options = []): \CouchbaseBucket
|
||||||
|
{
|
||||||
|
if (\is_string($servers)) {
|
||||||
|
$servers = [$servers];
|
||||||
|
} elseif (!\is_array($servers)) {
|
||||||
|
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be array or string, "%s" given.', __METHOD__, get_debug_type($servers)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
|
||||||
|
|
||||||
|
$dsnPattern = '/^(?<protocol>couchbase(?:s)?)\:\/\/(?:(?<username>[^\:]+)\:(?<password>[^\@]{6,})@)?'
|
||||||
|
.'(?<host>[^\:]+(?:\:\d+)?)(?:\/(?<bucketName>[^\?]+))(?:\?(?<options>.*))?$/i';
|
||||||
|
|
||||||
|
$newServers = [];
|
||||||
|
$protocol = 'couchbase';
|
||||||
|
try {
|
||||||
|
$options = self::initOptions($options);
|
||||||
|
$username = $options['username'];
|
||||||
|
$password = $options['password'];
|
||||||
|
|
||||||
|
foreach ($servers as $dsn) {
|
||||||
|
if (0 !== strpos($dsn, 'couchbase:')) {
|
||||||
|
throw new InvalidArgumentException('Invalid Couchbase DSN: it does not start with "couchbase:".');
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match($dsnPattern, $dsn, $matches);
|
||||||
|
|
||||||
|
$username = $matches['username'] ?: $username;
|
||||||
|
$password = $matches['password'] ?: $password;
|
||||||
|
$protocol = $matches['protocol'] ?: $protocol;
|
||||||
|
|
||||||
|
if (isset($matches['options'])) {
|
||||||
|
$optionsInDsn = self::getOptions($matches['options']);
|
||||||
|
|
||||||
|
foreach ($optionsInDsn as $parameter => $value) {
|
||||||
|
$options[$parameter] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$newServers[] = $matches['host'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$connectionString = $protocol.'://'.implode(',', $newServers);
|
||||||
|
|
||||||
|
$client = new \CouchbaseCluster($connectionString);
|
||||||
|
$client->authenticateAs($username, $password);
|
||||||
|
|
||||||
|
$bucket = $client->openBucket($matches['bucketName']);
|
||||||
|
|
||||||
|
unset($options['username'], $options['password']);
|
||||||
|
foreach ($options as $option => $value) {
|
||||||
|
if (!empty($value)) {
|
||||||
|
$bucket->$option = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $bucket;
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSupported(): bool
|
||||||
|
{
|
||||||
|
return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '2.6.0', '>=') && version_compare(phpversion('couchbase'), '3.0', '<');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getOptions(string $options): array
|
||||||
|
{
|
||||||
|
$results = [];
|
||||||
|
$optionsInArray = explode('&', $options);
|
||||||
|
|
||||||
|
foreach ($optionsInArray as $option) {
|
||||||
|
[$key, $value] = explode('=', $option);
|
||||||
|
|
||||||
|
if (\in_array($key, static::VALID_DSN_OPTIONS, true)) {
|
||||||
|
$results[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function initOptions(array $options): array
|
||||||
|
{
|
||||||
|
$options['username'] = $options['username'] ?? '';
|
||||||
|
$options['password'] = $options['password'] ?? '';
|
||||||
|
$options['operationTimeout'] = $options['operationTimeout'] ?? 0;
|
||||||
|
$options['configTimeout'] = $options['configTimeout'] ?? 0;
|
||||||
|
$options['configNodeTimeout'] = $options['configNodeTimeout'] ?? 0;
|
||||||
|
$options['n1qlTimeout'] = $options['n1qlTimeout'] ?? 0;
|
||||||
|
$options['httpTimeout'] = $options['httpTimeout'] ?? 0;
|
||||||
|
$options['configDelay'] = $options['configDelay'] ?? 0;
|
||||||
|
$options['htconfigIdleTimeout'] = $options['htconfigIdleTimeout'] ?? 0;
|
||||||
|
$options['durabilityInterval'] = $options['durabilityInterval'] ?? 0;
|
||||||
|
$options['durabilityTimeout'] = $options['durabilityTimeout'] ?? 0;
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
$resultsCouchbase = $this->bucket->get($ids);
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($resultsCouchbase as $key => $value) {
|
||||||
|
if (null !== $value->error) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$results[$key] = $this->marshaller->unmarshall($value->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id): bool
|
||||||
|
{
|
||||||
|
return false !== $this->bucket->get($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace): bool
|
||||||
|
{
|
||||||
|
if ('' === $namespace) {
|
||||||
|
$this->bucket->manager()->flush();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids): bool
|
||||||
|
{
|
||||||
|
$results = $this->bucket->remove(array_values($ids));
|
||||||
|
|
||||||
|
foreach ($results as $key => $result) {
|
||||||
|
if (null !== $result->error && static::KEY_NOT_FOUND !== $result->error->getCode()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unset($results[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0 === \count($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
if (!$values = $this->marshaller->marshall($values, $failed)) {
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lifetime = $this->normalizeExpiry($lifetime);
|
||||||
|
|
||||||
|
$ko = [];
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
$result = $this->bucket->upsert($key, $value, ['expiry' => $lifetime]);
|
||||||
|
|
||||||
|
if (null !== $result->error) {
|
||||||
|
$ko[$key] = $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [] === $ko ? true : $ko;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeExpiry(int $expiry): int
|
||||||
|
{
|
||||||
|
if ($expiry && $expiry > static::THIRTY_DAYS_IN_SECONDS) {
|
||||||
|
$expiry += time();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $expiry;
|
||||||
|
}
|
||||||
|
}
|
||||||
222
vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php
vendored
Normal file
222
vendor/symfony/cache/Adapter/CouchbaseCollectionAdapter.php
vendored
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Couchbase\Bucket;
|
||||||
|
use Couchbase\Cluster;
|
||||||
|
use Couchbase\ClusterOptions;
|
||||||
|
use Couchbase\Collection;
|
||||||
|
use Couchbase\DocumentNotFoundException;
|
||||||
|
use Couchbase\UpsertOptions;
|
||||||
|
use Symfony\Component\Cache\Exception\CacheException;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Antonio Jose Cerezo Aranda <aj.cerezo@gmail.com>
|
||||||
|
*/
|
||||||
|
class CouchbaseCollectionAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
private const MAX_KEY_LENGTH = 250;
|
||||||
|
|
||||||
|
/** @var Collection */
|
||||||
|
private $connection;
|
||||||
|
private $marshaller;
|
||||||
|
|
||||||
|
public function __construct(Collection $connection, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->maxIdLength = static::MAX_KEY_LENGTH;
|
||||||
|
|
||||||
|
$this->connection = $connection;
|
||||||
|
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
$this->enableVersioning();
|
||||||
|
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|string $dsn
|
||||||
|
*
|
||||||
|
* @return Bucket|Collection
|
||||||
|
*/
|
||||||
|
public static function createConnection($dsn, array $options = [])
|
||||||
|
{
|
||||||
|
if (\is_string($dsn)) {
|
||||||
|
$dsn = [$dsn];
|
||||||
|
} elseif (!\is_array($dsn)) {
|
||||||
|
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be array or string, "%s" given.', __METHOD__, get_debug_type($dsn)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error_handler(function ($type, $msg, $file, $line): bool { throw new \ErrorException($msg, 0, $type, $file, $line); });
|
||||||
|
|
||||||
|
$dsnPattern = '/^(?<protocol>couchbase(?:s)?)\:\/\/(?:(?<username>[^\:]+)\:(?<password>[^\@]{6,})@)?'
|
||||||
|
.'(?<host>[^\:]+(?:\:\d+)?)(?:\/(?<bucketName>[^\/\?]+))(?:(?:\/(?<scopeName>[^\/]+))'
|
||||||
|
.'(?:\/(?<collectionName>[^\/\?]+)))?(?:\/)?(?:\?(?<options>.*))?$/i';
|
||||||
|
|
||||||
|
$newServers = [];
|
||||||
|
$protocol = 'couchbase';
|
||||||
|
try {
|
||||||
|
$username = $options['username'] ?? '';
|
||||||
|
$password = $options['password'] ?? '';
|
||||||
|
|
||||||
|
foreach ($dsn as $server) {
|
||||||
|
if (0 !== strpos($server, 'couchbase:')) {
|
||||||
|
throw new InvalidArgumentException('Invalid Couchbase DSN: it does not start with "couchbase:".');
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match($dsnPattern, $server, $matches);
|
||||||
|
|
||||||
|
$username = $matches['username'] ?: $username;
|
||||||
|
$password = $matches['password'] ?: $password;
|
||||||
|
$protocol = $matches['protocol'] ?: $protocol;
|
||||||
|
|
||||||
|
if (isset($matches['options'])) {
|
||||||
|
$optionsInDsn = self::getOptions($matches['options']);
|
||||||
|
|
||||||
|
foreach ($optionsInDsn as $parameter => $value) {
|
||||||
|
$options[$parameter] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$newServers[] = $matches['host'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$option = isset($matches['options']) ? '?'.$matches['options'] : '';
|
||||||
|
$connectionString = $protocol.'://'.implode(',', $newServers).$option;
|
||||||
|
|
||||||
|
$clusterOptions = new ClusterOptions();
|
||||||
|
$clusterOptions->credentials($username, $password);
|
||||||
|
|
||||||
|
$client = new Cluster($connectionString, $clusterOptions);
|
||||||
|
|
||||||
|
$bucket = $client->bucket($matches['bucketName']);
|
||||||
|
$collection = $bucket->defaultCollection();
|
||||||
|
if (!empty($matches['scopeName'])) {
|
||||||
|
$scope = $bucket->scope($matches['scopeName']);
|
||||||
|
$collection = $scope->collection($matches['collectionName']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSupported(): bool
|
||||||
|
{
|
||||||
|
return \extension_loaded('couchbase') && version_compare(phpversion('couchbase'), '3.0.5', '>=') && version_compare(phpversion('couchbase'), '4.0', '<');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getOptions(string $options): array
|
||||||
|
{
|
||||||
|
$results = [];
|
||||||
|
$optionsInArray = explode('&', $options);
|
||||||
|
|
||||||
|
foreach ($optionsInArray as $option) {
|
||||||
|
[$key, $value] = explode('=', $option);
|
||||||
|
|
||||||
|
$results[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids): array
|
||||||
|
{
|
||||||
|
$results = [];
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
try {
|
||||||
|
$resultCouchbase = $this->connection->get($id);
|
||||||
|
} catch (DocumentNotFoundException $exception) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = $resultCouchbase->value ?? $resultCouchbase->content();
|
||||||
|
|
||||||
|
$results[$id] = $this->marshaller->unmarshall($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave($id): bool
|
||||||
|
{
|
||||||
|
return $this->connection->exists($id)->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear($namespace): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids): bool
|
||||||
|
{
|
||||||
|
$idsErrors = [];
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
try {
|
||||||
|
$result = $this->connection->remove($id);
|
||||||
|
|
||||||
|
if (null === $result->mutationToken()) {
|
||||||
|
$idsErrors[] = $id;
|
||||||
|
}
|
||||||
|
} catch (DocumentNotFoundException $exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0 === \count($idsErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, $lifetime)
|
||||||
|
{
|
||||||
|
if (!$values = $this->marshaller->marshall($values, $failed)) {
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$upsertOptions = new UpsertOptions();
|
||||||
|
$upsertOptions->expiry($lifetime);
|
||||||
|
|
||||||
|
$ko = [];
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
try {
|
||||||
|
$this->connection->upsert($key, $value, $upsertOptions);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
$ko[$key] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [] === $ko ? true : $ko;
|
||||||
|
}
|
||||||
|
}
|
||||||
110
vendor/symfony/cache/Adapter/DoctrineAdapter.php
vendored
Normal file
110
vendor/symfony/cache/Adapter/DoctrineAdapter.php
vendored
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Doctrine\Common\Cache\CacheProvider;
|
||||||
|
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*
|
||||||
|
* @deprecated Since Symfony 5.4, use Doctrine\Common\Cache\Psr6\CacheAdapter instead
|
||||||
|
*/
|
||||||
|
class DoctrineAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
private $provider;
|
||||||
|
|
||||||
|
public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
|
||||||
|
{
|
||||||
|
trigger_deprecation('symfony/cache', '5.4', '"%s" is deprecated, use "%s" instead.', __CLASS__, CacheAdapter::class);
|
||||||
|
|
||||||
|
parent::__construct('', $defaultLifetime);
|
||||||
|
$this->provider = $provider;
|
||||||
|
$provider->setNamespace($namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
parent::reset();
|
||||||
|
$this->provider->setNamespace($this->provider->getNamespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
$unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class.'::handleUnserializeCallback');
|
||||||
|
try {
|
||||||
|
return $this->provider->fetchMultiple($ids);
|
||||||
|
} catch (\Error $e) {
|
||||||
|
$trace = $e->getTrace();
|
||||||
|
|
||||||
|
if (isset($trace[0]['function']) && !isset($trace[0]['class'])) {
|
||||||
|
switch ($trace[0]['function']) {
|
||||||
|
case 'unserialize':
|
||||||
|
case 'apcu_fetch':
|
||||||
|
case 'apc_fetch':
|
||||||
|
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id)
|
||||||
|
{
|
||||||
|
return $this->provider->contains($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace)
|
||||||
|
{
|
||||||
|
$namespace = $this->provider->getNamespace();
|
||||||
|
|
||||||
|
return isset($namespace[0])
|
||||||
|
? $this->provider->deleteAll()
|
||||||
|
: $this->provider->flushAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$ok = $this->provider->delete($id) && $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
return $this->provider->saveMultiple($values, $lifetime);
|
||||||
|
}
|
||||||
|
}
|
||||||
448
vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php
vendored
Normal file
448
vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php
vendored
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\ArrayParameterType;
|
||||||
|
use Doctrine\DBAL\Configuration;
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
||||||
|
use Doctrine\DBAL\DriverManager;
|
||||||
|
use Doctrine\DBAL\Exception as DBALException;
|
||||||
|
use Doctrine\DBAL\Exception\TableNotFoundException;
|
||||||
|
use Doctrine\DBAL\ParameterType;
|
||||||
|
use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\DBAL\ServerVersionProvider;
|
||||||
|
use Doctrine\DBAL\Tools\DsnParser;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
|
||||||
|
class DoctrineDbalAdapter extends AbstractAdapter implements PruneableInterface
|
||||||
|
{
|
||||||
|
protected $maxIdLength = 255;
|
||||||
|
|
||||||
|
private $marshaller;
|
||||||
|
private $conn;
|
||||||
|
private $platformName;
|
||||||
|
private $serverVersion;
|
||||||
|
private $table = 'cache_items';
|
||||||
|
private $idCol = 'item_id';
|
||||||
|
private $dataCol = 'item_data';
|
||||||
|
private $lifetimeCol = 'item_lifetime';
|
||||||
|
private $timeCol = 'item_time';
|
||||||
|
private $namespace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can either pass an existing database Doctrine DBAL Connection or
|
||||||
|
* a DSN string that will be used to connect to the database.
|
||||||
|
*
|
||||||
|
* The cache table is created automatically when possible.
|
||||||
|
* Otherwise, use the createTable() method.
|
||||||
|
*
|
||||||
|
* List of available options:
|
||||||
|
* * db_table: The name of the table [default: cache_items]
|
||||||
|
* * db_id_col: The column where to store the cache id [default: item_id]
|
||||||
|
* * db_data_col: The column where to store the cache data [default: item_data]
|
||||||
|
* * db_lifetime_col: The column where to store the lifetime [default: item_lifetime]
|
||||||
|
* * db_time_col: The column where to store the timestamp [default: item_time]
|
||||||
|
*
|
||||||
|
* @param Connection|string $connOrDsn
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When namespace contains invalid characters
|
||||||
|
*/
|
||||||
|
public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($connOrDsn instanceof Connection) {
|
||||||
|
$this->conn = $connOrDsn;
|
||||||
|
} elseif (\is_string($connOrDsn)) {
|
||||||
|
if (!class_exists(DriverManager::class)) {
|
||||||
|
throw new InvalidArgumentException('Failed to parse DSN. Try running "composer require doctrine/dbal".');
|
||||||
|
}
|
||||||
|
if (class_exists(DsnParser::class)) {
|
||||||
|
$params = (new DsnParser([
|
||||||
|
'db2' => 'ibm_db2',
|
||||||
|
'mssql' => 'pdo_sqlsrv',
|
||||||
|
'mysql' => 'pdo_mysql',
|
||||||
|
'mysql2' => 'pdo_mysql',
|
||||||
|
'postgres' => 'pdo_pgsql',
|
||||||
|
'postgresql' => 'pdo_pgsql',
|
||||||
|
'pgsql' => 'pdo_pgsql',
|
||||||
|
'sqlite' => 'pdo_sqlite',
|
||||||
|
'sqlite3' => 'pdo_sqlite',
|
||||||
|
]))->parse($connOrDsn);
|
||||||
|
} else {
|
||||||
|
$params = ['url' => $connOrDsn];
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = new Configuration();
|
||||||
|
if (class_exists(DefaultSchemaManagerFactory::class)) {
|
||||||
|
$config->setSchemaManagerFactory(new DefaultSchemaManagerFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->conn = DriverManager::getConnection($params, $config);
|
||||||
|
} else {
|
||||||
|
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be "%s" or string, "%s" given.', __METHOD__, Connection::class, get_debug_type($connOrDsn)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->table = $options['db_table'] ?? $this->table;
|
||||||
|
$this->idCol = $options['db_id_col'] ?? $this->idCol;
|
||||||
|
$this->dataCol = $options['db_data_col'] ?? $this->dataCol;
|
||||||
|
$this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol;
|
||||||
|
$this->timeCol = $options['db_time_col'] ?? $this->timeCol;
|
||||||
|
$this->namespace = $namespace;
|
||||||
|
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||||
|
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the table to store cache items which can be called once for setup.
|
||||||
|
*
|
||||||
|
* Cache ID are saved in a column of maximum length 255. Cache data is
|
||||||
|
* saved in a BLOB.
|
||||||
|
*
|
||||||
|
* @throws DBALException When the table already exists
|
||||||
|
*/
|
||||||
|
public function createTable()
|
||||||
|
{
|
||||||
|
$schema = new Schema();
|
||||||
|
$this->addTableToSchema($schema);
|
||||||
|
|
||||||
|
foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) {
|
||||||
|
$this->conn->executeStatement($sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureSchema(Schema $schema, Connection $forConnection): void
|
||||||
|
{
|
||||||
|
// only update the schema for this connection
|
||||||
|
if ($forConnection !== $this->conn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($schema->hasTable($this->table)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addTableToSchema($schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function prune(): bool
|
||||||
|
{
|
||||||
|
$deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ?";
|
||||||
|
$params = [time()];
|
||||||
|
$paramTypes = [ParameterType::INTEGER];
|
||||||
|
|
||||||
|
if ('' !== $this->namespace) {
|
||||||
|
$deleteSql .= " AND $this->idCol LIKE ?";
|
||||||
|
$params[] = sprintf('%s%%', $this->namespace);
|
||||||
|
$paramTypes[] = ParameterType::STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->conn->executeStatement($deleteSql, $params, $paramTypes);
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids): iterable
|
||||||
|
{
|
||||||
|
$now = time();
|
||||||
|
$expired = [];
|
||||||
|
|
||||||
|
$sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN (?)";
|
||||||
|
$result = $this->conn->executeQuery($sql, [
|
||||||
|
$now,
|
||||||
|
$ids,
|
||||||
|
], [
|
||||||
|
ParameterType::INTEGER,
|
||||||
|
class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY,
|
||||||
|
])->iterateNumeric();
|
||||||
|
|
||||||
|
foreach ($result as $row) {
|
||||||
|
if (null === $row[1]) {
|
||||||
|
$expired[] = $row[0];
|
||||||
|
} else {
|
||||||
|
yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expired) {
|
||||||
|
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN (?)";
|
||||||
|
$this->conn->executeStatement($sql, [
|
||||||
|
$now,
|
||||||
|
$expired,
|
||||||
|
], [
|
||||||
|
ParameterType::INTEGER,
|
||||||
|
class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id): bool
|
||||||
|
{
|
||||||
|
$sql = "SELECT 1 FROM $this->table WHERE $this->idCol = ? AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ?)";
|
||||||
|
$result = $this->conn->executeQuery($sql, [
|
||||||
|
$id,
|
||||||
|
time(),
|
||||||
|
], [
|
||||||
|
ParameterType::STRING,
|
||||||
|
ParameterType::INTEGER,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (bool) $result->fetchOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace): bool
|
||||||
|
{
|
||||||
|
if ('' === $namespace) {
|
||||||
|
if ('sqlite' === $this->getPlatformName()) {
|
||||||
|
$sql = "DELETE FROM $this->table";
|
||||||
|
} else {
|
||||||
|
$sql = "TRUNCATE TABLE $this->table";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->conn->executeStatement($sql);
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids): bool
|
||||||
|
{
|
||||||
|
$sql = "DELETE FROM $this->table WHERE $this->idCol IN (?)";
|
||||||
|
try {
|
||||||
|
$this->conn->executeStatement($sql, [array_values($ids)], [class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY]);
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
if (!$values = $this->marshaller->marshall($values, $failed)) {
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$platformName = $this->getPlatformName();
|
||||||
|
$insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?)";
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case 'mysql' === $platformName:
|
||||||
|
$sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
|
||||||
|
break;
|
||||||
|
case 'oci' === $platformName:
|
||||||
|
// DUAL is Oracle specific dummy table
|
||||||
|
$sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ".
|
||||||
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
|
||||||
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?";
|
||||||
|
break;
|
||||||
|
case 'sqlsrv' === $platformName && version_compare($this->getServerVersion(), '10', '>='):
|
||||||
|
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
|
||||||
|
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
|
||||||
|
$sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
|
||||||
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
|
||||||
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
|
||||||
|
break;
|
||||||
|
case 'sqlite' === $platformName:
|
||||||
|
$sql = 'INSERT OR REPLACE'.substr($insertSql, 6);
|
||||||
|
break;
|
||||||
|
case 'pgsql' === $platformName && version_compare($this->getServerVersion(), '9.5', '>='):
|
||||||
|
$sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$platformName = null;
|
||||||
|
$sql = "UPDATE $this->table SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ? WHERE $this->idCol = ?";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
$lifetime = $lifetime ?: null;
|
||||||
|
try {
|
||||||
|
$stmt = $this->conn->prepare($sql);
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
|
||||||
|
$this->createTable();
|
||||||
|
}
|
||||||
|
$stmt = $this->conn->prepare($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('sqlsrv' === $platformName || 'oci' === $platformName) {
|
||||||
|
$bind = static function ($id, $data) use ($stmt) {
|
||||||
|
$stmt->bindValue(1, $id);
|
||||||
|
$stmt->bindValue(2, $id);
|
||||||
|
$stmt->bindValue(3, $data, ParameterType::LARGE_OBJECT);
|
||||||
|
$stmt->bindValue(6, $data, ParameterType::LARGE_OBJECT);
|
||||||
|
};
|
||||||
|
$stmt->bindValue(4, $lifetime, ParameterType::INTEGER);
|
||||||
|
$stmt->bindValue(5, $now, ParameterType::INTEGER);
|
||||||
|
$stmt->bindValue(7, $lifetime, ParameterType::INTEGER);
|
||||||
|
$stmt->bindValue(8, $now, ParameterType::INTEGER);
|
||||||
|
} elseif (null !== $platformName) {
|
||||||
|
$bind = static function ($id, $data) use ($stmt) {
|
||||||
|
$stmt->bindValue(1, $id);
|
||||||
|
$stmt->bindValue(2, $data, ParameterType::LARGE_OBJECT);
|
||||||
|
};
|
||||||
|
$stmt->bindValue(3, $lifetime, ParameterType::INTEGER);
|
||||||
|
$stmt->bindValue(4, $now, ParameterType::INTEGER);
|
||||||
|
} else {
|
||||||
|
$stmt->bindValue(2, $lifetime, ParameterType::INTEGER);
|
||||||
|
$stmt->bindValue(3, $now, ParameterType::INTEGER);
|
||||||
|
|
||||||
|
$insertStmt = $this->conn->prepare($insertSql);
|
||||||
|
$insertStmt->bindValue(3, $lifetime, ParameterType::INTEGER);
|
||||||
|
$insertStmt->bindValue(4, $now, ParameterType::INTEGER);
|
||||||
|
|
||||||
|
$bind = static function ($id, $data) use ($stmt, $insertStmt) {
|
||||||
|
$stmt->bindValue(1, $data, ParameterType::LARGE_OBJECT);
|
||||||
|
$stmt->bindValue(4, $id);
|
||||||
|
$insertStmt->bindValue(1, $id);
|
||||||
|
$insertStmt->bindValue(2, $data, ParameterType::LARGE_OBJECT);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($values as $id => $data) {
|
||||||
|
$bind($id, $data);
|
||||||
|
try {
|
||||||
|
$rowCount = $stmt->executeStatement();
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
|
||||||
|
$this->createTable();
|
||||||
|
}
|
||||||
|
$rowCount = $stmt->executeStatement();
|
||||||
|
}
|
||||||
|
if (null === $platformName && 0 === $rowCount) {
|
||||||
|
try {
|
||||||
|
$insertStmt->executeStatement();
|
||||||
|
} catch (DBALException $e) {
|
||||||
|
// A concurrent write won, let it be
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getId($key)
|
||||||
|
{
|
||||||
|
if ('pgsql' !== $this->getPlatformName()) {
|
||||||
|
return parent::getId($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_contains($key, "\0") || str_contains($key, '%') || !preg_match('//u', $key)) {
|
||||||
|
$key = rawurlencode($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getId($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPlatformName(): string
|
||||||
|
{
|
||||||
|
if (isset($this->platformName)) {
|
||||||
|
return $this->platformName;
|
||||||
|
}
|
||||||
|
|
||||||
|
$platform = $this->conn->getDatabasePlatform();
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\MySQLPlatform:
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\MySQL57Platform:
|
||||||
|
return $this->platformName = 'mysql';
|
||||||
|
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform:
|
||||||
|
return $this->platformName = 'sqlite';
|
||||||
|
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform:
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQL94Platform:
|
||||||
|
return $this->platformName = 'pgsql';
|
||||||
|
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\OraclePlatform:
|
||||||
|
return $this->platformName = 'oci';
|
||||||
|
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform:
|
||||||
|
case $platform instanceof \Doctrine\DBAL\Platforms\SQLServer2012Platform:
|
||||||
|
return $this->platformName = 'sqlsrv';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return $this->platformName = \get_class($platform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getServerVersion(): string
|
||||||
|
{
|
||||||
|
if (isset($this->serverVersion)) {
|
||||||
|
return $this->serverVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->conn instanceof ServerVersionProvider || $this->conn instanceof ServerInfoAwareConnection) {
|
||||||
|
return $this->serverVersion = $this->conn->getServerVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The condition should be removed once support for DBAL <3.3 is dropped
|
||||||
|
$conn = method_exists($this->conn, 'getNativeConnection') ? $this->conn->getNativeConnection() : $this->conn->getWrappedConnection();
|
||||||
|
|
||||||
|
return $this->serverVersion = $conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addTableToSchema(Schema $schema): void
|
||||||
|
{
|
||||||
|
$types = [
|
||||||
|
'mysql' => 'binary',
|
||||||
|
'sqlite' => 'text',
|
||||||
|
];
|
||||||
|
|
||||||
|
$table = $schema->createTable($this->table);
|
||||||
|
$table->addColumn($this->idCol, $types[$this->getPlatformName()] ?? 'string', ['length' => 255]);
|
||||||
|
$table->addColumn($this->dataCol, 'blob', ['length' => 16777215]);
|
||||||
|
$table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]);
|
||||||
|
$table->addColumn($this->timeCol, 'integer', ['unsigned' => true]);
|
||||||
|
$table->setPrimaryKey([$this->idCol]);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
vendor/symfony/cache/Adapter/FilesystemAdapter.php
vendored
Normal file
29
vendor/symfony/cache/Adapter/FilesystemAdapter.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\FilesystemTrait;
|
||||||
|
|
||||||
|
class FilesystemAdapter extends AbstractAdapter implements PruneableInterface
|
||||||
|
{
|
||||||
|
use FilesystemTrait;
|
||||||
|
|
||||||
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $directory = null, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||||
|
parent::__construct('', $defaultLifetime);
|
||||||
|
$this->init($namespace, $directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
239
vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php
vendored
Normal file
239
vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\FilesystemTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores tag id <> cache id relationship as a symlink, and lookup on invalidation calls.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
* @author André Rømcke <andre.romcke+symfony@gmail.com>
|
||||||
|
*/
|
||||||
|
class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface
|
||||||
|
{
|
||||||
|
use FilesystemTrait {
|
||||||
|
doClear as private doClearCache;
|
||||||
|
doSave as private doSaveCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Folder used for tag symlinks.
|
||||||
|
*/
|
||||||
|
private const TAG_FOLDER = 'tags';
|
||||||
|
|
||||||
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $directory = null, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
$this->marshaller = new TagAwareMarshaller($marshaller);
|
||||||
|
parent::__construct('', $defaultLifetime);
|
||||||
|
$this->init($namespace, $directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace)
|
||||||
|
{
|
||||||
|
$ok = $this->doClearCache($namespace);
|
||||||
|
|
||||||
|
if ('' !== $namespace) {
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error_handler(static function () {});
|
||||||
|
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($this->scanHashDir($this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR) as $dir) {
|
||||||
|
if (rename($dir, $renamed = substr_replace($dir, bin2hex(random_bytes(4)), -8))) {
|
||||||
|
$dir = $renamed.\DIRECTORY_SEPARATOR;
|
||||||
|
} else {
|
||||||
|
$dir .= \DIRECTORY_SEPARATOR;
|
||||||
|
$renamed = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i < 38; ++$i) {
|
||||||
|
if (!is_dir($dir.$chars[$i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for ($j = 0; $j < 38; ++$j) {
|
||||||
|
if (!is_dir($d = $dir.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach (scandir($d, \SCANDIR_SORT_NONE) ?: [] as $link) {
|
||||||
|
if ('.' !== $link && '..' !== $link && (null !== $renamed || !realpath($d.\DIRECTORY_SEPARATOR.$link))) {
|
||||||
|
unlink($d.\DIRECTORY_SEPARATOR.$link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null === $renamed ?: rmdir($d);
|
||||||
|
}
|
||||||
|
null === $renamed ?: rmdir($dir.$chars[$i]);
|
||||||
|
}
|
||||||
|
null === $renamed ?: rmdir($renamed);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array
|
||||||
|
{
|
||||||
|
$failed = $this->doSaveCache($values, $lifetime);
|
||||||
|
|
||||||
|
// Add Tags as symlinks
|
||||||
|
foreach ($addTagData as $tagId => $ids) {
|
||||||
|
$tagFolder = $this->getTagFolder($tagId);
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if ($failed && \in_array($id, $failed, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->getFile($id);
|
||||||
|
|
||||||
|
if (!@symlink($file, $tagLink = $this->getFile($id, true, $tagFolder)) && !is_link($tagLink)) {
|
||||||
|
@unlink($file);
|
||||||
|
$failed[] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink removed Tags
|
||||||
|
foreach ($removeTagData as $tagId => $ids) {
|
||||||
|
$tagFolder = $this->getTagFolder($tagId);
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if ($failed && \in_array($id, $failed, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@unlink($this->getFile($id, false, $tagFolder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDeleteYieldTags(array $ids): iterable
|
||||||
|
{
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$file = $this->getFile($id);
|
||||||
|
if (!is_file($file) || !$h = @fopen($file, 'r')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((\PHP_VERSION_ID >= 70300 || '\\' !== \DIRECTORY_SEPARATOR) && !@unlink($file)) {
|
||||||
|
fclose($h);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$meta = explode("\n", fread($h, 4096), 3)[2] ?? '';
|
||||||
|
|
||||||
|
// detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
|
||||||
|
if (13 < \strlen($meta) && "\x9D" === $meta[0] && "\0" === $meta[5] && "\x5F" === $meta[9]) {
|
||||||
|
$meta[9] = "\0";
|
||||||
|
$tagLen = unpack('Nlen', $meta, 9)['len'];
|
||||||
|
$meta = substr($meta, 13, $tagLen);
|
||||||
|
|
||||||
|
if (0 < $tagLen -= \strlen($meta)) {
|
||||||
|
$meta .= fread($h, $tagLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
yield $id => '' === $meta ? [] : $this->marshaller->unmarshall($meta);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
yield $id => [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($h);
|
||||||
|
|
||||||
|
if (\PHP_VERSION_ID < 70300 && '\\' === \DIRECTORY_SEPARATOR) {
|
||||||
|
@unlink($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDeleteTagRelations(array $tagData): bool
|
||||||
|
{
|
||||||
|
foreach ($tagData as $tagId => $idList) {
|
||||||
|
$tagFolder = $this->getTagFolder($tagId);
|
||||||
|
foreach ($idList as $id) {
|
||||||
|
@unlink($this->getFile($id, false, $tagFolder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doInvalidate(array $tagIds): bool
|
||||||
|
{
|
||||||
|
foreach ($tagIds as $tagId) {
|
||||||
|
if (!is_dir($tagFolder = $this->getTagFolder($tagId))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error_handler(static function () {});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -9))) {
|
||||||
|
$tagFolder = $renamed.\DIRECTORY_SEPARATOR;
|
||||||
|
} else {
|
||||||
|
$renamed = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->scanHashDir($tagFolder) as $itemLink) {
|
||||||
|
unlink(realpath($itemLink) ?: $itemLink);
|
||||||
|
unlink($itemLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $renamed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
|
||||||
|
for ($i = 0; $i < 38; ++$i) {
|
||||||
|
for ($j = 0; $j < 38; ++$j) {
|
||||||
|
rmdir($tagFolder.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j]);
|
||||||
|
}
|
||||||
|
rmdir($tagFolder.$chars[$i]);
|
||||||
|
}
|
||||||
|
rmdir($renamed);
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTagFolder(string $tagId): string
|
||||||
|
{
|
||||||
|
return $this->getFile($tagId, false, $this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
347
vendor/symfony/cache/Adapter/MemcachedAdapter.php
vendored
Normal file
347
vendor/symfony/cache/Adapter/MemcachedAdapter.php
vendored
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Exception\CacheException;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Rob Frawley 2nd <rmf@src.run>
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class MemcachedAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* We are replacing characters that are illegal in Memcached keys with reserved characters from
|
||||||
|
* {@see \Symfony\Contracts\Cache\ItemInterface::RESERVED_CHARACTERS} that are legal in Memcached.
|
||||||
|
* Note: don’t use {@see \Symfony\Component\Cache\Adapter\AbstractAdapter::NS_SEPARATOR}.
|
||||||
|
*/
|
||||||
|
private const RESERVED_MEMCACHED = " \n\r\t\v\f\0";
|
||||||
|
private const RESERVED_PSR6 = '@()\{}/';
|
||||||
|
|
||||||
|
protected $maxIdLength = 250;
|
||||||
|
|
||||||
|
private $marshaller;
|
||||||
|
private $client;
|
||||||
|
private $lazyClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged.
|
||||||
|
* Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that:
|
||||||
|
* - the Memcached::OPT_BINARY_PROTOCOL must be enabled
|
||||||
|
* (that's the default when using MemcachedAdapter::createConnection());
|
||||||
|
* - tags eviction by Memcached's LRU algorithm will break by-tags invalidation;
|
||||||
|
* your Memcached memory should be large enough to never trigger LRU.
|
||||||
|
*
|
||||||
|
* Using a MemcachedAdapter as a pure items store is fine.
|
||||||
|
*/
|
||||||
|
public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('Memcached '.(\PHP_VERSION_ID >= 80100 ? '> 3.1.5' : '>= 2.2.0').' is required.');
|
||||||
|
}
|
||||||
|
if ('Memcached' === \get_class($client)) {
|
||||||
|
$opt = $client->getOption(\Memcached::OPT_SERIALIZER);
|
||||||
|
if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
|
||||||
|
throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
|
||||||
|
}
|
||||||
|
$this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY));
|
||||||
|
$this->client = $client;
|
||||||
|
} else {
|
||||||
|
$this->lazyClient = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
$this->enableVersioning();
|
||||||
|
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSupported()
|
||||||
|
{
|
||||||
|
return \extension_loaded('memcached') && version_compare(phpversion('memcached'), \PHP_VERSION_ID >= 80100 ? '3.1.6' : '2.2.0', '>=');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Memcached instance.
|
||||||
|
*
|
||||||
|
* By default, the binary protocol, no block, and libketama compatible options are enabled.
|
||||||
|
*
|
||||||
|
* Examples for servers:
|
||||||
|
* - 'memcached://user:pass@localhost?weight=33'
|
||||||
|
* - [['localhost', 11211, 33]]
|
||||||
|
*
|
||||||
|
* @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs
|
||||||
|
*
|
||||||
|
* @return \Memcached
|
||||||
|
*
|
||||||
|
* @throws \ErrorException When invalid options or servers are provided
|
||||||
|
*/
|
||||||
|
public static function createConnection($servers, array $options = [])
|
||||||
|
{
|
||||||
|
if (\is_string($servers)) {
|
||||||
|
$servers = [$servers];
|
||||||
|
} elseif (!\is_array($servers)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, "%s" given.', get_debug_type($servers)));
|
||||||
|
}
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('Memcached '.(\PHP_VERSION_ID >= 80100 ? '> 3.1.5' : '>= 2.2.0').' is required.');
|
||||||
|
}
|
||||||
|
set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
|
||||||
|
try {
|
||||||
|
$client = new \Memcached($options['persistent_id'] ?? null);
|
||||||
|
$username = $options['username'] ?? null;
|
||||||
|
$password = $options['password'] ?? null;
|
||||||
|
|
||||||
|
// parse any DSN in $servers
|
||||||
|
foreach ($servers as $i => $dsn) {
|
||||||
|
if (\is_array($dsn)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!str_starts_with($dsn, 'memcached:')) {
|
||||||
|
throw new InvalidArgumentException('Invalid Memcached DSN: it does not start with "memcached:".');
|
||||||
|
}
|
||||||
|
$params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) {
|
||||||
|
if (!empty($m[2])) {
|
||||||
|
[$username, $password] = explode(':', $m[2], 2) + [1 => null];
|
||||||
|
$username = rawurldecode($username);
|
||||||
|
$password = null !== $password ? rawurldecode($password) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'file:'.($m[1] ?? '');
|
||||||
|
}, $dsn);
|
||||||
|
if (false === $params = parse_url($params)) {
|
||||||
|
throw new InvalidArgumentException('Invalid Memcached DSN.');
|
||||||
|
}
|
||||||
|
$query = $hosts = [];
|
||||||
|
if (isset($params['query'])) {
|
||||||
|
parse_str($params['query'], $query);
|
||||||
|
|
||||||
|
if (isset($query['host'])) {
|
||||||
|
if (!\is_array($hosts = $query['host'])) {
|
||||||
|
throw new InvalidArgumentException('Invalid Memcached DSN: query parameter "host" must be an array.');
|
||||||
|
}
|
||||||
|
foreach ($hosts as $host => $weight) {
|
||||||
|
if (false === $port = strrpos($host, ':')) {
|
||||||
|
$hosts[$host] = [$host, 11211, (int) $weight];
|
||||||
|
} else {
|
||||||
|
$hosts[$host] = [substr($host, 0, $port), (int) substr($host, 1 + $port), (int) $weight];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$hosts = array_values($hosts);
|
||||||
|
unset($query['host']);
|
||||||
|
}
|
||||||
|
if ($hosts && !isset($params['host']) && !isset($params['path'])) {
|
||||||
|
unset($servers[$i]);
|
||||||
|
$servers = array_merge($servers, $hosts);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset($params['host']) && !isset($params['path'])) {
|
||||||
|
throw new InvalidArgumentException('Invalid Memcached DSN: missing host or path.');
|
||||||
|
}
|
||||||
|
if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) {
|
||||||
|
$params['weight'] = $m[1];
|
||||||
|
$params['path'] = substr($params['path'], 0, -\strlen($m[0]));
|
||||||
|
}
|
||||||
|
$params += [
|
||||||
|
'host' => $params['host'] ?? $params['path'],
|
||||||
|
'port' => isset($params['host']) ? 11211 : null,
|
||||||
|
'weight' => 0,
|
||||||
|
];
|
||||||
|
if ($query) {
|
||||||
|
$params += $query;
|
||||||
|
$options = $query + $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
$servers[$i] = [$params['host'], $params['port'], $params['weight']];
|
||||||
|
|
||||||
|
if ($hosts) {
|
||||||
|
$servers = array_merge($servers, $hosts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set client's options
|
||||||
|
unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']);
|
||||||
|
$options = array_change_key_case($options, \CASE_UPPER);
|
||||||
|
$client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
||||||
|
$client->setOption(\Memcached::OPT_NO_BLOCK, true);
|
||||||
|
$client->setOption(\Memcached::OPT_TCP_NODELAY, true);
|
||||||
|
if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
|
||||||
|
$client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
|
||||||
|
}
|
||||||
|
foreach ($options as $name => $value) {
|
||||||
|
if (\is_int($name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) {
|
||||||
|
$value = \constant('Memcached::'.$name.'_'.strtoupper($value));
|
||||||
|
}
|
||||||
|
unset($options[$name]);
|
||||||
|
|
||||||
|
if (\defined('Memcached::OPT_'.$name)) {
|
||||||
|
$options[\constant('Memcached::OPT_'.$name)] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$client->setOptions($options + [\Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_PHP]);
|
||||||
|
|
||||||
|
// set client's servers, taking care of persistent connections
|
||||||
|
if (!$client->isPristine()) {
|
||||||
|
$oldServers = [];
|
||||||
|
foreach ($client->getServerList() as $server) {
|
||||||
|
$oldServers[] = [$server['host'], $server['port']];
|
||||||
|
}
|
||||||
|
|
||||||
|
$newServers = [];
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
if (1 < \count($server)) {
|
||||||
|
$server = array_values($server);
|
||||||
|
unset($server[2]);
|
||||||
|
$server[1] = (int) $server[1];
|
||||||
|
}
|
||||||
|
$newServers[] = $server;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oldServers !== $newServers) {
|
||||||
|
$client->resetServerList();
|
||||||
|
$client->addServers($servers);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$client->addServers($servers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $username || null !== $password) {
|
||||||
|
if (!method_exists($client, 'setSaslAuthData')) {
|
||||||
|
trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.');
|
||||||
|
}
|
||||||
|
$client->setSaslAuthData($username, $password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $client;
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
if (!$values = $this->marshaller->marshall($values, $failed)) {
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lifetime && $lifetime > 30 * 86400) {
|
||||||
|
$lifetime += time();
|
||||||
|
}
|
||||||
|
|
||||||
|
$encodedValues = [];
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
$encodedValues[self::encodeKey($key)] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)) ? $failed : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$encodedIds = array_map([__CLASS__, 'encodeKey'], $ids);
|
||||||
|
|
||||||
|
$encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds));
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($encodedResult as $key => $value) {
|
||||||
|
$result[self::decodeKey($key)] = $this->marshaller->unmarshall($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
} catch (\Error $e) {
|
||||||
|
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id)
|
||||||
|
{
|
||||||
|
return false !== $this->getClient()->get(self::encodeKey($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
$encodedIds = array_map([__CLASS__, 'encodeKey'], $ids);
|
||||||
|
foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) {
|
||||||
|
if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) {
|
||||||
|
$ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace)
|
||||||
|
{
|
||||||
|
return '' === $namespace && $this->getClient()->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkResultCode($result)
|
||||||
|
{
|
||||||
|
$code = $this->client->getResultCode();
|
||||||
|
|
||||||
|
if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CacheException('MemcachedAdapter client error: '.strtolower($this->client->getResultMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getClient(): \Memcached
|
||||||
|
{
|
||||||
|
if ($this->client) {
|
||||||
|
return $this->client;
|
||||||
|
}
|
||||||
|
|
||||||
|
$opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER);
|
||||||
|
if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
|
||||||
|
throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
|
||||||
|
}
|
||||||
|
if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) {
|
||||||
|
throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->client = $this->lazyClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function encodeKey(string $key): string
|
||||||
|
{
|
||||||
|
return strtr($key, self::RESERVED_MEMCACHED, self::RESERVED_PSR6);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function decodeKey(string $key): string
|
||||||
|
{
|
||||||
|
return strtr($key, self::RESERVED_PSR6, self::RESERVED_MEMCACHED);
|
||||||
|
}
|
||||||
|
}
|
||||||
152
vendor/symfony/cache/Adapter/NullAdapter.php
vendored
Normal file
152
vendor/symfony/cache/Adapter/NullAdapter.php
vendored
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||||
|
*/
|
||||||
|
class NullAdapter implements AdapterInterface, CacheInterface
|
||||||
|
{
|
||||||
|
private static $createCacheItem;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
|
||||||
|
static function ($key) {
|
||||||
|
$item = new CacheItem();
|
||||||
|
$item->key = $key;
|
||||||
|
$item->isHit = false;
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
$save = true;
|
||||||
|
|
||||||
|
return $callback((self::$createCacheItem)($key), $save);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
return (self::$createCacheItem)($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
return $this->generateItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(string $key): bool
|
||||||
|
{
|
||||||
|
return $this->deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateItems(array $keys): \Generator
|
||||||
|
{
|
||||||
|
$f = self::$createCacheItem;
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
yield $key => $f($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
vendor/symfony/cache/Adapter/ParameterNormalizer.php
vendored
Normal file
35
vendor/symfony/cache/Adapter/ParameterNormalizer.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lars Strojny <lars@strojny.net>
|
||||||
|
*/
|
||||||
|
final class ParameterNormalizer
|
||||||
|
{
|
||||||
|
public static function normalizeDuration(string $duration): int
|
||||||
|
{
|
||||||
|
if (is_numeric($duration)) {
|
||||||
|
return $duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $time = strtotime($duration, 0)) {
|
||||||
|
return $time;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return \DateTime::createFromFormat('U', 0)->add(new \DateInterval($duration))->getTimestamp();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Cannot parse date interval "%s".', $duration), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
616
vendor/symfony/cache/Adapter/PdoAdapter.php
vendored
Normal file
616
vendor/symfony/cache/Adapter/PdoAdapter.php
vendored
Normal file
@@ -0,0 +1,616 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
|
||||||
|
class PdoAdapter extends AbstractAdapter implements PruneableInterface
|
||||||
|
{
|
||||||
|
protected $maxIdLength = 255;
|
||||||
|
|
||||||
|
private $marshaller;
|
||||||
|
private $conn;
|
||||||
|
private $dsn;
|
||||||
|
private $driver;
|
||||||
|
private $serverVersion;
|
||||||
|
private $table = 'cache_items';
|
||||||
|
private $idCol = 'item_id';
|
||||||
|
private $dataCol = 'item_data';
|
||||||
|
private $lifetimeCol = 'item_lifetime';
|
||||||
|
private $timeCol = 'item_time';
|
||||||
|
private $username = null;
|
||||||
|
private $password = null;
|
||||||
|
private $connectionOptions = [];
|
||||||
|
private $namespace;
|
||||||
|
|
||||||
|
private $dbalAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can either pass an existing database connection as PDO instance or
|
||||||
|
* a DSN string that will be used to lazy-connect to the database when the
|
||||||
|
* cache is actually used.
|
||||||
|
*
|
||||||
|
* List of available options:
|
||||||
|
* * db_table: The name of the table [default: cache_items]
|
||||||
|
* * db_id_col: The column where to store the cache id [default: item_id]
|
||||||
|
* * db_data_col: The column where to store the cache data [default: item_data]
|
||||||
|
* * db_lifetime_col: The column where to store the lifetime [default: item_lifetime]
|
||||||
|
* * db_time_col: The column where to store the timestamp [default: item_time]
|
||||||
|
* * db_username: The username when lazy-connect [default: '']
|
||||||
|
* * db_password: The password when lazy-connect [default: '']
|
||||||
|
* * db_connection_options: An array of driver-specific connection options [default: []]
|
||||||
|
*
|
||||||
|
* @param \PDO|string $connOrDsn
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When first argument is not PDO nor Connection nor string
|
||||||
|
* @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
|
||||||
|
* @throws InvalidArgumentException When namespace contains invalid characters
|
||||||
|
*/
|
||||||
|
public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
if ($connOrDsn instanceof Connection || (\is_string($connOrDsn) && str_contains($connOrDsn, '://'))) {
|
||||||
|
trigger_deprecation('symfony/cache', '5.4', 'Usage of a DBAL Connection with "%s" is deprecated and will be removed in symfony 6.0. Use "%s" instead.', __CLASS__, DoctrineDbalAdapter::class);
|
||||||
|
$this->dbalAdapter = new DoctrineDbalAdapter($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($connOrDsn instanceof \PDO) {
|
||||||
|
if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __CLASS__));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->conn = $connOrDsn;
|
||||||
|
} elseif (\is_string($connOrDsn)) {
|
||||||
|
$this->dsn = $connOrDsn;
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException(sprintf('"%s" requires PDO or Doctrine\DBAL\Connection instance or DSN string as first argument, "%s" given.', __CLASS__, get_debug_type($connOrDsn)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->table = $options['db_table'] ?? $this->table;
|
||||||
|
$this->idCol = $options['db_id_col'] ?? $this->idCol;
|
||||||
|
$this->dataCol = $options['db_data_col'] ?? $this->dataCol;
|
||||||
|
$this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol;
|
||||||
|
$this->timeCol = $options['db_time_col'] ?? $this->timeCol;
|
||||||
|
$this->username = $options['db_username'] ?? $this->username;
|
||||||
|
$this->password = $options['db_password'] ?? $this->password;
|
||||||
|
$this->connectionOptions = $options['db_connection_options'] ?? $this->connectionOptions;
|
||||||
|
$this->namespace = $namespace;
|
||||||
|
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||||
|
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->getItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->getItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->hasItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::hasItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->deleteItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::deleteItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->clear($prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::clear($prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->get($key, $callback, $beta, $metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::get($key, $callback, $beta, $metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function delete(string $key): bool
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->delete($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::delete($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->saveDeferred($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::saveDeferred($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function setLogger(LoggerInterface $logger): void
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
$this->dbalAdapter->setLogger($logger);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::setLogger($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
$this->dbalAdapter->reset();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the table to store cache items which can be called once for setup.
|
||||||
|
*
|
||||||
|
* Cache ID are saved in a column of maximum length 255. Cache data is
|
||||||
|
* saved in a BLOB.
|
||||||
|
*
|
||||||
|
* @throws \PDOException When the table already exists
|
||||||
|
* @throws \DomainException When an unsupported PDO driver is used
|
||||||
|
*/
|
||||||
|
public function createTable()
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
$this->dbalAdapter->createTable();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect if we are not yet
|
||||||
|
$conn = $this->getConnection();
|
||||||
|
|
||||||
|
switch ($this->driver) {
|
||||||
|
case 'mysql':
|
||||||
|
// We use varbinary for the ID column because it prevents unwanted conversions:
|
||||||
|
// - character set conversions between server and client
|
||||||
|
// - trailing space removal
|
||||||
|
// - case-insensitivity
|
||||||
|
// - language processing like é == e
|
||||||
|
$sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(255) NOT NULL PRIMARY KEY, $this->dataCol MEDIUMBLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB";
|
||||||
|
break;
|
||||||
|
case 'sqlite':
|
||||||
|
$sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
|
||||||
|
break;
|
||||||
|
case 'pgsql':
|
||||||
|
$sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
|
||||||
|
break;
|
||||||
|
case 'oci':
|
||||||
|
$sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(255) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
|
||||||
|
break;
|
||||||
|
case 'sqlsrv':
|
||||||
|
$sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->exec($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the Table to the Schema if the adapter uses this Connection.
|
||||||
|
*
|
||||||
|
* @deprecated since symfony/cache 5.4 use DoctrineDbalAdapter instead
|
||||||
|
*/
|
||||||
|
public function configureSchema(Schema $schema, Connection $forConnection): void
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
$this->dbalAdapter->configureSchema($schema, $forConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function prune()
|
||||||
|
{
|
||||||
|
if (isset($this->dbalAdapter)) {
|
||||||
|
return $this->dbalAdapter->prune();
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time";
|
||||||
|
|
||||||
|
if ('' !== $this->namespace) {
|
||||||
|
$deleteSql .= " AND $this->idCol LIKE :namespace";
|
||||||
|
}
|
||||||
|
|
||||||
|
$connection = $this->getConnection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$delete = $connection->prepare($deleteSql);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$delete->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
|
|
||||||
|
if ('' !== $this->namespace) {
|
||||||
|
$delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), \PDO::PARAM_STR);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return $delete->execute();
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
$connection = $this->getConnection();
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
$expired = [];
|
||||||
|
|
||||||
|
$sql = str_pad('', (\count($ids) << 1) - 1, '?,');
|
||||||
|
$sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN ($sql)";
|
||||||
|
$stmt = $connection->prepare($sql);
|
||||||
|
$stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$stmt->bindValue(++$i, $id);
|
||||||
|
}
|
||||||
|
$result = $stmt->execute();
|
||||||
|
|
||||||
|
if (\is_object($result)) {
|
||||||
|
$result = $result->iterateNumeric();
|
||||||
|
} else {
|
||||||
|
$stmt->setFetchMode(\PDO::FETCH_NUM);
|
||||||
|
$result = $stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($result as $row) {
|
||||||
|
if (null === $row[1]) {
|
||||||
|
$expired[] = $row[0];
|
||||||
|
} else {
|
||||||
|
yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expired) {
|
||||||
|
$sql = str_pad('', (\count($expired) << 1) - 1, '?,');
|
||||||
|
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN ($sql)";
|
||||||
|
$stmt = $connection->prepare($sql);
|
||||||
|
$stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
|
||||||
|
foreach ($expired as $id) {
|
||||||
|
$stmt->bindValue(++$i, $id);
|
||||||
|
}
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id)
|
||||||
|
{
|
||||||
|
$connection = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "SELECT 1 FROM $this->table WHERE $this->idCol = :id AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > :time)";
|
||||||
|
$stmt = $connection->prepare($sql);
|
||||||
|
|
||||||
|
$stmt->bindValue(':id', $id);
|
||||||
|
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
return (bool) $stmt->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace)
|
||||||
|
{
|
||||||
|
$conn = $this->getConnection();
|
||||||
|
|
||||||
|
if ('' === $namespace) {
|
||||||
|
if ('sqlite' === $this->driver) {
|
||||||
|
$sql = "DELETE FROM $this->table";
|
||||||
|
} else {
|
||||||
|
$sql = "TRUNCATE TABLE $this->table";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$conn->exec($sql);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
$sql = str_pad('', (\count($ids) << 1) - 1, '?,');
|
||||||
|
$sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)";
|
||||||
|
try {
|
||||||
|
$stmt = $this->getConnection()->prepare($sql);
|
||||||
|
$stmt->execute(array_values($ids));
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
if (!$values = $this->marshaller->marshall($values, $failed)) {
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn = $this->getConnection();
|
||||||
|
|
||||||
|
$driver = $this->driver;
|
||||||
|
$insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case 'mysql' === $driver:
|
||||||
|
$sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
|
||||||
|
break;
|
||||||
|
case 'oci' === $driver:
|
||||||
|
// DUAL is Oracle specific dummy table
|
||||||
|
$sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ".
|
||||||
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
|
||||||
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?";
|
||||||
|
break;
|
||||||
|
case 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '10', '>='):
|
||||||
|
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
|
||||||
|
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
|
||||||
|
$sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
|
||||||
|
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
|
||||||
|
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
|
||||||
|
break;
|
||||||
|
case 'sqlite' === $driver:
|
||||||
|
$sql = 'INSERT OR REPLACE'.substr($insertSql, 6);
|
||||||
|
break;
|
||||||
|
case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='):
|
||||||
|
$sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$driver = null;
|
||||||
|
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
$lifetime = $lifetime ?: null;
|
||||||
|
try {
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
|
||||||
|
$this->createTable();
|
||||||
|
}
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
// $id and $data are defined later in the loop. Binding is done by reference, values are read on execution.
|
||||||
|
if ('sqlsrv' === $driver || 'oci' === $driver) {
|
||||||
|
$stmt->bindParam(1, $id);
|
||||||
|
$stmt->bindParam(2, $id);
|
||||||
|
$stmt->bindParam(3, $data, \PDO::PARAM_LOB);
|
||||||
|
$stmt->bindValue(4, $lifetime, \PDO::PARAM_INT);
|
||||||
|
$stmt->bindValue(5, $now, \PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(6, $data, \PDO::PARAM_LOB);
|
||||||
|
$stmt->bindValue(7, $lifetime, \PDO::PARAM_INT);
|
||||||
|
$stmt->bindValue(8, $now, \PDO::PARAM_INT);
|
||||||
|
} else {
|
||||||
|
$stmt->bindParam(':id', $id);
|
||||||
|
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
|
||||||
|
$stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
|
||||||
|
$stmt->bindValue(':time', $now, \PDO::PARAM_INT);
|
||||||
|
}
|
||||||
|
if (null === $driver) {
|
||||||
|
$insertStmt = $conn->prepare($insertSql);
|
||||||
|
|
||||||
|
$insertStmt->bindParam(':id', $id);
|
||||||
|
$insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
|
||||||
|
$insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
|
||||||
|
$insertStmt->bindValue(':time', $now, \PDO::PARAM_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($values as $id => $data) {
|
||||||
|
try {
|
||||||
|
$stmt->execute();
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
|
||||||
|
$this->createTable();
|
||||||
|
}
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
if (null === $driver && !$stmt->rowCount()) {
|
||||||
|
try {
|
||||||
|
$insertStmt->execute();
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
// A concurrent write won, let it be
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getId($key)
|
||||||
|
{
|
||||||
|
if ('pgsql' !== $this->driver ?? ($this->getConnection() ? $this->driver : null)) {
|
||||||
|
return parent::getId($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_contains($key, "\0") || str_contains($key, '%') || !preg_match('//u', $key)) {
|
||||||
|
$key = rawurlencode($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getId($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConnection(): \PDO
|
||||||
|
{
|
||||||
|
if (null === $this->conn) {
|
||||||
|
$this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions);
|
||||||
|
$this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
}
|
||||||
|
if (null === $this->driver) {
|
||||||
|
$this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getServerVersion(): string
|
||||||
|
{
|
||||||
|
if (null === $this->serverVersion) {
|
||||||
|
$this->serverVersion = $this->conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->serverVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isTableMissing(\PDOException $exception): bool
|
||||||
|
{
|
||||||
|
$driver = $this->driver;
|
||||||
|
[$sqlState, $code] = $exception->errorInfo ?? [null, $exception->getCode()];
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case 'pgsql' === $driver && '42P01' === $sqlState:
|
||||||
|
case 'sqlite' === $driver && str_contains($exception->getMessage(), 'no such table:'):
|
||||||
|
case 'oci' === $driver && 942 === $code:
|
||||||
|
case 'sqlsrv' === $driver && 208 === $code:
|
||||||
|
case 'mysql' === $driver && 1146 === $code:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
435
vendor/symfony/cache/Adapter/PhpArrayAdapter.php
vendored
Normal file
435
vendor/symfony/cache/Adapter/PhpArrayAdapter.php
vendored
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||||
|
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||||
|
use Symfony\Component\VarExporter\VarExporter;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0.
|
||||||
|
* Warmed up items are read-only and run-time discovered items are cached using a fallback adapter.
|
||||||
|
*
|
||||||
|
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
use ContractsTrait;
|
||||||
|
use ProxyTrait;
|
||||||
|
|
||||||
|
private $file;
|
||||||
|
private $keys;
|
||||||
|
private $values;
|
||||||
|
|
||||||
|
private static $createCacheItem;
|
||||||
|
private static $valuesCache = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $file The PHP file were values are cached
|
||||||
|
* @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||||
|
*/
|
||||||
|
public function __construct(string $file, AdapterInterface $fallbackPool)
|
||||||
|
{
|
||||||
|
$this->file = $file;
|
||||||
|
$this->pool = $fallbackPool;
|
||||||
|
self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
|
||||||
|
static function ($key, $value, $isHit) {
|
||||||
|
$item = new CacheItem();
|
||||||
|
$item->key = $key;
|
||||||
|
$item->value = $value;
|
||||||
|
$item->isHit = $isHit;
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This adapter takes advantage of how PHP stores arrays in its latest versions.
|
||||||
|
*
|
||||||
|
* @param string $file The PHP file were values are cached
|
||||||
|
* @param CacheItemPoolInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||||
|
*
|
||||||
|
* @return CacheItemPoolInterface
|
||||||
|
*/
|
||||||
|
public static function create(string $file, CacheItemPoolInterface $fallbackPool)
|
||||||
|
{
|
||||||
|
if (!$fallbackPool instanceof AdapterInterface) {
|
||||||
|
$fallbackPool = new ProxyAdapter($fallbackPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new static($file, $fallbackPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
if (!isset($this->keys[$key])) {
|
||||||
|
get_from_pool:
|
||||||
|
if ($this->pool instanceof CacheInterface) {
|
||||||
|
return $this->pool->get($key, $callback, $beta, $metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->doGet($this->pool, $key, $callback, $beta, $metadata);
|
||||||
|
}
|
||||||
|
$value = $this->values[$this->keys[$key]];
|
||||||
|
|
||||||
|
if ('N;' === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if ($value instanceof \Closure) {
|
||||||
|
return $value();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
unset($this->keys[$key]);
|
||||||
|
goto get_from_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key)));
|
||||||
|
}
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
if (!isset($this->keys[$key])) {
|
||||||
|
return $this->pool->getItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->values[$this->keys[$key]];
|
||||||
|
$isHit = true;
|
||||||
|
|
||||||
|
if ('N;' === $value) {
|
||||||
|
$value = null;
|
||||||
|
} elseif ($value instanceof \Closure) {
|
||||||
|
try {
|
||||||
|
$value = $value();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$value = null;
|
||||||
|
$isHit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (self::$createCacheItem)($key, $value, $isHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->generateItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key)));
|
||||||
|
}
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->keys[$key]) || $this->pool->hasItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key)));
|
||||||
|
}
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isset($this->keys[$key]) && $this->pool->deleteItem($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
$deleted = true;
|
||||||
|
$fallbackKeys = [];
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->keys[$key])) {
|
||||||
|
$deleted = false;
|
||||||
|
} else {
|
||||||
|
$fallbackKeys[] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fallbackKeys) {
|
||||||
|
$deleted = $this->pool->deleteItems($fallbackKeys) && $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isset($this->keys[$item->getKey()]) && $this->pool->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
if (null === $this->values) {
|
||||||
|
$this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
return $this->pool->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
$this->keys = $this->values = [];
|
||||||
|
|
||||||
|
$cleared = @unlink($this->file) || !file_exists($this->file);
|
||||||
|
unset(self::$valuesCache[$this->file]);
|
||||||
|
|
||||||
|
if ($this->pool instanceof AdapterInterface) {
|
||||||
|
return $this->pool->clear($prefix) && $cleared;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pool->clear() && $cleared;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store an array of cached values.
|
||||||
|
*
|
||||||
|
* @param array $values The cached values
|
||||||
|
*
|
||||||
|
* @return string[] A list of classes to preload on PHP 7.4+
|
||||||
|
*/
|
||||||
|
public function warmUp(array $values)
|
||||||
|
{
|
||||||
|
if (file_exists($this->file)) {
|
||||||
|
if (!is_file($this->file)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: "%s".', $this->file));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable($this->file)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache file is not writable: "%s".', $this->file));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$directory = \dirname($this->file);
|
||||||
|
|
||||||
|
if (!is_dir($directory) && !@mkdir($directory, 0777, true)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: "%s".', $directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable($directory)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory is not writable: "%s".', $directory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$preload = [];
|
||||||
|
$dumpedValues = '';
|
||||||
|
$dumpedMap = [];
|
||||||
|
$dump = <<<'EOF'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// This file has been auto-generated by the Symfony Cache Component.
|
||||||
|
|
||||||
|
return [[
|
||||||
|
|
||||||
|
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
|
||||||
|
$isStaticValue = true;
|
||||||
|
|
||||||
|
if (null === $value) {
|
||||||
|
$value = "'N;'";
|
||||||
|
} elseif (\is_object($value) || \is_array($value)) {
|
||||||
|
try {
|
||||||
|
$value = VarExporter::export($value, $isStaticValue, $preload);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)), 0, $e);
|
||||||
|
}
|
||||||
|
} elseif (\is_string($value)) {
|
||||||
|
// Wrap "N;" in a closure to not confuse it with an encoded `null`
|
||||||
|
if ('N;' === $value) {
|
||||||
|
$isStaticValue = false;
|
||||||
|
}
|
||||||
|
$value = var_export($value, true);
|
||||||
|
} elseif (!\is_scalar($value)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)));
|
||||||
|
} else {
|
||||||
|
$value = var_export($value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$isStaticValue) {
|
||||||
|
$value = str_replace("\n", "\n ", $value);
|
||||||
|
$value = "static function () {\n return {$value};\n}";
|
||||||
|
}
|
||||||
|
$hash = hash('md5', $value);
|
||||||
|
|
||||||
|
if (null === $id = $dumpedMap[$hash] ?? null) {
|
||||||
|
$id = $dumpedMap[$hash] = \count($dumpedMap);
|
||||||
|
$dumpedValues .= "{$id} => {$value},\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$dump .= var_export($key, true)." => {$id},\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$dump .= "\n], [\n\n{$dumpedValues}\n]];\n";
|
||||||
|
|
||||||
|
$tmpFile = uniqid($this->file, true);
|
||||||
|
|
||||||
|
file_put_contents($tmpFile, $dump);
|
||||||
|
@chmod($tmpFile, 0666 & ~umask());
|
||||||
|
unset($serialized, $value, $dump);
|
||||||
|
|
||||||
|
@rename($tmpFile, $this->file);
|
||||||
|
unset(self::$valuesCache[$this->file]);
|
||||||
|
|
||||||
|
$this->initialize();
|
||||||
|
|
||||||
|
return $preload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the cache file.
|
||||||
|
*/
|
||||||
|
private function initialize()
|
||||||
|
{
|
||||||
|
if (isset(self::$valuesCache[$this->file])) {
|
||||||
|
$values = self::$valuesCache[$this->file];
|
||||||
|
} elseif (!is_file($this->file)) {
|
||||||
|
$this->keys = $this->values = [];
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (2 !== \count($values) || !isset($values[0], $values[1])) {
|
||||||
|
$this->keys = $this->values = [];
|
||||||
|
} else {
|
||||||
|
[$this->keys, $this->values] = $values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateItems(array $keys): \Generator
|
||||||
|
{
|
||||||
|
$f = self::$createCacheItem;
|
||||||
|
$fallbackKeys = [];
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (isset($this->keys[$key])) {
|
||||||
|
$value = $this->values[$this->keys[$key]];
|
||||||
|
|
||||||
|
if ('N;' === $value) {
|
||||||
|
yield $key => $f($key, null, true);
|
||||||
|
} elseif ($value instanceof \Closure) {
|
||||||
|
try {
|
||||||
|
yield $key => $f($key, $value(), true);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
yield $key => $f($key, null, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
yield $key => $f($key, $value, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$fallbackKeys[] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fallbackKeys) {
|
||||||
|
yield from $this->pool->getItems($fallbackKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
330
vendor/symfony/cache/Adapter/PhpFilesAdapter.php
vendored
Normal file
330
vendor/symfony/cache/Adapter/PhpFilesAdapter.php
vendored
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Exception\CacheException;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\FilesystemCommonTrait;
|
||||||
|
use Symfony\Component\VarExporter\VarExporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Piotr Stankowski <git@trakos.pl>
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
* @author Rob Frawley 2nd <rmf@src.run>
|
||||||
|
*/
|
||||||
|
class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||||
|
{
|
||||||
|
use FilesystemCommonTrait {
|
||||||
|
doClear as private doCommonClear;
|
||||||
|
doDelete as private doCommonDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
private $includeHandler;
|
||||||
|
private $appendOnly;
|
||||||
|
private $values = [];
|
||||||
|
private $files = [];
|
||||||
|
|
||||||
|
private static $startTime;
|
||||||
|
private static $valuesCache = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
|
||||||
|
* Doing so is encouraged because it fits perfectly OPcache's memory model.
|
||||||
|
*
|
||||||
|
* @throws CacheException if OPcache is not enabled
|
||||||
|
*/
|
||||||
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, ?string $directory = null, bool $appendOnly = false)
|
||||||
|
{
|
||||||
|
$this->appendOnly = $appendOnly;
|
||||||
|
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||||
|
parent::__construct('', $defaultLifetime);
|
||||||
|
$this->init($namespace, $directory);
|
||||||
|
$this->includeHandler = static function ($type, $msg, $file, $line) {
|
||||||
|
throw new \ErrorException($msg, 0, $type, $file, $line);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isSupported()
|
||||||
|
{
|
||||||
|
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||||
|
|
||||||
|
return \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function prune()
|
||||||
|
{
|
||||||
|
$time = time();
|
||||||
|
$pruned = true;
|
||||||
|
$getExpiry = true;
|
||||||
|
|
||||||
|
set_error_handler($this->includeHandler);
|
||||||
|
try {
|
||||||
|
foreach ($this->scanHashDir($this->directory) as $file) {
|
||||||
|
try {
|
||||||
|
if (\is_array($expiresAt = include $file)) {
|
||||||
|
$expiresAt = $expiresAt[0];
|
||||||
|
}
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
$expiresAt = $time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($time >= $expiresAt) {
|
||||||
|
$pruned = ($this->doUnlink($file) || !file_exists($file)) && $pruned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pruned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
if ($this->appendOnly) {
|
||||||
|
$now = 0;
|
||||||
|
$missingIds = [];
|
||||||
|
} else {
|
||||||
|
$now = time();
|
||||||
|
$missingIds = $ids;
|
||||||
|
$ids = [];
|
||||||
|
}
|
||||||
|
$values = [];
|
||||||
|
|
||||||
|
begin:
|
||||||
|
$getExpiry = false;
|
||||||
|
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
if (null === $value = $this->values[$id] ?? null) {
|
||||||
|
$missingIds[] = $id;
|
||||||
|
} elseif ('N;' === $value) {
|
||||||
|
$values[$id] = null;
|
||||||
|
} elseif (!\is_object($value)) {
|
||||||
|
$values[$id] = $value;
|
||||||
|
} elseif (!$value instanceof LazyValue) {
|
||||||
|
$values[$id] = $value();
|
||||||
|
} elseif (false === $values[$id] = include $value->file) {
|
||||||
|
unset($values[$id], $this->values[$id]);
|
||||||
|
$missingIds[] = $id;
|
||||||
|
}
|
||||||
|
if (!$this->appendOnly) {
|
||||||
|
unset($this->values[$id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$missingIds) {
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error_handler($this->includeHandler);
|
||||||
|
try {
|
||||||
|
$getExpiry = true;
|
||||||
|
|
||||||
|
foreach ($missingIds as $k => $id) {
|
||||||
|
try {
|
||||||
|
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||||
|
|
||||||
|
if (isset(self::$valuesCache[$file])) {
|
||||||
|
[$expiresAt, $this->values[$id]] = self::$valuesCache[$file];
|
||||||
|
} elseif (\is_array($expiresAt = include $file)) {
|
||||||
|
if ($this->appendOnly) {
|
||||||
|
self::$valuesCache[$file] = $expiresAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$expiresAt, $this->values[$id]] = $expiresAt;
|
||||||
|
} elseif ($now < $expiresAt) {
|
||||||
|
$this->values[$id] = new LazyValue($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($now >= $expiresAt) {
|
||||||
|
unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
|
||||||
|
}
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
unset($missingIds[$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $missingIds;
|
||||||
|
$missingIds = [];
|
||||||
|
goto begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id)
|
||||||
|
{
|
||||||
|
if ($this->appendOnly && isset($this->values[$id])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error_handler($this->includeHandler);
|
||||||
|
try {
|
||||||
|
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||||
|
$getExpiry = true;
|
||||||
|
|
||||||
|
if (isset(self::$valuesCache[$file])) {
|
||||||
|
[$expiresAt, $value] = self::$valuesCache[$file];
|
||||||
|
} elseif (\is_array($expiresAt = include $file)) {
|
||||||
|
if ($this->appendOnly) {
|
||||||
|
self::$valuesCache[$file] = $expiresAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$expiresAt, $value] = $expiresAt;
|
||||||
|
} elseif ($this->appendOnly) {
|
||||||
|
$value = new LazyValue($file);
|
||||||
|
}
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
if ($this->appendOnly) {
|
||||||
|
$now = 0;
|
||||||
|
$this->values[$id] = $value;
|
||||||
|
} else {
|
||||||
|
$now = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $now < $expiresAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
$expiry = $lifetime ? time() + $lifetime : 'PHP_INT_MAX';
|
||||||
|
$allowCompile = self::isSupported();
|
||||||
|
|
||||||
|
foreach ($values as $key => $value) {
|
||||||
|
unset($this->values[$key]);
|
||||||
|
$isStaticValue = true;
|
||||||
|
if (null === $value) {
|
||||||
|
$value = "'N;'";
|
||||||
|
} elseif (\is_object($value) || \is_array($value)) {
|
||||||
|
try {
|
||||||
|
$value = VarExporter::export($value, $isStaticValue);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)), 0, $e);
|
||||||
|
}
|
||||||
|
} elseif (\is_string($value)) {
|
||||||
|
// Wrap "N;" in a closure to not confuse it with an encoded `null`
|
||||||
|
if ('N;' === $value) {
|
||||||
|
$isStaticValue = false;
|
||||||
|
}
|
||||||
|
$value = var_export($value, true);
|
||||||
|
} elseif (!\is_scalar($value)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, get_debug_type($value)));
|
||||||
|
} else {
|
||||||
|
$value = var_export($value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$encodedKey = rawurlencode($key);
|
||||||
|
|
||||||
|
if ($isStaticValue) {
|
||||||
|
$value = "return [{$expiry}, {$value}];";
|
||||||
|
} elseif ($this->appendOnly) {
|
||||||
|
$value = "return [{$expiry}, static function () { return {$value}; }];";
|
||||||
|
} else {
|
||||||
|
// We cannot use a closure here because of https://bugs.php.net/76982
|
||||||
|
$value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
|
||||||
|
$value = "namespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};";
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->files[$key] = $this->getFile($key, true);
|
||||||
|
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
|
||||||
|
$ok = $this->write($file, "<?php //{$encodedKey}\n\n{$value}\n", self::$startTime - 10) && $ok;
|
||||||
|
|
||||||
|
if ($allowCompile) {
|
||||||
|
@opcache_invalidate($file, true);
|
||||||
|
@opcache_compile_file($file);
|
||||||
|
}
|
||||||
|
unset(self::$valuesCache[$file]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ok && !is_writable($this->directory)) {
|
||||||
|
throw new CacheException(sprintf('Cache directory is not writable (%s).', $this->directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace)
|
||||||
|
{
|
||||||
|
$this->values = [];
|
||||||
|
|
||||||
|
return $this->doCommonClear($namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
unset($this->values[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->doCommonDelete($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doUnlink(string $file)
|
||||||
|
{
|
||||||
|
unset(self::$valuesCache[$file]);
|
||||||
|
|
||||||
|
if (self::isSupported()) {
|
||||||
|
@opcache_invalidate($file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return @unlink($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFileKey(string $file): string
|
||||||
|
{
|
||||||
|
if (!$h = @fopen($file, 'r')) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$encodedKey = substr(fgets($h), 8);
|
||||||
|
fclose($h);
|
||||||
|
|
||||||
|
return rawurldecode(rtrim($encodedKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class LazyValue
|
||||||
|
{
|
||||||
|
public $file;
|
||||||
|
|
||||||
|
public function __construct(string $file)
|
||||||
|
{
|
||||||
|
$this->file = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
268
vendor/symfony/cache/Adapter/ProxyAdapter.php
vendored
Normal file
268
vendor/symfony/cache/Adapter/ProxyAdapter.php
vendored
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||||
|
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
use ContractsTrait;
|
||||||
|
use ProxyTrait;
|
||||||
|
|
||||||
|
private $namespace = '';
|
||||||
|
private $namespaceLen;
|
||||||
|
private $poolHash;
|
||||||
|
private $defaultLifetime;
|
||||||
|
|
||||||
|
private static $createCacheItem;
|
||||||
|
private static $setInnerItem;
|
||||||
|
|
||||||
|
public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0)
|
||||||
|
{
|
||||||
|
$this->pool = $pool;
|
||||||
|
$this->poolHash = $poolHash = spl_object_hash($pool);
|
||||||
|
if ('' !== $namespace) {
|
||||||
|
\assert('' !== CacheItem::validateKey($namespace));
|
||||||
|
$this->namespace = $namespace;
|
||||||
|
}
|
||||||
|
$this->namespaceLen = \strlen($namespace);
|
||||||
|
$this->defaultLifetime = $defaultLifetime;
|
||||||
|
self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
|
||||||
|
static function ($key, $innerItem, $poolHash) {
|
||||||
|
$item = new CacheItem();
|
||||||
|
$item->key = $key;
|
||||||
|
|
||||||
|
if (null === $innerItem) {
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item->value = $v = $innerItem->get();
|
||||||
|
$item->isHit = $innerItem->isHit();
|
||||||
|
$item->innerItem = $innerItem;
|
||||||
|
$item->poolHash = $poolHash;
|
||||||
|
|
||||||
|
// Detect wrapped values that encode for their expiry and creation duration
|
||||||
|
// For compactness, these values are packed in the key of an array using
|
||||||
|
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
|
||||||
|
if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9D" === $k[0] && "\0" === $k[5] && "\x5F" === $k[9]) {
|
||||||
|
$item->value = $v[$k];
|
||||||
|
$v = unpack('Ve/Nc', substr($k, 1, -1));
|
||||||
|
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
|
||||||
|
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
|
||||||
|
} elseif ($innerItem instanceof CacheItem) {
|
||||||
|
$item->metadata = $innerItem->metadata;
|
||||||
|
}
|
||||||
|
$innerItem->set(null);
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
self::$setInnerItem ?? self::$setInnerItem = \Closure::bind(
|
||||||
|
/**
|
||||||
|
* @param array $item A CacheItem cast to (array); accessing protected properties requires adding the "\0*\0" PHP prefix
|
||||||
|
*/
|
||||||
|
static function (CacheItemInterface $innerItem, array $item) {
|
||||||
|
// Tags are stored separately, no need to account for them when considering this item's newly set metadata
|
||||||
|
if (isset(($metadata = $item["\0*\0newMetadata"])[CacheItem::METADATA_TAGS])) {
|
||||||
|
unset($metadata[CacheItem::METADATA_TAGS]);
|
||||||
|
}
|
||||||
|
if ($metadata) {
|
||||||
|
// For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators
|
||||||
|
$item["\0*\0value"] = ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item["\0*\0value"]];
|
||||||
|
}
|
||||||
|
$innerItem->set($item["\0*\0value"]);
|
||||||
|
$innerItem->expiresAt(null !== $item["\0*\0expiry"] ? \DateTime::createFromFormat('U.u', sprintf('%.6F', $item["\0*\0expiry"])) : null);
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
if (!$this->pool instanceof CacheInterface) {
|
||||||
|
return $this->doGet($this, $key, $callback, $beta, $metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pool->get($this->getId($key), function ($innerItem, bool &$save) use ($key, $callback) {
|
||||||
|
$item = (self::$createCacheItem)($key, $innerItem, $this->poolHash);
|
||||||
|
$item->set($value = $callback($item, $save));
|
||||||
|
(self::$setInnerItem)($innerItem, (array) $item);
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}, $beta, $metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
$item = $this->pool->getItem($this->getId($key));
|
||||||
|
|
||||||
|
return (self::$createCacheItem)($key, $item, $this->poolHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
if ($this->namespaceLen) {
|
||||||
|
foreach ($keys as $i => $key) {
|
||||||
|
$keys[$i] = $this->getId($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->generateItems($this->pool->getItems($keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
return $this->pool->hasItem($this->getId($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
if ($this->pool instanceof AdapterInterface) {
|
||||||
|
return $this->pool->clear($this->namespace.$prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pool->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
return $this->pool->deleteItem($this->getId($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
if ($this->namespaceLen) {
|
||||||
|
foreach ($keys as $i => $key) {
|
||||||
|
$keys[$i] = $this->getId($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pool->deleteItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
return $this->doSave($item, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
return $this->doSave($item, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
return $this->pool->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function doSave(CacheItemInterface $item, string $method)
|
||||||
|
{
|
||||||
|
if (!$item instanceof CacheItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$item = (array) $item;
|
||||||
|
if (null === $item["\0*\0expiry"] && 0 < $this->defaultLifetime) {
|
||||||
|
$item["\0*\0expiry"] = microtime(true) + $this->defaultLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) {
|
||||||
|
$innerItem = $item["\0*\0innerItem"];
|
||||||
|
} elseif ($this->pool instanceof AdapterInterface) {
|
||||||
|
// this is an optimization specific for AdapterInterface implementations
|
||||||
|
// so we can save a round-trip to the backend by just creating a new item
|
||||||
|
$innerItem = (self::$createCacheItem)($this->namespace.$item["\0*\0key"], null, $this->poolHash);
|
||||||
|
} else {
|
||||||
|
$innerItem = $this->pool->getItem($this->namespace.$item["\0*\0key"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
(self::$setInnerItem)($innerItem, $item);
|
||||||
|
|
||||||
|
return $this->pool->$method($innerItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateItems(iterable $items): \Generator
|
||||||
|
{
|
||||||
|
$f = self::$createCacheItem;
|
||||||
|
|
||||||
|
foreach ($items as $key => $item) {
|
||||||
|
if ($this->namespaceLen) {
|
||||||
|
$key = substr($key, $this->namespaceLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $key => $f($key, $item, $this->poolHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getId($key): string
|
||||||
|
{
|
||||||
|
\assert('' !== CacheItem::validateKey($key));
|
||||||
|
|
||||||
|
return $this->namespace.$key;
|
||||||
|
}
|
||||||
|
}
|
||||||
86
vendor/symfony/cache/Adapter/Psr16Adapter.php
vendored
Normal file
86
vendor/symfony/cache/Adapter/Psr16Adapter.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns a PSR-16 cache into a PSR-6 one.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class Psr16Adapter extends AbstractAdapter implements PruneableInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
use ProxyTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected const NS_SEPARATOR = '_';
|
||||||
|
|
||||||
|
private $miss;
|
||||||
|
|
||||||
|
public function __construct(CacheInterface $pool, string $namespace = '', int $defaultLifetime = 0)
|
||||||
|
{
|
||||||
|
parent::__construct($namespace, $defaultLifetime);
|
||||||
|
|
||||||
|
$this->pool = $pool;
|
||||||
|
$this->miss = new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) {
|
||||||
|
if ($this->miss !== $value) {
|
||||||
|
yield $key => $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave(string $id)
|
||||||
|
{
|
||||||
|
return $this->pool->has($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear(string $namespace)
|
||||||
|
{
|
||||||
|
return $this->pool->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
return $this->pool->deleteMultiple($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime)
|
||||||
|
{
|
||||||
|
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
vendor/symfony/cache/Adapter/RedisAdapter.php
vendored
Normal file
32
vendor/symfony/cache/Adapter/RedisAdapter.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisTrait;
|
||||||
|
|
||||||
|
class RedisAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
use RedisTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client
|
||||||
|
* @param string $namespace The default namespace
|
||||||
|
* @param int $defaultLifetime The default lifetime
|
||||||
|
*/
|
||||||
|
public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
$this->init($redis, $namespace, $defaultLifetime, $marshaller);
|
||||||
|
}
|
||||||
|
}
|
||||||
325
vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php
vendored
Normal file
325
vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Predis\Connection\Aggregate\ClusterInterface;
|
||||||
|
use Predis\Connection\Aggregate\PredisCluster;
|
||||||
|
use Predis\Connection\Aggregate\ReplicationInterface;
|
||||||
|
use Predis\Response\ErrorInterface;
|
||||||
|
use Predis\Response\Status;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Exception\LogicException;
|
||||||
|
use Symfony\Component\Cache\Marshaller\DeflateMarshaller;
|
||||||
|
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||||
|
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||||
|
use Symfony\Component\Cache\Traits\RedisTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores tag id <> cache id relationship as a Redis Set.
|
||||||
|
*
|
||||||
|
* Set (tag relation info) is stored without expiry (non-volatile), while cache always gets an expiry (volatile) even
|
||||||
|
* if not set by caller. Thus if you configure redis with the right eviction policy you can be safe this tag <> cache
|
||||||
|
* relationship survives eviction (cache cleanup when Redis runs out of memory).
|
||||||
|
*
|
||||||
|
* Redis server 2.8+ with any `volatile-*` eviction policy, OR `noeviction` if you're sure memory will NEVER fill up
|
||||||
|
*
|
||||||
|
* Design limitations:
|
||||||
|
* - Max 4 billion cache keys per cache tag as limited by Redis Set datatype.
|
||||||
|
* E.g. If you use a "all" items tag for expiry instead of clear(), that limits you to 4 billion cache items also.
|
||||||
|
*
|
||||||
|
* @see https://redis.io/topics/lru-cache#eviction-policies Documentation for Redis eviction policies.
|
||||||
|
* @see https://redis.io/topics/data-types#sets Documentation for Redis Set datatype.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
* @author André Rømcke <andre.romcke+symfony@gmail.com>
|
||||||
|
*/
|
||||||
|
class RedisTagAwareAdapter extends AbstractTagAwareAdapter
|
||||||
|
{
|
||||||
|
use RedisTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On cache items without a lifetime set, we set it to 100 days. This is to make sure cache items are
|
||||||
|
* preferred to be evicted over tag Sets, if eviction policy is configured according to requirements.
|
||||||
|
*/
|
||||||
|
private const DEFAULT_CACHE_TTL = 8640000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null detected eviction policy used on Redis server
|
||||||
|
*/
|
||||||
|
private $redisEvictionPolicy;
|
||||||
|
private $namespace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client
|
||||||
|
* @param string $namespace The default namespace
|
||||||
|
* @param int $defaultLifetime The default lifetime
|
||||||
|
*/
|
||||||
|
public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, ?MarshallerInterface $marshaller = null)
|
||||||
|
{
|
||||||
|
if ($redis instanceof \Predis\ClientInterface && $redis->getConnection() instanceof ClusterInterface && !$redis->getConnection() instanceof PredisCluster) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, get_debug_type($redis->getConnection())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\defined('Redis::OPT_COMPRESSION') && ($redis instanceof \Redis || $redis instanceof \RedisArray || $redis instanceof \RedisCluster)) {
|
||||||
|
$compression = $redis->getOption(\Redis::OPT_COMPRESSION);
|
||||||
|
|
||||||
|
foreach (\is_array($compression) ? $compression : [$compression] as $c) {
|
||||||
|
if (\Redis::COMPRESSION_NONE !== $c) {
|
||||||
|
throw new InvalidArgumentException(sprintf('phpredis compression must be disabled when using "%s", use "%s" instead.', static::class, DeflateMarshaller::class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->init($redis, $namespace, $defaultLifetime, new TagAwareMarshaller($marshaller));
|
||||||
|
$this->namespace = $namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, int $lifetime, array $addTagData = [], array $delTagData = []): array
|
||||||
|
{
|
||||||
|
$eviction = $this->getRedisEvictionPolicy();
|
||||||
|
if ('noeviction' !== $eviction && !str_starts_with($eviction, 'volatile-')) {
|
||||||
|
throw new LogicException(sprintf('Redis maxmemory-policy setting "%s" is *not* supported by RedisTagAwareAdapter, use "noeviction" or "volatile-*" eviction policies.', $eviction));
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialize values
|
||||||
|
if (!$serialized = $this->marshaller->marshall($values, $failed)) {
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// While pipeline isn't supported on RedisCluster, other setups will at least benefit from doing this in one op
|
||||||
|
$results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData, $failed) {
|
||||||
|
// Store cache items, force a ttl if none is set, as there is no MSETEX we need to set each one
|
||||||
|
foreach ($serialized as $id => $value) {
|
||||||
|
yield 'setEx' => [
|
||||||
|
$id,
|
||||||
|
0 >= $lifetime ? self::DEFAULT_CACHE_TTL : $lifetime,
|
||||||
|
$value,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add and Remove Tags
|
||||||
|
foreach ($addTagData as $tagId => $ids) {
|
||||||
|
if (!$failed || $ids = array_diff($ids, $failed)) {
|
||||||
|
yield 'sAdd' => array_merge([$tagId], $ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($delTagData as $tagId => $ids) {
|
||||||
|
if (!$failed || $ids = array_diff($ids, $failed)) {
|
||||||
|
yield 'sRem' => array_merge([$tagId], $ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($results as $id => $result) {
|
||||||
|
// Skip results of SADD/SREM operations, they'll be 1 or 0 depending on if set value already existed or not
|
||||||
|
if (is_numeric($result)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// setEx results
|
||||||
|
if (true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) {
|
||||||
|
$failed[] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDeleteYieldTags(array $ids): iterable
|
||||||
|
{
|
||||||
|
$lua = <<<'EOLUA'
|
||||||
|
local v = redis.call('GET', KEYS[1])
|
||||||
|
local e = redis.pcall('UNLINK', KEYS[1])
|
||||||
|
|
||||||
|
if type(e) ~= 'number' then
|
||||||
|
redis.call('DEL', KEYS[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if not v or v:len() <= 13 or v:byte(1) ~= 0x9D or v:byte(6) ~= 0 or v:byte(10) ~= 0x5F then
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
|
||||||
|
return v:sub(14, 13 + v:byte(13) + v:byte(12) * 256 + v:byte(11) * 65536)
|
||||||
|
EOLUA;
|
||||||
|
|
||||||
|
$results = $this->pipeline(function () use ($ids, $lua) {
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
yield 'eval' => $this->redis instanceof \Predis\ClientInterface ? [$lua, 1, $id] : [$lua, [$id], 1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($results as $id => $result) {
|
||||||
|
if ($result instanceof \RedisException || $result instanceof ErrorInterface) {
|
||||||
|
CacheItem::log($this->logger, 'Failed to delete key "{key}": '.$result->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $result]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
yield $id => !\is_string($result) || '' === $result ? [] : $this->marshaller->unmarshall($result);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
yield $id => [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDeleteTagRelations(array $tagData): bool
|
||||||
|
{
|
||||||
|
$results = $this->pipeline(static function () use ($tagData) {
|
||||||
|
foreach ($tagData as $tagId => $idList) {
|
||||||
|
array_unshift($idList, $tagId);
|
||||||
|
yield 'sRem' => $idList;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
foreach ($results as $result) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doInvalidate(array $tagIds): bool
|
||||||
|
{
|
||||||
|
// This script scans the set of items linked to tag: it empties the set
|
||||||
|
// and removes the linked items. When the set is still not empty after
|
||||||
|
// the scan, it means we're in cluster mode and that the linked items
|
||||||
|
// are on other nodes: we move the links to a temporary set and we
|
||||||
|
// garbage collect that set from the client side.
|
||||||
|
|
||||||
|
$lua = <<<'EOLUA'
|
||||||
|
redis.replicate_commands()
|
||||||
|
|
||||||
|
local cursor = '0'
|
||||||
|
local id = KEYS[1]
|
||||||
|
repeat
|
||||||
|
local result = redis.call('SSCAN', id, cursor, 'COUNT', 5000);
|
||||||
|
cursor = result[1];
|
||||||
|
local rems = {}
|
||||||
|
|
||||||
|
for _, v in ipairs(result[2]) do
|
||||||
|
local ok, _ = pcall(redis.call, 'DEL', ARGV[1]..v)
|
||||||
|
if ok then
|
||||||
|
table.insert(rems, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if 0 < #rems then
|
||||||
|
redis.call('SREM', id, unpack(rems))
|
||||||
|
end
|
||||||
|
until '0' == cursor;
|
||||||
|
|
||||||
|
redis.call('SUNIONSTORE', '{'..id..'}'..id, id)
|
||||||
|
redis.call('DEL', id)
|
||||||
|
|
||||||
|
return redis.call('SSCAN', '{'..id..'}'..id, '0', 'COUNT', 5000)
|
||||||
|
EOLUA;
|
||||||
|
|
||||||
|
$results = $this->pipeline(function () use ($tagIds, $lua) {
|
||||||
|
if ($this->redis instanceof \Predis\ClientInterface) {
|
||||||
|
$prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : '';
|
||||||
|
} elseif (\is_array($prefix = $this->redis->getOption(\Redis::OPT_PREFIX) ?? '')) {
|
||||||
|
$prefix = current($prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($tagIds as $id) {
|
||||||
|
yield 'eval' => $this->redis instanceof \Predis\ClientInterface ? [$lua, 1, $id, $prefix] : [$lua, [$id, $prefix], 1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$lua = <<<'EOLUA'
|
||||||
|
redis.replicate_commands()
|
||||||
|
|
||||||
|
local id = KEYS[1]
|
||||||
|
local cursor = table.remove(ARGV)
|
||||||
|
redis.call('SREM', '{'..id..'}'..id, unpack(ARGV))
|
||||||
|
|
||||||
|
return redis.call('SSCAN', '{'..id..'}'..id, cursor, 'COUNT', 5000)
|
||||||
|
EOLUA;
|
||||||
|
|
||||||
|
$success = true;
|
||||||
|
foreach ($results as $id => $values) {
|
||||||
|
if ($values instanceof \RedisException || $values instanceof ErrorInterface) {
|
||||||
|
CacheItem::log($this->logger, 'Failed to invalidate key "{key}": '.$values->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $values]);
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$cursor, $ids] = $values;
|
||||||
|
|
||||||
|
while ($ids || '0' !== $cursor) {
|
||||||
|
$this->doDelete($ids);
|
||||||
|
|
||||||
|
$evalArgs = [$id, $cursor];
|
||||||
|
array_splice($evalArgs, 1, 0, $ids);
|
||||||
|
|
||||||
|
if ($this->redis instanceof \Predis\ClientInterface) {
|
||||||
|
array_unshift($evalArgs, $lua, 1);
|
||||||
|
} else {
|
||||||
|
$evalArgs = [$lua, $evalArgs, 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $this->pipeline(function () use ($evalArgs) {
|
||||||
|
yield 'eval' => $evalArgs;
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($results as [$cursor, $ids]) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRedisEvictionPolicy(): string
|
||||||
|
{
|
||||||
|
if (null !== $this->redisEvictionPolicy) {
|
||||||
|
return $this->redisEvictionPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hosts = $this->getHosts();
|
||||||
|
$host = reset($hosts);
|
||||||
|
if ($host instanceof \Predis\Client && $host->getConnection() instanceof ReplicationInterface) {
|
||||||
|
// Predis supports info command only on the master in replication environments
|
||||||
|
$hosts = [$host->getClientFor('master')];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($hosts as $host) {
|
||||||
|
$info = $host->info('Memory');
|
||||||
|
|
||||||
|
if ($info instanceof ErrorInterface) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = $info['Memory'] ?? $info;
|
||||||
|
|
||||||
|
return $this->redisEvictionPolicy = $info['maxmemory_policy'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redisEvictionPolicy = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
428
vendor/symfony/cache/Adapter/TagAwareAdapter.php
vendored
Normal file
428
vendor/symfony/cache/Adapter/TagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\InvalidArgumentException;
|
||||||
|
use Psr\Log\LoggerAwareInterface;
|
||||||
|
use Psr\Log\LoggerAwareTrait;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||||
|
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||||
|
use Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface, LoggerAwareInterface
|
||||||
|
{
|
||||||
|
use ContractsTrait;
|
||||||
|
use LoggerAwareTrait;
|
||||||
|
use ProxyTrait;
|
||||||
|
|
||||||
|
public const TAGS_PREFIX = "\0tags\0";
|
||||||
|
|
||||||
|
private $deferred = [];
|
||||||
|
private $tags;
|
||||||
|
private $knownTagVersions = [];
|
||||||
|
private $knownTagVersionsTtl;
|
||||||
|
|
||||||
|
private static $createCacheItem;
|
||||||
|
private static $setCacheItemTags;
|
||||||
|
private static $getTagsByKey;
|
||||||
|
private static $saveTags;
|
||||||
|
|
||||||
|
public function __construct(AdapterInterface $itemsPool, ?AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15)
|
||||||
|
{
|
||||||
|
$this->pool = $itemsPool;
|
||||||
|
$this->tags = $tagsPool ?: $itemsPool;
|
||||||
|
$this->knownTagVersionsTtl = $knownTagVersionsTtl;
|
||||||
|
self::$createCacheItem ?? self::$createCacheItem = \Closure::bind(
|
||||||
|
static function ($key, $value, CacheItem $protoItem) {
|
||||||
|
$item = new CacheItem();
|
||||||
|
$item->key = $key;
|
||||||
|
$item->value = $value;
|
||||||
|
$item->expiry = $protoItem->expiry;
|
||||||
|
$item->poolHash = $protoItem->poolHash;
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
self::$setCacheItemTags ?? self::$setCacheItemTags = \Closure::bind(
|
||||||
|
static function (CacheItem $item, $key, array &$itemTags) {
|
||||||
|
$item->isTaggable = true;
|
||||||
|
if (!$item->isHit) {
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
if (isset($itemTags[$key])) {
|
||||||
|
foreach ($itemTags[$key] as $tag => $version) {
|
||||||
|
$item->metadata[CacheItem::METADATA_TAGS][$tag] = $tag;
|
||||||
|
}
|
||||||
|
unset($itemTags[$key]);
|
||||||
|
} else {
|
||||||
|
$item->value = null;
|
||||||
|
$item->isHit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
self::$getTagsByKey ?? self::$getTagsByKey = \Closure::bind(
|
||||||
|
static function ($deferred) {
|
||||||
|
$tagsByKey = [];
|
||||||
|
foreach ($deferred as $key => $item) {
|
||||||
|
$tagsByKey[$key] = $item->newMetadata[CacheItem::METADATA_TAGS] ?? [];
|
||||||
|
$item->metadata = $item->newMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tagsByKey;
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
self::$saveTags ?? self::$saveTags = \Closure::bind(
|
||||||
|
static function (AdapterInterface $tagsAdapter, array $tags) {
|
||||||
|
ksort($tags);
|
||||||
|
|
||||||
|
foreach ($tags as $v) {
|
||||||
|
$v->expiry = 0;
|
||||||
|
$tagsAdapter->saveDeferred($v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tagsAdapter->commit();
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
CacheItem::class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function invalidateTags(array $tags)
|
||||||
|
{
|
||||||
|
$ids = [];
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
\assert('' !== CacheItem::validateKey($tag));
|
||||||
|
unset($this->knownTagVersions[$tag]);
|
||||||
|
$ids[] = $tag.static::TAGS_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !$tags || $this->tags->deleteItems($ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
if (\is_string($key) && isset($this->deferred[$key])) {
|
||||||
|
$this->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->pool->hasItem($key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key);
|
||||||
|
|
||||||
|
if (!$itemTags->isHit()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$itemTags = $itemTags->get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->getTagVersions([$itemTags]) as $tag => $version) {
|
||||||
|
if ($itemTags[$tag] !== $version) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
foreach ($this->getItems([$key]) as $item) {
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
$tagKeys = [];
|
||||||
|
$commit = false;
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if ('' !== $key && \is_string($key)) {
|
||||||
|
$commit = $commit || isset($this->deferred[$key]);
|
||||||
|
$key = static::TAGS_PREFIX.$key;
|
||||||
|
$tagKeys[$key] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($commit) {
|
||||||
|
$this->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$items = $this->pool->getItems($tagKeys + $keys);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
$this->pool->getItems($keys); // Should throw an exception
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->generateItems($items, $tagKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
if ('' !== $prefix) {
|
||||||
|
foreach ($this->deferred as $key => $item) {
|
||||||
|
if (str_starts_with($key, $prefix)) {
|
||||||
|
unset($this->deferred[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->deferred = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->pool instanceof AdapterInterface) {
|
||||||
|
return $this->pool->clear($prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pool->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
return $this->deleteItems([$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if ('' !== $key && \is_string($key)) {
|
||||||
|
$keys[] = static::TAGS_PREFIX.$key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pool->deleteItems($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
if (!$item instanceof CacheItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->deferred[$item->getKey()] = $item;
|
||||||
|
|
||||||
|
return $this->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
if (!$item instanceof CacheItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->deferred[$item->getKey()] = $item;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
if (!$this->deferred) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ok = true;
|
||||||
|
foreach ($this->deferred as $key => $item) {
|
||||||
|
if (!$this->pool->saveDeferred($item)) {
|
||||||
|
unset($this->deferred[$key]);
|
||||||
|
$ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $this->deferred;
|
||||||
|
$tagsByKey = (self::$getTagsByKey)($items);
|
||||||
|
$this->deferred = [];
|
||||||
|
|
||||||
|
$tagVersions = $this->getTagVersions($tagsByKey);
|
||||||
|
$f = self::$createCacheItem;
|
||||||
|
|
||||||
|
foreach ($tagsByKey as $key => $tags) {
|
||||||
|
$this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pool->commit() && $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function __sleep()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __wakeup()
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
$this->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateItems(iterable $items, array $tagKeys): \Generator
|
||||||
|
{
|
||||||
|
$bufferedItems = $itemTags = [];
|
||||||
|
$f = self::$setCacheItemTags;
|
||||||
|
|
||||||
|
foreach ($items as $key => $item) {
|
||||||
|
if (!$tagKeys) {
|
||||||
|
yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!isset($tagKeys[$key])) {
|
||||||
|
$bufferedItems[$key] = $item;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($tagKeys[$key]);
|
||||||
|
|
||||||
|
if ($item->isHit()) {
|
||||||
|
$itemTags[$key] = $item->get() ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$tagKeys) {
|
||||||
|
$tagVersions = $this->getTagVersions($itemTags);
|
||||||
|
|
||||||
|
foreach ($itemTags as $key => $tags) {
|
||||||
|
foreach ($tags as $tag => $version) {
|
||||||
|
if ($tagVersions[$tag] !== $version) {
|
||||||
|
unset($itemTags[$key]);
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$tagVersions = $tagKeys = null;
|
||||||
|
|
||||||
|
foreach ($bufferedItems as $key => $item) {
|
||||||
|
yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags);
|
||||||
|
}
|
||||||
|
$bufferedItems = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTagVersions(array $tagsByKey)
|
||||||
|
{
|
||||||
|
$tagVersions = [];
|
||||||
|
$fetchTagVersions = false;
|
||||||
|
|
||||||
|
foreach ($tagsByKey as $tags) {
|
||||||
|
$tagVersions += $tags;
|
||||||
|
|
||||||
|
foreach ($tags as $tag => $version) {
|
||||||
|
if ($tagVersions[$tag] !== $version) {
|
||||||
|
unset($this->knownTagVersions[$tag]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$tagVersions) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$now = microtime(true);
|
||||||
|
$tags = [];
|
||||||
|
foreach ($tagVersions as $tag => $version) {
|
||||||
|
$tags[$tag.static::TAGS_PREFIX] = $tag;
|
||||||
|
if ($fetchTagVersions || ($this->knownTagVersions[$tag][1] ?? null) !== $version || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
|
||||||
|
// reuse previously fetched tag versions up to the ttl
|
||||||
|
$fetchTagVersions = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$fetchTagVersions) {
|
||||||
|
return $tagVersions;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newTags = [];
|
||||||
|
$newVersion = null;
|
||||||
|
foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) {
|
||||||
|
if (!$version->isHit()) {
|
||||||
|
$newTags[$tag] = $version->set($newVersion ?? $newVersion = random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||||
|
}
|
||||||
|
$tagVersions[$tag = $tags[$tag]] = $version->get();
|
||||||
|
$this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($newTags) {
|
||||||
|
(self::$saveTags)($this->tags, $newTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tagVersions;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php
vendored
Normal file
33
vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for invalidating cached items using tags.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
interface TagAwareAdapterInterface extends AdapterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Invalidates cached items using tags.
|
||||||
|
*
|
||||||
|
* @param string[] $tags An array of tags to invalidate
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When $tags is not valid
|
||||||
|
*/
|
||||||
|
public function invalidateTags(array $tags);
|
||||||
|
}
|
||||||
295
vendor/symfony/cache/Adapter/TraceableAdapter.php
vendored
Normal file
295
vendor/symfony/cache/Adapter/TraceableAdapter.php
vendored
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Symfony\Component\Cache\CacheItem;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
|
use Symfony\Component\Cache\ResettableInterface;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
use Symfony\Contracts\Service\ResetInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adapter that collects data about all cache calls.
|
||||||
|
*
|
||||||
|
* @author Aaron Scherer <aequasi@gmail.com>
|
||||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class TraceableAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||||
|
{
|
||||||
|
protected $pool;
|
||||||
|
private $calls = [];
|
||||||
|
|
||||||
|
public function __construct(AdapterInterface $pool)
|
||||||
|
{
|
||||||
|
$this->pool = $pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null)
|
||||||
|
{
|
||||||
|
if (!$this->pool instanceof CacheInterface) {
|
||||||
|
throw new \BadMethodCallException(sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".', get_debug_type($this->pool), CacheInterface::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
$isHit = true;
|
||||||
|
$callback = function (CacheItem $item, bool &$save) use ($callback, &$isHit) {
|
||||||
|
$isHit = $item->isHit();
|
||||||
|
|
||||||
|
return $callback($item, $save);
|
||||||
|
};
|
||||||
|
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
$value = $this->pool->get($key, $callback, $beta, $metadata);
|
||||||
|
$event->result[$key] = get_debug_type($value);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
if ($isHit) {
|
||||||
|
++$event->hits;
|
||||||
|
} else {
|
||||||
|
++$event->misses;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItem($key)
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
$item = $this->pool->getItem($key);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
if ($event->result[$key] = $item->isHit()) {
|
||||||
|
++$event->hits;
|
||||||
|
} else {
|
||||||
|
++$event->misses;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasItem($key)
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result[$key] = $this->pool->hasItem($key);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItem($key)
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result[$key] = $this->pool->deleteItem($key);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function save(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result[$item->getKey()] = $this->pool->save($item);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function saveDeferred(CacheItemInterface $item)
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result[$item->getKey()] = $this->pool->saveDeferred($item);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getItems(array $keys = [])
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
$result = $this->pool->getItems($keys);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
$f = function () use ($result, $event) {
|
||||||
|
$event->result = [];
|
||||||
|
foreach ($result as $key => $item) {
|
||||||
|
if ($event->result[$key] = $item->isHit()) {
|
||||||
|
++$event->hits;
|
||||||
|
} else {
|
||||||
|
++$event->misses;
|
||||||
|
}
|
||||||
|
yield $key => $item;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return $f();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function clear(string $prefix = '')
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
if ($this->pool instanceof AdapterInterface) {
|
||||||
|
return $event->result = $this->pool->clear($prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $event->result = $this->pool->clear();
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteItems(array $keys)
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
$event->result['keys'] = $keys;
|
||||||
|
try {
|
||||||
|
return $event->result['result'] = $this->pool->deleteItems($keys);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function commit()
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result = $this->pool->commit();
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function prune()
|
||||||
|
{
|
||||||
|
if (!$this->pool instanceof PruneableInterface) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result = $this->pool->prune();
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
if ($this->pool instanceof ResetInterface) {
|
||||||
|
$this->pool->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->clearCalls();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(string $key): bool
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result[$key] = $this->pool->deleteItem($key);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCalls()
|
||||||
|
{
|
||||||
|
return $this->calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clearCalls()
|
||||||
|
{
|
||||||
|
$this->calls = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function start(string $name)
|
||||||
|
{
|
||||||
|
$this->calls[] = $event = new TraceableAdapterEvent();
|
||||||
|
$event->name = $name;
|
||||||
|
$event->start = microtime(true);
|
||||||
|
|
||||||
|
return $event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TraceableAdapterEvent
|
||||||
|
{
|
||||||
|
public $name;
|
||||||
|
public $start;
|
||||||
|
public $end;
|
||||||
|
public $result;
|
||||||
|
public $hits = 0;
|
||||||
|
public $misses = 0;
|
||||||
|
}
|
||||||
38
vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php
vendored
Normal file
38
vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||||
|
*/
|
||||||
|
class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface
|
||||||
|
{
|
||||||
|
public function __construct(TagAwareAdapterInterface $pool)
|
||||||
|
{
|
||||||
|
parent::__construct($pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function invalidateTags(array $tags)
|
||||||
|
{
|
||||||
|
$event = $this->start(__FUNCTION__);
|
||||||
|
try {
|
||||||
|
return $event->result = $this->pool->invalidateTags($tags);
|
||||||
|
} finally {
|
||||||
|
$event->end = microtime(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
vendor/symfony/cache/CHANGELOG.md
vendored
Normal file
108
vendor/symfony/cache/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
5.4
|
||||||
|
---
|
||||||
|
|
||||||
|
* Deprecate `DoctrineProvider` and `DoctrineAdapter` because these classes have been added to the `doctrine/cache` package
|
||||||
|
* Add `DoctrineDbalAdapter` identical to `PdoAdapter` for `Doctrine\DBAL\Connection` or DBAL URL
|
||||||
|
* Deprecate usage of `PdoAdapter` with `Doctrine\DBAL\Connection` or DBAL URL
|
||||||
|
|
||||||
|
5.3
|
||||||
|
---
|
||||||
|
|
||||||
|
* added support for connecting to Redis Sentinel clusters when using the Redis PHP extension
|
||||||
|
* add support for a custom serializer to the `ApcuAdapter` class
|
||||||
|
|
||||||
|
5.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added integration with Messenger to allow computing cached values in a worker
|
||||||
|
* allow ISO 8601 time intervals to specify default lifetime
|
||||||
|
|
||||||
|
5.1.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added max-items + LRU + max-lifetime capabilities to `ArrayCache`
|
||||||
|
* added `CouchbaseBucketAdapter`
|
||||||
|
* added context `cache-adapter` to log messages
|
||||||
|
|
||||||
|
5.0.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* removed all PSR-16 implementations in the `Simple` namespace
|
||||||
|
* removed `SimpleCacheAdapter`
|
||||||
|
* removed `AbstractAdapter::unserialize()`
|
||||||
|
* removed `CacheItem::getPreviousTags()`
|
||||||
|
|
||||||
|
4.4.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added support for connecting to Redis Sentinel clusters
|
||||||
|
* added argument `$prefix` to `AdapterInterface::clear()`
|
||||||
|
* improved `RedisTagAwareAdapter` to support Redis server >= 2.8 and up to 4B items per tag
|
||||||
|
* added `TagAwareMarshaller` for optimized data storage when using `AbstractTagAwareAdapter`
|
||||||
|
* added `DeflateMarshaller` to compress serialized values
|
||||||
|
* removed support for phpredis 4 `compression`
|
||||||
|
* [BC BREAK] `RedisTagAwareAdapter` is not compatible with `RedisCluster` from `Predis` anymore, use `phpredis` instead
|
||||||
|
* Marked the `CacheDataCollector` class as `@final`.
|
||||||
|
* added `SodiumMarshaller` to encrypt/decrypt values using libsodium
|
||||||
|
|
||||||
|
4.3.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* removed `psr/simple-cache` dependency, run `composer require psr/simple-cache` if you need it
|
||||||
|
* deprecated all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead
|
||||||
|
* deprecated `SimpleCacheAdapter`, use `Psr16Adapter` instead
|
||||||
|
|
||||||
|
4.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added support for connecting to Redis clusters via DSN
|
||||||
|
* added support for configuring multiple Memcached servers via DSN
|
||||||
|
* added `MarshallerInterface` and `DefaultMarshaller` to allow changing the serializer and provide one that automatically uses igbinary when available
|
||||||
|
* implemented `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
|
||||||
|
* added sub-second expiry accuracy for backends that support it
|
||||||
|
* added support for phpredis 4 `compression` and `tcp_keepalive` options
|
||||||
|
* added automatic table creation when using Doctrine DBAL with PDO-based backends
|
||||||
|
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
|
||||||
|
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
|
||||||
|
* deprecated the `AbstractAdapter::unserialize()` and `AbstractCache::unserialize()` methods
|
||||||
|
* added `CacheCollectorPass` (originally in `FrameworkBundle`)
|
||||||
|
* added `CachePoolClearerPass` (originally in `FrameworkBundle`)
|
||||||
|
* added `CachePoolPass` (originally in `FrameworkBundle`)
|
||||||
|
* added `CachePoolPrunerPass` (originally in `FrameworkBundle`)
|
||||||
|
|
||||||
|
3.4.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added using options from Memcached DSN
|
||||||
|
* added PruneableInterface so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning
|
||||||
|
* added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, TagAwareAdapter and ChainTrait
|
||||||
|
* now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, PhpFilesCache, PdoAdapter, PdoCache, ChainAdapter, and
|
||||||
|
ChainCache implement PruneableInterface and support manual stale cache pruning
|
||||||
|
|
||||||
|
3.3.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any
|
||||||
|
* added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters
|
||||||
|
* added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16
|
||||||
|
* added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16)
|
||||||
|
* added TraceableAdapter (PSR-6) and TraceableCache (PSR-16)
|
||||||
|
|
||||||
|
3.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added TagAwareAdapter for tags-based invalidation
|
||||||
|
* added PdoAdapter with PDO and Doctrine DBAL support
|
||||||
|
* added PhpArrayAdapter and PhpFilesAdapter for OPcache-backed shared memory storage (PHP 7+ only)
|
||||||
|
* added NullAdapter
|
||||||
|
|
||||||
|
3.1.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added the component with strict PSR-6 implementations
|
||||||
|
* added ApcuAdapter, ArrayAdapter, FilesystemAdapter and RedisAdapter
|
||||||
|
* added AbstractAdapter, ChainAdapter and ProxyAdapter
|
||||||
|
* added DoctrineAdapter and DoctrineProvider for bidirectional interoperability with Doctrine Cache
|
||||||
192
vendor/symfony/cache/CacheItem.php
vendored
Normal file
192
vendor/symfony/cache/CacheItem.php
vendored
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Cache\Exception\LogicException;
|
||||||
|
use Symfony\Contracts\Cache\ItemInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
final class CacheItem implements ItemInterface
|
||||||
|
{
|
||||||
|
private const METADATA_EXPIRY_OFFSET = 1527506807;
|
||||||
|
|
||||||
|
protected $key;
|
||||||
|
protected $value;
|
||||||
|
protected $isHit = false;
|
||||||
|
protected $expiry;
|
||||||
|
protected $metadata = [];
|
||||||
|
protected $newMetadata = [];
|
||||||
|
protected $innerItem;
|
||||||
|
protected $poolHash;
|
||||||
|
protected $isTaggable = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getKey(): string
|
||||||
|
{
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isHit(): bool
|
||||||
|
{
|
||||||
|
return $this->isHit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function set($value): self
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function expiresAt($expiration): self
|
||||||
|
{
|
||||||
|
if (null === $expiration) {
|
||||||
|
$this->expiry = null;
|
||||||
|
} elseif ($expiration instanceof \DateTimeInterface) {
|
||||||
|
$this->expiry = (float) $expiration->format('U.u');
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException(sprintf('Expiration date must implement DateTimeInterface or be null, "%s" given.', get_debug_type($expiration)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function expiresAfter($time): self
|
||||||
|
{
|
||||||
|
if (null === $time) {
|
||||||
|
$this->expiry = null;
|
||||||
|
} elseif ($time instanceof \DateInterval) {
|
||||||
|
$this->expiry = microtime(true) + \DateTime::createFromFormat('U', 0)->add($time)->format('U.u');
|
||||||
|
} elseif (\is_int($time)) {
|
||||||
|
$this->expiry = $time + microtime(true);
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', get_debug_type($time)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function tag($tags): ItemInterface
|
||||||
|
{
|
||||||
|
if (!$this->isTaggable) {
|
||||||
|
throw new LogicException(sprintf('Cache item "%s" comes from a non tag-aware pool: you cannot tag it.', $this->key));
|
||||||
|
}
|
||||||
|
if (!is_iterable($tags)) {
|
||||||
|
$tags = [$tags];
|
||||||
|
}
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
if (!\is_string($tag) && !(\is_object($tag) && method_exists($tag, '__toString'))) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache tag must be string or object that implements __toString(), "%s" given.', \is_object($tag) ? \get_class($tag) : \gettype($tag)));
|
||||||
|
}
|
||||||
|
$tag = (string) $tag;
|
||||||
|
if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ('' === $tag) {
|
||||||
|
throw new InvalidArgumentException('Cache tag length must be greater than zero.');
|
||||||
|
}
|
||||||
|
if (false !== strpbrk($tag, self::RESERVED_CHARACTERS)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters "%s".', $tag, self::RESERVED_CHARACTERS));
|
||||||
|
}
|
||||||
|
$this->newMetadata[self::METADATA_TAGS][$tag] = $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getMetadata(): array
|
||||||
|
{
|
||||||
|
return $this->metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a cache key according to PSR-6.
|
||||||
|
*
|
||||||
|
* @param mixed $key The key to validate
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException When $key is not valid
|
||||||
|
*/
|
||||||
|
public static function validateKey($key): string
|
||||||
|
{
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', get_debug_type($key)));
|
||||||
|
}
|
||||||
|
if ('' === $key) {
|
||||||
|
throw new InvalidArgumentException('Cache key length must be greater than zero.');
|
||||||
|
}
|
||||||
|
if (false !== strpbrk($key, self::RESERVED_CHARACTERS)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal logging helper.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static function log(?LoggerInterface $logger, string $message, array $context = [])
|
||||||
|
{
|
||||||
|
if ($logger) {
|
||||||
|
$logger->warning($message, $context);
|
||||||
|
} else {
|
||||||
|
$replace = [];
|
||||||
|
foreach ($context as $k => $v) {
|
||||||
|
if (\is_scalar($v)) {
|
||||||
|
$replace['{'.$k.'}'] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@trigger_error(strtr($message, $replace), \E_USER_WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
183
vendor/symfony/cache/DataCollector/CacheDataCollector.php
vendored
Normal file
183
vendor/symfony/cache/DataCollector/CacheDataCollector.php
vendored
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\DataCollector;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||||
|
use Symfony\Component\Cache\Adapter\TraceableAdapterEvent;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aaron Scherer <aequasi@gmail.com>
|
||||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class CacheDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TraceableAdapter[]
|
||||||
|
*/
|
||||||
|
private $instances = [];
|
||||||
|
|
||||||
|
public function addInstance(string $name, TraceableAdapter $instance)
|
||||||
|
{
|
||||||
|
$this->instances[$name] = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function collect(Request $request, Response $response, ?\Throwable $exception = null)
|
||||||
|
{
|
||||||
|
$empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []];
|
||||||
|
$this->data = ['instances' => $empty, 'total' => $empty];
|
||||||
|
foreach ($this->instances as $name => $instance) {
|
||||||
|
$this->data['instances']['calls'][$name] = $instance->getCalls();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->data['instances']['statistics'] = $this->calculateStatistics();
|
||||||
|
$this->data['total']['statistics'] = $this->calculateTotalStatistics();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
$this->data = [];
|
||||||
|
foreach ($this->instances as $instance) {
|
||||||
|
$instance->clearCalls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lateCollect()
|
||||||
|
{
|
||||||
|
$this->data['instances']['calls'] = $this->cloneVar($this->data['instances']['calls']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'cache';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method returns amount of logged Cache reads: "get" calls.
|
||||||
|
*/
|
||||||
|
public function getStatistics(): array
|
||||||
|
{
|
||||||
|
return $this->data['instances']['statistics'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method returns the statistic totals.
|
||||||
|
*/
|
||||||
|
public function getTotals(): array
|
||||||
|
{
|
||||||
|
return $this->data['total']['statistics'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method returns all logged Cache call objects.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getCalls()
|
||||||
|
{
|
||||||
|
return $this->data['instances']['calls'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateStatistics(): array
|
||||||
|
{
|
||||||
|
$statistics = [];
|
||||||
|
foreach ($this->data['instances']['calls'] as $name => $calls) {
|
||||||
|
$statistics[$name] = [
|
||||||
|
'calls' => 0,
|
||||||
|
'time' => 0,
|
||||||
|
'reads' => 0,
|
||||||
|
'writes' => 0,
|
||||||
|
'deletes' => 0,
|
||||||
|
'hits' => 0,
|
||||||
|
'misses' => 0,
|
||||||
|
];
|
||||||
|
/** @var TraceableAdapterEvent $call */
|
||||||
|
foreach ($calls as $call) {
|
||||||
|
++$statistics[$name]['calls'];
|
||||||
|
$statistics[$name]['time'] += ($call->end ?? microtime(true)) - $call->start;
|
||||||
|
if ('get' === $call->name) {
|
||||||
|
++$statistics[$name]['reads'];
|
||||||
|
if ($call->hits) {
|
||||||
|
++$statistics[$name]['hits'];
|
||||||
|
} else {
|
||||||
|
++$statistics[$name]['misses'];
|
||||||
|
++$statistics[$name]['writes'];
|
||||||
|
}
|
||||||
|
} elseif ('getItem' === $call->name) {
|
||||||
|
++$statistics[$name]['reads'];
|
||||||
|
if ($call->hits) {
|
||||||
|
++$statistics[$name]['hits'];
|
||||||
|
} else {
|
||||||
|
++$statistics[$name]['misses'];
|
||||||
|
}
|
||||||
|
} elseif ('getItems' === $call->name) {
|
||||||
|
$statistics[$name]['reads'] += $call->hits + $call->misses;
|
||||||
|
$statistics[$name]['hits'] += $call->hits;
|
||||||
|
$statistics[$name]['misses'] += $call->misses;
|
||||||
|
} elseif ('hasItem' === $call->name) {
|
||||||
|
++$statistics[$name]['reads'];
|
||||||
|
foreach ($call->result ?? [] as $result) {
|
||||||
|
++$statistics[$name][$result ? 'hits' : 'misses'];
|
||||||
|
}
|
||||||
|
} elseif ('save' === $call->name) {
|
||||||
|
++$statistics[$name]['writes'];
|
||||||
|
} elseif ('deleteItem' === $call->name) {
|
||||||
|
++$statistics[$name]['deletes'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($statistics[$name]['reads']) {
|
||||||
|
$statistics[$name]['hit_read_ratio'] = round(100 * $statistics[$name]['hits'] / $statistics[$name]['reads'], 2);
|
||||||
|
} else {
|
||||||
|
$statistics[$name]['hit_read_ratio'] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $statistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateTotalStatistics(): array
|
||||||
|
{
|
||||||
|
$statistics = $this->getStatistics();
|
||||||
|
$totals = [
|
||||||
|
'calls' => 0,
|
||||||
|
'time' => 0,
|
||||||
|
'reads' => 0,
|
||||||
|
'writes' => 0,
|
||||||
|
'deletes' => 0,
|
||||||
|
'hits' => 0,
|
||||||
|
'misses' => 0,
|
||||||
|
];
|
||||||
|
foreach ($statistics as $name => $values) {
|
||||||
|
foreach ($totals as $key => $value) {
|
||||||
|
$totals[$key] += $statistics[$name][$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($totals['reads']) {
|
||||||
|
$totals['hit_read_ratio'] = round(100 * $totals['hits'] / $totals['reads'], 2);
|
||||||
|
} else {
|
||||||
|
$totals['hit_read_ratio'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $totals;
|
||||||
|
}
|
||||||
|
}
|
||||||
94
vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php
vendored
Normal file
94
vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
|
||||||
|
use Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||||
|
use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter;
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject a data collector to all the cache services to be able to get detailed statistics.
|
||||||
|
*
|
||||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
*/
|
||||||
|
class CacheCollectorPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
private $dataCollectorCacheId;
|
||||||
|
private $cachePoolTag;
|
||||||
|
private $cachePoolRecorderInnerSuffix;
|
||||||
|
|
||||||
|
public function __construct(string $dataCollectorCacheId = 'data_collector.cache', string $cachePoolTag = 'cache.pool', string $cachePoolRecorderInnerSuffix = '.recorder_inner')
|
||||||
|
{
|
||||||
|
if (0 < \func_num_args()) {
|
||||||
|
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dataCollectorCacheId = $dataCollectorCacheId;
|
||||||
|
$this->cachePoolTag = $cachePoolTag;
|
||||||
|
$this->cachePoolRecorderInnerSuffix = $cachePoolRecorderInnerSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
if (!$container->hasDefinition($this->dataCollectorCacheId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) {
|
||||||
|
$poolName = $attributes[0]['name'] ?? $id;
|
||||||
|
|
||||||
|
$this->addToCollector($id, $poolName, $container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addToCollector(string $id, string $name, ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$definition = $container->getDefinition($id);
|
||||||
|
if ($definition->isAbstract()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$collectorDefinition = $container->getDefinition($this->dataCollectorCacheId);
|
||||||
|
$recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
|
||||||
|
$recorder->setTags($definition->getTags());
|
||||||
|
if (!$definition->isPublic() || !$definition->isPrivate()) {
|
||||||
|
$recorder->setPublic($definition->isPublic());
|
||||||
|
}
|
||||||
|
$recorder->setArguments([new Reference($innerId = $id.$this->cachePoolRecorderInnerSuffix)]);
|
||||||
|
|
||||||
|
foreach ($definition->getMethodCalls() as [$method, $args]) {
|
||||||
|
if ('setCallbackWrapper' !== $method || !$args[0] instanceof Definition || !($args[0]->getArguments()[2] ?? null) instanceof Definition) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ([new Reference($id), 'setCallbackWrapper'] == $args[0]->getArguments()[2]->getFactory()) {
|
||||||
|
$args[0]->getArguments()[2]->setFactory([new Reference($innerId), 'setCallbackWrapper']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$definition->setTags([]);
|
||||||
|
$definition->setPublic(false);
|
||||||
|
|
||||||
|
$container->setDefinition($innerId, $definition);
|
||||||
|
$container->setDefinition($id, $recorder);
|
||||||
|
|
||||||
|
// Tell the collector to add the new instance
|
||||||
|
$collectorDefinition->addMethodCall('addInstance', [$name, new Reference($id)]);
|
||||||
|
$collectorDefinition->setPublic(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php
vendored
Normal file
52
vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class CachePoolClearerPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
private $cachePoolClearerTag;
|
||||||
|
|
||||||
|
public function __construct(string $cachePoolClearerTag = 'cache.pool.clearer')
|
||||||
|
{
|
||||||
|
if (0 < \func_num_args()) {
|
||||||
|
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cachePoolClearerTag = $cachePoolClearerTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$container->getParameterBag()->remove('cache.prefix.seed');
|
||||||
|
|
||||||
|
foreach ($container->findTaggedServiceIds($this->cachePoolClearerTag) as $id => $attr) {
|
||||||
|
$clearer = $container->getDefinition($id);
|
||||||
|
$pools = [];
|
||||||
|
foreach ($clearer->getArgument(0) as $name => $ref) {
|
||||||
|
if ($container->hasDefinition($ref)) {
|
||||||
|
$pools[$name] = new Reference($ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$clearer->replaceArgument(0, $pools);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user