实现请求缓存1天。
This commit is contained in:
1
vendor/composer/autoload_classmap.php
vendored
1
vendor/composer/autoload_classmap.php
vendored
@@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir);
|
||||
return array(
|
||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.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',
|
||||
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.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(
|
||||
'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',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/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',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.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',
|
||||
);
|
||||
|
||||
9
vendor/composer/autoload_psr4.php
vendored
9
vendor/composer/autoload_psr4.php
vendored
@@ -7,14 +7,18 @@ $baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'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'),
|
||||
'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\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
||||
'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\\Cache\\' => array($vendorDir . '/symfony/cache'),
|
||||
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||
'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\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/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\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/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 (
|
||||
'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',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/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',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.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',
|
||||
);
|
||||
|
||||
@@ -34,11 +35,15 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||
'Symfony\\Polyfill\\Php72\\' => 23,
|
||||
'Symfony\\Polyfill\\Php73\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
||||
'Symfony\\Contracts\\Service\\' => 26,
|
||||
'Symfony\\Contracts\\Cache\\' => 24,
|
||||
'Symfony\\Component\\VarExporter\\' => 30,
|
||||
'Symfony\\Component\\VarDumper\\' => 28,
|
||||
'Symfony\\Component\\Cache\\' => 24,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
@@ -54,6 +59,10 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
||||
'League\\Flysystem\\Cached\\' => 24,
|
||||
'League\\Flysystem\\' => 17,
|
||||
),
|
||||
'K' =>
|
||||
array (
|
||||
'Kevinrob\\GuzzleCache\\' => 21,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'GuzzleHttp\\Psr7\\' => 16,
|
||||
@@ -73,10 +82,10 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
||||
),
|
||||
'think\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/topthink/think-helper/src',
|
||||
1 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
||||
2 => __DIR__ . '/..' . '/topthink/framework/src/think',
|
||||
3 => __DIR__ . '/..' . '/topthink/think-filesystem/src',
|
||||
0 => __DIR__ . '/..' . '/topthink/framework/src/think',
|
||||
1 => __DIR__ . '/..' . '/topthink/think-filesystem/src',
|
||||
2 => __DIR__ . '/..' . '/topthink/think-helper/src',
|
||||
3 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
||||
),
|
||||
'app\\' =>
|
||||
array (
|
||||
@@ -86,9 +95,9 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php72\\' =>
|
||||
'Symfony\\Polyfill\\Php73\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
@@ -102,10 +111,26 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
||||
array (
|
||||
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\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/var-dumper',
|
||||
),
|
||||
'Symfony\\Component\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/cache',
|
||||
),
|
||||
'Psr\\SimpleCache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
|
||||
@@ -138,6 +163,10 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
||||
),
|
||||
'Kevinrob\\GuzzleCache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/kevinrob/guzzle-cache-middleware/src',
|
||||
),
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||
@@ -167,6 +196,7 @@ class ComposerStaticInit39c91b02671a17b9a5c68011e075e1a2
|
||||
public static $classMap = array (
|
||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.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',
|
||||
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.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,
|
||||
),
|
||||
'guzzlehttp/promises' => array(
|
||||
'pretty_version' => '1.5.2',
|
||||
'version' => '1.5.2.0',
|
||||
'reference' => 'b94b2807d85443f9719887892882d0329d1e2598',
|
||||
'pretty_version' => '1.5.3',
|
||||
'version' => '1.5.3.0',
|
||||
'reference' => '67ab6e18aaa14d753cc148911d273f6e6cb6721e',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/promises',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/psr7' => array(
|
||||
'pretty_version' => '1.9.0',
|
||||
'version' => '1.9.0.0',
|
||||
'reference' => 'e98e3e6d4f86621a9b75f623996e6bbdeb4b9318',
|
||||
'pretty_version' => '1.9.1',
|
||||
'version' => '1.9.1.0',
|
||||
'reference' => 'e4490cabc77465aaee90b20cfc9a770f8c04be6b',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
||||
'aliases' => array(),
|
||||
'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(
|
||||
'pretty_version' => '1.1.10',
|
||||
'version' => '1.1.10.0',
|
||||
@@ -65,18 +74,18 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'league/mime-type-detection' => array(
|
||||
'pretty_version' => '1.11.0',
|
||||
'version' => '1.11.0.0',
|
||||
'reference' => 'ff6248ea87a9f116e78edd6002e39e5128a0d4dd',
|
||||
'pretty_version' => '1.12.0',
|
||||
'version' => '1.12.0.0',
|
||||
'reference' => 'c7f2872fb273bf493811473dafc88d60ae829f48',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../league/mime-type-detection',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-curl-class/php-curl-class' => array(
|
||||
'pretty_version' => '9.14.3',
|
||||
'version' => '9.14.3.0',
|
||||
'reference' => '5d87676a3a7f83dd33d65f3c8d97f36679305193',
|
||||
'pretty_version' => '9.19.2',
|
||||
'version' => '9.19.2.0',
|
||||
'reference' => 'c41efeb4ea2dc3cf8f90f8f967b0fcf45a41e294',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-curl-class/php-curl-class',
|
||||
'aliases' => array(),
|
||||
@@ -91,6 +100,12 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/cache-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0|2.0',
|
||||
),
|
||||
),
|
||||
'psr/container' => array(
|
||||
'pretty_version' => '1.1.1',
|
||||
'version' => '1.1.1.0',
|
||||
@@ -133,6 +148,12 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/simple-cache-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0|2.0',
|
||||
),
|
||||
),
|
||||
'ralouphie/getallheaders' => array(
|
||||
'pretty_version' => '3.0.3',
|
||||
'version' => '3.0.3.0',
|
||||
@@ -142,50 +163,101 @@
|
||||
'aliases' => array(),
|
||||
'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(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '639084e360537a19f9ee352433b84ce831f3d2da',
|
||||
'pretty_version' => 'v1.31.0',
|
||||
'version' => '1.31.0.0',
|
||||
'reference' => 'c36586dcf89a12315939e00ec9b4474adcb1d773',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-intl-normalizer' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6',
|
||||
'pretty_version' => 'v1.31.0',
|
||||
'version' => '1.31.0.0',
|
||||
'reference' => '3833d7255cc303546435cb650316bff708a1c75c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
|
||||
'pretty_version' => 'v1.31.0',
|
||||
'version' => '1.31.0.0',
|
||||
'reference' => '85181ba99b2345b0ef10ce42ecac37612d9fd341',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'symfony/polyfill-php72' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '869329b1e9894268a8a61dabb69153029b7a8c97',
|
||||
'pretty_version' => 'v1.31.0',
|
||||
'version' => '1.31.0.0',
|
||||
'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',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php72',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php73',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.27.0',
|
||||
'version' => '1.27.0.0',
|
||||
'reference' => '7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936',
|
||||
'pretty_version' => 'v1.31.0',
|
||||
'version' => '1.31.0.0',
|
||||
'reference' => '60328e362d4c2c802a54fcbf04f9d3fb892b4cf8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'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(
|
||||
'pretty_version' => 'v4.4.47',
|
||||
@@ -196,6 +268,15 @@
|
||||
'aliases' => array(),
|
||||
'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(
|
||||
'pretty_version' => 'v6.1.5',
|
||||
'version' => '6.1.5.0',
|
||||
@@ -224,18 +305,18 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-helper' => array(
|
||||
'pretty_version' => 'v3.1.6',
|
||||
'version' => '3.1.6.0',
|
||||
'reference' => '769acbe50a4274327162f9c68ec2e89a38eb2aff',
|
||||
'pretty_version' => 'v3.1.11',
|
||||
'version' => '3.1.11.0',
|
||||
'reference' => '1d6ada9b9f3130046bf6922fe1bd159c8d88a33c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../topthink/think-helper',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-orm' => array(
|
||||
'pretty_version' => 'v2.0.60',
|
||||
'version' => '2.0.60.0',
|
||||
'reference' => '8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46',
|
||||
'pretty_version' => 'v2.0.62',
|
||||
'version' => '2.0.62.0',
|
||||
'reference' => 'e53bfea572a133039ad687077120de5521af617f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../topthink/think-orm',
|
||||
'aliases' => array(),
|
||||
|
||||
6
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
6
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
@@ -1,5 +1,11 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 1.5.3 - 2023-05-21
|
||||
|
||||
### Changed
|
||||
|
||||
- Removed remaining usage of deprecated functions
|
||||
|
||||
## 1.5.2 - 2022-08-07
|
||||
|
||||
### 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-ci": "vendor/bin/simple-phpunit --coverage-text"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.5-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"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,
|
||||
callable $onFulfilled = null
|
||||
) {
|
||||
return each_limit(
|
||||
return self::ofLimit(
|
||||
$iterable,
|
||||
$concurrency,
|
||||
$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 = [];
|
||||
foreach ($promises as $key => $promise) {
|
||||
$results[$key] = inspect($promise);
|
||||
$results[$key] = self::inspect($promise);
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
max-parallel: 10
|
||||
matrix:
|
||||
@@ -21,11 +21,7 @@ jobs:
|
||||
extensions: mbstring
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Mimic PHP 8.0
|
||||
run: composer config platform.php 8.0.999
|
||||
if: matrix.php > 8
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer update --no-interaction --no-progress
|
||||
|
||||
@@ -4,14 +4,13 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
max-parallel: 10
|
||||
matrix:
|
||||
php: ['7.2', '7.3', '7.4', '8.0']
|
||||
php: ['7.2', '7.3', '7.4', '8.0', '8.1']
|
||||
|
||||
steps:
|
||||
- name: Set up PHP
|
||||
@@ -21,7 +20,7 @@ jobs:
|
||||
coverage: none
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download dependencies
|
||||
uses: ramsey/composer-install@v1
|
||||
|
||||
@@ -6,11 +6,11 @@ on:
|
||||
jobs:
|
||||
php-cs-fixer:
|
||||
name: PHP-CS-Fixer
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
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
|
||||
|
||||
## 1.9.1 - 2023-04-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed header validation issue
|
||||
|
||||
## 1.9.0 - 2022-06-20
|
||||
|
||||
### Added
|
||||
|
||||
5
vendor/guzzlehttp/psr7/composer.json
vendored
5
vendor/guzzlehttp/psr7/composer.json
vendored
@@ -61,11 +61,6 @@
|
||||
"GuzzleHttp\\Tests\\Psr7\\": "tests/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.9-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"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.');
|
||||
}
|
||||
|
||||
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) {
|
||||
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
'"%s" is not valid header name',
|
||||
$header
|
||||
)
|
||||
sprintf('"%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
|
||||
// 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.
|
||||
if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/', $value)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value));
|
||||
if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $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
|
||||
|
||||
## 1.12.0 - 2022-08-03
|
||||
|
||||
### Updated
|
||||
|
||||
- Updated lookup
|
||||
|
||||
## 1.11.0 - 2022-04-17
|
||||
|
||||
### Updated
|
||||
|
||||
- Updated lookup
|
||||
|
||||
## 1.10.0 - 2022-04-11
|
||||
|
||||
### 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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -34,6 +34,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'acu' => 'application/vnd.acucobol',
|
||||
'acutc' => 'application/vnd.acucorp',
|
||||
'adp' => 'audio/adpcm',
|
||||
'adts' => 'audio/aac',
|
||||
'aep' => 'application/vnd.audiograph',
|
||||
'afm' => 'application/x-font-type1',
|
||||
'afp' => 'application/vnd.ibm.modcap',
|
||||
@@ -46,11 +47,16 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'air' => 'application/vnd.adobe.air-application-installer-package+zip',
|
||||
'ait' => 'application/vnd.dvb.ait',
|
||||
'ami' => 'application/vnd.amiga.ami',
|
||||
'aml' => 'application/automationml-aml+xml',
|
||||
'amlx' => 'application/automationml-amlx+zip',
|
||||
'amr' => 'audio/amr',
|
||||
'apk' => 'application/vnd.android.package-archive',
|
||||
'apng' => 'image/apng',
|
||||
'appcache' => 'text/cache-manifest',
|
||||
'appinstaller' => 'application/appinstaller',
|
||||
'application' => 'application/x-ms-application',
|
||||
'appx' => 'application/appx',
|
||||
'appxbundle' => 'application/appxbundle',
|
||||
'apr' => 'application/vnd.lotus-approach',
|
||||
'arc' => 'application/x-freearc',
|
||||
'arj' => 'application/x-arj',
|
||||
@@ -95,6 +101,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'bpk' => 'application/octet-stream',
|
||||
'bpmn' => 'application/octet-stream',
|
||||
'bsp' => 'model/vnd.valve.source.compiled-map',
|
||||
'btf' => 'image/prs.btif',
|
||||
'btif' => 'image/prs.btif',
|
||||
'buffer' => 'application/octet-stream',
|
||||
'bz' => 'application/x-bzip',
|
||||
@@ -146,6 +153,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'cjs' => 'application/node',
|
||||
'cla' => 'application/vnd.claymore',
|
||||
'class' => 'application/octet-stream',
|
||||
'cld' => 'model/vnd.cld',
|
||||
'clkk' => 'application/vnd.crick.clicker.keyboard',
|
||||
'clkp' => 'application/vnd.crick.clicker.palette',
|
||||
'clkt' => 'application/vnd.crick.clicker.template',
|
||||
@@ -180,6 +188,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'csv' => 'text/csv',
|
||||
'cu' => 'application/cu-seeme',
|
||||
'curl' => 'text/vnd.curl',
|
||||
'cwl' => 'application/cwl',
|
||||
'cww' => 'application/prs.cww',
|
||||
'cxt' => 'application/x-director',
|
||||
'cxx' => 'text/x-c',
|
||||
@@ -202,6 +211,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'der' => 'application/x-x509-ca-cert',
|
||||
'dfac' => 'application/vnd.dreamfactory',
|
||||
'dgc' => 'application/x-dgc-compressed',
|
||||
'dib' => 'image/bmp',
|
||||
'dic' => 'text/x-c',
|
||||
'dir' => 'application/x-director',
|
||||
'dis' => 'application/vnd.mobius.dis',
|
||||
@@ -224,6 +234,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'dp' => 'application/vnd.osgi.dp',
|
||||
'dpg' => 'application/vnd.dpgraph',
|
||||
'dpx' => 'image/dpx',
|
||||
'dra' => 'audio/vnd.dra',
|
||||
'drle' => 'image/dicom-rle',
|
||||
'dsc' => 'text/prs.lines.tag',
|
||||
@@ -260,7 +271,6 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'eot' => 'application/vnd.ms-fontobject',
|
||||
'eps' => 'application/postscript',
|
||||
'epub' => 'application/epub+zip',
|
||||
'es' => 'application/ecmascript',
|
||||
'es3' => 'application/vnd.eszigno3+xml',
|
||||
'esa' => 'application/vnd.osgi.subsystem',
|
||||
'esf' => 'application/vnd.epson.esf',
|
||||
@@ -453,6 +463,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'jsonld' => 'application/ld+json',
|
||||
'jsonml' => 'application/jsonml+json',
|
||||
'jsx' => 'text/jsx',
|
||||
'jt' => 'model/jt',
|
||||
'jxr' => 'image/jxr',
|
||||
'jxra' => 'image/jxra',
|
||||
'jxrs' => 'image/jxrs',
|
||||
@@ -557,7 +568,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'mime' => 'message/rfc822',
|
||||
'mj2' => 'video/mj2',
|
||||
'mjp2' => 'video/mj2',
|
||||
'mjs' => 'application/javascript',
|
||||
'mjs' => 'text/javascript',
|
||||
'mk3d' => 'video/x-matroska',
|
||||
'mka' => 'audio/x-matroska',
|
||||
'mkd' => 'text/x-markdown',
|
||||
@@ -607,6 +618,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'msg' => 'application/vnd.ms-outlook',
|
||||
'msh' => 'model/mesh',
|
||||
'msi' => 'application/x-msdownload',
|
||||
'msix' => 'application/msix',
|
||||
'msixbundle' => 'application/msixbundle',
|
||||
'msl' => 'application/vnd.mobius.msl',
|
||||
'msm' => 'application/octet-stream',
|
||||
'msp' => 'application/octet-stream',
|
||||
@@ -780,6 +793,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'pvb' => 'application/vnd.3gpp.pic-bw-var',
|
||||
'pwn' => 'application/vnd.3m.post-it-notes',
|
||||
'pya' => 'audio/vnd.ms-playready.media.pya',
|
||||
'pyo' => 'model/vnd.pytha.pyox',
|
||||
'pyox' => 'model/vnd.pytha.pyox',
|
||||
'pyv' => 'video/vnd.ms-playready.media.pyv',
|
||||
'qam' => 'application/vnd.epson.quickanime',
|
||||
'qbo' => 'application/vnd.intu.qbo',
|
||||
@@ -928,10 +943,12 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'st' => 'application/vnd.sailingtracker.track',
|
||||
'stc' => 'application/vnd.sun.xml.calc.template',
|
||||
'std' => 'application/vnd.sun.xml.draw.template',
|
||||
'step' => 'application/STEP',
|
||||
'stf' => 'application/vnd.wt.stf',
|
||||
'sti' => 'application/vnd.sun.xml.impress.template',
|
||||
'stk' => 'application/hyperstudio',
|
||||
'stl' => 'model/stl',
|
||||
'stp' => 'application/STEP',
|
||||
'stpx' => 'model/step+xml',
|
||||
'stpxz' => 'model/step-xml+zip',
|
||||
'stpz' => 'model/step+zip',
|
||||
@@ -1018,10 +1035,12 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'ulx' => 'application/x-glulx',
|
||||
'umj' => 'application/vnd.umajin',
|
||||
'unityweb' => 'application/vnd.unity',
|
||||
'uo' => 'application/vnd.uoml+xml',
|
||||
'uoml' => 'application/vnd.uoml+xml',
|
||||
'uri' => 'text/uri-list',
|
||||
'uris' => 'text/uri-list',
|
||||
'urls' => 'text/uri-list',
|
||||
'usda' => 'model/vnd.usda',
|
||||
'usdz' => 'model/vnd.usdz+zip',
|
||||
'ustar' => 'application/x-ustar',
|
||||
'utz' => 'application/vnd.uiq.theme',
|
||||
@@ -1101,6 +1120,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'webmanifest' => 'application/manifest+json',
|
||||
'webp' => 'image/webp',
|
||||
'wg' => 'application/vnd.pmi.widget',
|
||||
'wgsl' => 'text/wgsl',
|
||||
'wgt' => 'application/widget',
|
||||
'wif' => 'application/watcherinfo+xml',
|
||||
'wks' => 'application/vnd.ms-works',
|
||||
@@ -1155,9 +1175,10 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'xel' => 'application/xcap-el+xml',
|
||||
'xenc' => 'application/xenc+xml',
|
||||
'xer' => 'application/patch-ops-error+xml',
|
||||
'xfdf' => 'application/vnd.adobe.xfdf',
|
||||
'xfdf' => 'application/xfdf',
|
||||
'xfdl' => 'application/vnd.xfdl',
|
||||
'xht' => 'application/xhtml+xml',
|
||||
'xhtm' => 'application/vnd.pwg-xhtml-print+xml',
|
||||
'xhtml' => 'application/xhtml+xml',
|
||||
'xhvml' => 'application/xv+xml',
|
||||
'xif' => 'image/vnd.xiff',
|
||||
@@ -1188,6 +1209,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
|
||||
'xpw' => 'application/vnd.intercon.formnet',
|
||||
'xpx' => 'application/vnd.intercon.formnet',
|
||||
'xsd' => 'application/xml',
|
||||
'xsf' => 'application/prs.xsf+xml',
|
||||
'xsl' => 'application/xml',
|
||||
'xslt' => 'application/xslt+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 -->
|
||||
|
||||
## 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
|
||||
|
||||
- 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
|
||||
|
||||
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
|
||||
|
||||
@@ -199,7 +199,7 @@ More examples are available under [/examples](https://github.com/php-curl-class/
|
||||
Curl::__construct($base_url = null, $options = [])
|
||||
Curl::__destruct()
|
||||
Curl::__get($name)
|
||||
Curl::_fastDownload($url, $filename, $connections = 4)
|
||||
Curl::afterSend($callback)
|
||||
Curl::attemptRetry()
|
||||
Curl::beforeSend($callback)
|
||||
Curl::buildPostData($data)
|
||||
@@ -209,10 +209,12 @@ Curl::complete($callback)
|
||||
Curl::delete($url, $query_parameters = [], $data = [])
|
||||
Curl::diagnose($return = false)
|
||||
Curl::disableTimeout()
|
||||
Curl::displayCurlOptionValue($option, $value = null)
|
||||
Curl::download($url, $mixed_filename)
|
||||
Curl::error($callback)
|
||||
Curl::exec($ch = null)
|
||||
Curl::execDone()
|
||||
Curl::fastDownload($url, $filename, $connections = 4)
|
||||
Curl::get($url, $data = [])
|
||||
Curl::getAttempts()
|
||||
Curl::getBeforeSendCallback()
|
||||
@@ -233,6 +235,7 @@ Curl::getId()
|
||||
Curl::getInfo($opt = null)
|
||||
Curl::getJsonDecoder()
|
||||
Curl::getOpt($option)
|
||||
Curl::getOptions()
|
||||
Curl::getRawResponse()
|
||||
Curl::getRawResponseHeaders()
|
||||
Curl::getRemainingRetries()
|
||||
@@ -245,6 +248,7 @@ Curl::getRetries()
|
||||
Curl::getRetryDecider()
|
||||
Curl::getSuccessCallback()
|
||||
Curl::getUrl()
|
||||
Curl::getUserSetOptions()
|
||||
Curl::getXmlDecoder()
|
||||
Curl::head($url, $data = [])
|
||||
Curl::isChildOfMultiCurl()
|
||||
@@ -319,6 +323,7 @@ MultiCurl::addPatch($url, $data = [])
|
||||
MultiCurl::addPost($url, $data = '', $follow_303_with_post = false)
|
||||
MultiCurl::addPut($url, $data = [])
|
||||
MultiCurl::addSearch($url, $data = [])
|
||||
MultiCurl::afterSend($callback)
|
||||
MultiCurl::beforeSend($callback)
|
||||
MultiCurl::close()
|
||||
MultiCurl::complete($callback)
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Zach Borboa"
|
||||
},
|
||||
{
|
||||
"name": "Contributors",
|
||||
"homepage": "https://github.com/php-curl-class/php-curl-class/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
@@ -20,11 +24,12 @@
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "*",
|
||||
"ext-gd": "*",
|
||||
"friendsofphp/php-cs-fixer": "*",
|
||||
"phpcompatibility/php-compatibility": "dev-develop",
|
||||
"phpcsstandards/phpcsutils": "@alpha",
|
||||
"phpunit/phpunit": "*",
|
||||
"squizlabs/php_codesniffer": "*",
|
||||
"vimeo/psalm": "*"
|
||||
"vimeo/psalm": ">=0.3.63"
|
||||
},
|
||||
"suggest": {
|
||||
"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;
|
||||
|
||||
use Curl\CaseInsensitiveArray;
|
||||
|
||||
class ArrayUtil
|
||||
{
|
||||
/**
|
||||
* Is Array Assoc
|
||||
*
|
||||
* @access public
|
||||
* @param $array
|
||||
*
|
||||
* @return boolean
|
||||
* @param $array
|
||||
* @return bool
|
||||
*/
|
||||
public static function isArrayAssoc($array)
|
||||
{
|
||||
@@ -26,10 +24,8 @@ class ArrayUtil
|
||||
* Is Array Assoc
|
||||
*
|
||||
* @deprecated Use ArrayUtil::isArrayAssoc().
|
||||
* @access public
|
||||
* @param $array
|
||||
*
|
||||
* @return boolean
|
||||
* @param $array
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_array_assoc($array)
|
||||
{
|
||||
@@ -39,10 +35,8 @@ class ArrayUtil
|
||||
/**
|
||||
* Is Array Multidim
|
||||
*
|
||||
* @access public
|
||||
* @param $array
|
||||
*
|
||||
* @return boolean
|
||||
* @param $array
|
||||
* @return bool
|
||||
*/
|
||||
public static function isArrayMultidim($array)
|
||||
{
|
||||
@@ -57,10 +51,8 @@ class ArrayUtil
|
||||
* Is Array Multidim
|
||||
*
|
||||
* @deprecated Use ArrayUtil::isArrayMultidim().
|
||||
* @access public
|
||||
* @param $array
|
||||
*
|
||||
* @return boolean
|
||||
* @param $array
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_array_multidim($array)
|
||||
{
|
||||
@@ -70,10 +62,8 @@ class ArrayUtil
|
||||
/**
|
||||
* Array Flatten Multidim
|
||||
*
|
||||
* @access public
|
||||
* @param $array
|
||||
* @param $prefix
|
||||
*
|
||||
* @param $array
|
||||
* @param $prefix
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayFlattenMultidim($array, $prefix = false)
|
||||
@@ -124,10 +114,8 @@ class ArrayUtil
|
||||
* Array Flatten Multidim
|
||||
*
|
||||
* @deprecated Use ArrayUtil::arrayFlattenMultidim().
|
||||
* @access public
|
||||
* @param $array
|
||||
* @param $prefix
|
||||
*
|
||||
* @param $array
|
||||
* @param $prefix
|
||||
* @return array
|
||||
*/
|
||||
public static function array_flatten_multidim($array, $prefix = false)
|
||||
@@ -138,9 +126,7 @@ class ArrayUtil
|
||||
/**
|
||||
* Array Random
|
||||
*
|
||||
* @access public
|
||||
* @param $array
|
||||
*
|
||||
* @param $array
|
||||
* @return mixed
|
||||
*/
|
||||
public static function arrayRandom($array)
|
||||
@@ -151,10 +137,8 @@ class ArrayUtil
|
||||
/**
|
||||
* Array Random Index
|
||||
*
|
||||
* @access public
|
||||
* @param $array
|
||||
*
|
||||
* @return integer
|
||||
* @param $array
|
||||
* @return int
|
||||
*/
|
||||
public static function arrayRandomIndex($array)
|
||||
{
|
||||
@@ -165,9 +149,7 @@ class ArrayUtil
|
||||
* Array Random
|
||||
*
|
||||
* @deprecated Use ArrayUtil::arrayRandom().
|
||||
* @access public
|
||||
* @param $array
|
||||
*
|
||||
* @param $array
|
||||
* @return mixed
|
||||
*/
|
||||
public static function array_random($array)
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Curl;
|
||||
|
||||
abstract class BaseCurl
|
||||
{
|
||||
public $beforeSendCallback = null;
|
||||
public $afterSendCallback = null;
|
||||
public $successCallback = null;
|
||||
public $errorCallback = null;
|
||||
public $completeCallback = null;
|
||||
|
||||
protected $options = [];
|
||||
protected $userSetOptions = [];
|
||||
|
||||
/**
|
||||
* Before Send
|
||||
*
|
||||
* @access public
|
||||
* @param $callback callable|null
|
||||
* @param $callback callable|null
|
||||
*/
|
||||
public function beforeSend($callback)
|
||||
{
|
||||
@@ -27,8 +30,7 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Complete
|
||||
*
|
||||
* @access public
|
||||
* @param $callback callable|null
|
||||
* @param $callback callable|null
|
||||
*/
|
||||
public function complete($callback)
|
||||
{
|
||||
@@ -37,8 +39,6 @@ abstract class BaseCurl
|
||||
|
||||
/**
|
||||
* Disable Timeout
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disableTimeout()
|
||||
{
|
||||
@@ -48,8 +48,7 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Error
|
||||
*
|
||||
* @access public
|
||||
* @param $callback callable|null
|
||||
* @param $callback callable|null
|
||||
*/
|
||||
public function error($callback)
|
||||
{
|
||||
@@ -59,14 +58,12 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Get Opt
|
||||
*
|
||||
* @access public
|
||||
* @param $option
|
||||
*
|
||||
* @param $option
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOpt($option)
|
||||
{
|
||||
return isset($this->options[$option]) ? $this->options[$option] : null;
|
||||
return $this->options[$option] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,8 +72,7 @@ abstract class BaseCurl
|
||||
* Remove an internal header from the request.
|
||||
* Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');.
|
||||
*
|
||||
* @access public
|
||||
* @param $key
|
||||
* @param $key
|
||||
*/
|
||||
public function removeHeader($key)
|
||||
{
|
||||
@@ -86,7 +82,7 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set auto referer
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $auto_referer
|
||||
*/
|
||||
public function setAutoReferer($auto_referer = true)
|
||||
{
|
||||
@@ -96,7 +92,7 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set auto referrer
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $auto_referrer
|
||||
*/
|
||||
public function setAutoReferrer($auto_referrer = true)
|
||||
{
|
||||
@@ -106,9 +102,8 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set Basic Authentication
|
||||
*
|
||||
* @access public
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @param $username
|
||||
* @param $password
|
||||
*/
|
||||
public function setBasicAuthentication($username, $password = '')
|
||||
{
|
||||
@@ -119,8 +114,7 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set Connect Timeout
|
||||
*
|
||||
* @access public
|
||||
* @param $seconds
|
||||
* @param $seconds
|
||||
*/
|
||||
public function setConnectTimeout($seconds)
|
||||
{
|
||||
@@ -136,9 +130,8 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set Digest Authentication
|
||||
*
|
||||
* @access public
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @param $username
|
||||
* @param $password
|
||||
*/
|
||||
public function setDigestAuthentication($username, $password = '')
|
||||
{
|
||||
@@ -146,21 +139,48 @@ abstract class BaseCurl
|
||||
$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
|
||||
*
|
||||
* @access public
|
||||
* @param $file
|
||||
* @param $file
|
||||
*/
|
||||
public function setFile($file)
|
||||
{
|
||||
$this->setOpt(CURLOPT_FILE, $file);
|
||||
}
|
||||
|
||||
protected function setFileInternal($file)
|
||||
{
|
||||
$this->setOptInternal(CURLOPT_FILE, $file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set follow location
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $follow_location
|
||||
* @see Curl::setMaximumRedirects()
|
||||
*/
|
||||
public function setFollowLocation($follow_location = true)
|
||||
{
|
||||
@@ -170,7 +190,7 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set forbid reuse
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $forbid_reuse
|
||||
*/
|
||||
public function setForbidReuse($forbid_reuse = true)
|
||||
{
|
||||
@@ -186,8 +206,7 @@ abstract class BaseCurl
|
||||
* The name of the outgoing network interface to use.
|
||||
* This can be an interface name, an IP address or a host name.
|
||||
*
|
||||
* @access public
|
||||
* @param $interface
|
||||
* @param $interface
|
||||
*/
|
||||
public function setInterface($interface)
|
||||
{
|
||||
@@ -199,7 +218,8 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set maximum redirects
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $maximum_redirects
|
||||
* @see Curl::setFollowLocation()
|
||||
*/
|
||||
public function setMaximumRedirects($maximum_redirects)
|
||||
{
|
||||
@@ -207,13 +227,17 @@ abstract class BaseCurl
|
||||
}
|
||||
|
||||
abstract public function setOpt($option, $value);
|
||||
|
||||
protected function setOptInternal($option, $value)
|
||||
{
|
||||
}
|
||||
|
||||
abstract public function setOpts($options);
|
||||
|
||||
/**
|
||||
* Set Port
|
||||
*
|
||||
* @access public
|
||||
* @param $port
|
||||
* @param $port
|
||||
*/
|
||||
public function setPort($port)
|
||||
{
|
||||
@@ -225,11 +249,10 @@ abstract class BaseCurl
|
||||
*
|
||||
* Set an HTTP proxy to tunnel requests through.
|
||||
*
|
||||
* @access public
|
||||
* @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 $username - The username to use for the connection to the proxy.
|
||||
* @param $password - The password to use for the connection to the proxy.
|
||||
* @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 $username - The username to use for the connection to the proxy.
|
||||
* @param $password - The password to use for the connection to the proxy.
|
||||
*/
|
||||
public function setProxy($proxy, $port = null, $username = null, $password = null)
|
||||
{
|
||||
@@ -247,8 +270,7 @@ abstract class BaseCurl
|
||||
*
|
||||
* Set the HTTP authentication method(s) to use for the proxy connection.
|
||||
*
|
||||
* @access public
|
||||
* @param $auth
|
||||
* @param $auth
|
||||
*/
|
||||
public function setProxyAuth($auth)
|
||||
{
|
||||
@@ -260,8 +282,7 @@ abstract class BaseCurl
|
||||
*
|
||||
* Set the proxy to tunnel through HTTP proxy.
|
||||
*
|
||||
* @access public
|
||||
* @param $tunnel boolean
|
||||
* @param $tunnel boolean
|
||||
*/
|
||||
public function setProxyTunnel($tunnel = true)
|
||||
{
|
||||
@@ -273,8 +294,7 @@ abstract class BaseCurl
|
||||
*
|
||||
* Set the proxy protocol type.
|
||||
*
|
||||
* @access public
|
||||
* @param $type
|
||||
* @param $type
|
||||
*/
|
||||
public function setProxyType($type)
|
||||
{
|
||||
@@ -284,19 +304,22 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set Range
|
||||
*
|
||||
* @access public
|
||||
* @param $range
|
||||
* @param $range
|
||||
*/
|
||||
public function setRange($range)
|
||||
{
|
||||
$this->setOpt(CURLOPT_RANGE, $range);
|
||||
}
|
||||
|
||||
protected function setRangeInternal($range)
|
||||
{
|
||||
$this->setOptInternal(CURLOPT_RANGE, $range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Referer
|
||||
*
|
||||
* @access public
|
||||
* @param $referer
|
||||
* @param $referer
|
||||
*/
|
||||
public function setReferer($referer)
|
||||
{
|
||||
@@ -306,8 +329,7 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set Referrer
|
||||
*
|
||||
* @access public
|
||||
* @param $referrer
|
||||
* @param $referrer
|
||||
*/
|
||||
public function setReferrer($referrer)
|
||||
{
|
||||
@@ -319,35 +341,42 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Set Timeout
|
||||
*
|
||||
* @access public
|
||||
* @param $seconds
|
||||
* @param $seconds
|
||||
*/
|
||||
public function setTimeout($seconds)
|
||||
{
|
||||
$this->setOpt(CURLOPT_TIMEOUT, $seconds);
|
||||
}
|
||||
|
||||
protected function setTimeoutInternal($seconds)
|
||||
{
|
||||
$this->setOptInternal(CURLOPT_TIMEOUT, $seconds);
|
||||
}
|
||||
|
||||
abstract public function setUrl($url, $mixed_data = '');
|
||||
|
||||
/**
|
||||
* Set User Agent
|
||||
*
|
||||
* @access public
|
||||
* @param $user_agent
|
||||
* @param $user_agent
|
||||
*/
|
||||
public function setUserAgent($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 stop();
|
||||
|
||||
/**
|
||||
* Success
|
||||
*
|
||||
* @access public
|
||||
* @param $callback callable|null
|
||||
* @param $callback callable|null
|
||||
*/
|
||||
public function success($callback)
|
||||
{
|
||||
@@ -360,8 +389,6 @@ abstract class BaseCurl
|
||||
* Unset Proxy
|
||||
*
|
||||
* Disable use of the proxy.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function unsetProxy()
|
||||
{
|
||||
@@ -371,9 +398,8 @@ abstract class BaseCurl
|
||||
/**
|
||||
* Verbose
|
||||
*
|
||||
* @access public
|
||||
* @param bool $on
|
||||
* @param resource|string $output
|
||||
* @param bool $on
|
||||
* @param resource|string $output
|
||||
*/
|
||||
public function verbose($on = true, $output = 'STDERR')
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Curl;
|
||||
|
||||
@@ -7,9 +9,9 @@ namespace Curl;
|
||||
*/
|
||||
class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var mixed[] Data storage with lowercase keys.
|
||||
*
|
||||
* @see offsetSet()
|
||||
* @see offsetExists()
|
||||
* @see offsetUnset()
|
||||
@@ -23,6 +25,7 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
/**
|
||||
* @var string[] Case-sensitive keys.
|
||||
*
|
||||
* @see offsetSet()
|
||||
* @see offsetUnset()
|
||||
* @see key()
|
||||
@@ -36,13 +39,15 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
* case-insensitive array. Caution: Data may be lost when converting
|
||||
* 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
|
||||
*
|
||||
* @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) {
|
||||
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
|
||||
* $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 mixed $value The data to store at the specified offset.
|
||||
*
|
||||
* @param string $offset The offset to store the data at (case-insensitive).
|
||||
* @param mixed $value The data to store at the specified offset.
|
||||
* @return void
|
||||
*
|
||||
* @access public
|
||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetset.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
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
|
||||
* the lowercase version of the provided offset.
|
||||
*
|
||||
* @param string $offset Offset to check
|
||||
* @return bool If the offset exists.
|
||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetexists.php
|
||||
*
|
||||
* @param string $offset Offset to check
|
||||
*
|
||||
* @return bool If the offset exists.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
@@ -105,13 +102,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
* Unsets the specified offset. Converts the provided offset to lowercase,
|
||||
* 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
|
||||
*
|
||||
* @access public
|
||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetunset.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
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
|
||||
* lowercase and the lookup is done on the data store directly.
|
||||
*
|
||||
* @param string $offset Offset to lookup.
|
||||
* @return mixed The data stored at the offset.
|
||||
* @see https://secure.php.net/manual/en/arrayaccess.offsetget.php
|
||||
*
|
||||
* @param string $offset Offset to lookup.
|
||||
*
|
||||
* @return mixed The data stored at the offset.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$offsetlower = strtolower($offset);
|
||||
return isset($this->data[$offsetlower]) ? $this->data[$offsetlower] : null;
|
||||
return $this->data[$offsetlower] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count
|
||||
*
|
||||
* @see https://secure.php.net/manual/en/countable.count.php
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return integer The number of elements stored in the array.
|
||||
*
|
||||
* @access public
|
||||
* @return int The number of elements stored in the array.
|
||||
* @see https://secure.php.net/manual/en/countable.count.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
@@ -162,13 +147,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
/**
|
||||
* Current
|
||||
*
|
||||
* @see https://secure.php.net/manual/en/iterator.current.php
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return mixed Data at the current position.
|
||||
*
|
||||
* @access public
|
||||
* @see https://secure.php.net/manual/en/iterator.current.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
@@ -179,13 +160,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
/**
|
||||
* Next
|
||||
*
|
||||
* @see https://secure.php.net/manual/en/iterator.next.php
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @access public
|
||||
* @see https://secure.php.net/manual/en/iterator.next.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
@@ -196,29 +173,22 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
/**
|
||||
* Key
|
||||
*
|
||||
* @see https://secure.php.net/manual/en/iterator.key.php
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return mixed Case-sensitive key at current position.
|
||||
*
|
||||
* @access public
|
||||
* @see https://secure.php.net/manual/en/iterator.key.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
$key = key($this->data);
|
||||
return isset($this->keys[$key]) ? $this->keys[$key] : $key;
|
||||
return $this->keys[$key] ?? $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid
|
||||
*
|
||||
* @see https://secure.php.net/manual/en/iterator.valid.php
|
||||
*
|
||||
* @return bool If the current position is valid.
|
||||
*
|
||||
* @access public
|
||||
* @see https://secure.php.net/manual/en/iterator.valid.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
@@ -229,13 +199,9 @@ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
||||
/**
|
||||
* Rewind
|
||||
*
|
||||
* @see https://secure.php.net/manual/en/iterator.rewind.php
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @access public
|
||||
* @see https://secure.php.net/manual/en/iterator.rewind.php
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
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;
|
||||
|
||||
@@ -7,11 +9,10 @@ class Decoder
|
||||
/**
|
||||
* Decode JSON
|
||||
*
|
||||
* @access public
|
||||
* @param $json
|
||||
* @param $assoc
|
||||
* @param $depth
|
||||
* @param $options
|
||||
* @param $json
|
||||
* @param $assoc
|
||||
* @param $depth
|
||||
* @param $options
|
||||
*/
|
||||
public static function decodeJson()
|
||||
{
|
||||
@@ -26,12 +27,11 @@ class Decoder
|
||||
/**
|
||||
* Decode XML
|
||||
*
|
||||
* @access public
|
||||
* @param $data
|
||||
* @param $class_name
|
||||
* @param $options
|
||||
* @param $ns
|
||||
* @param $is_prefix
|
||||
* @param $data
|
||||
* @param $class_name
|
||||
* @param $options
|
||||
* @param $ns
|
||||
* @param $is_prefix
|
||||
*/
|
||||
public static function decodeXml()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Curl;
|
||||
|
||||
@@ -9,11 +11,9 @@ class Encoder
|
||||
*
|
||||
* Wrap json_encode() to throw error when the value being encoded fails.
|
||||
*
|
||||
* @access public
|
||||
* @param $value
|
||||
* @param $options
|
||||
* @param $depth
|
||||
*
|
||||
* @param $value
|
||||
* @param $options
|
||||
* @param $depth
|
||||
* @return string
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Curl;
|
||||
|
||||
use Curl\ArrayUtil;
|
||||
use Curl\BaseCurl;
|
||||
use Curl\Url;
|
||||
|
||||
class MultiCurl extends BaseCurl
|
||||
{
|
||||
public $baseUrl = null;
|
||||
@@ -44,8 +42,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @access public
|
||||
* @param $base_url
|
||||
* @param $base_url
|
||||
*/
|
||||
public function __construct($base_url = null)
|
||||
{
|
||||
@@ -60,11 +57,9 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Delete
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $query_parameters
|
||||
* @param $data
|
||||
*
|
||||
* @param $url
|
||||
* @param $query_parameters
|
||||
* @param $data
|
||||
* @return object
|
||||
*/
|
||||
public function addDelete($url, $query_parameters = [], $data = [])
|
||||
@@ -87,10 +82,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Download
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $mixed_filename
|
||||
*
|
||||
* @param $url
|
||||
* @param $mixed_filename
|
||||
* @return object
|
||||
*/
|
||||
public function addDownload($url, $mixed_filename)
|
||||
@@ -148,10 +141,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Get
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $data
|
||||
*
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @return object
|
||||
*/
|
||||
public function addGet($url, $data = [])
|
||||
@@ -173,10 +164,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Head
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $data
|
||||
*
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @return object
|
||||
*/
|
||||
public function addHead($url, $data = [])
|
||||
@@ -198,10 +187,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Options
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $data
|
||||
*
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @return object
|
||||
*/
|
||||
public function addOptions($url, $data = [])
|
||||
@@ -223,10 +210,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Patch
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $data
|
||||
*
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @return object
|
||||
*/
|
||||
public function addPatch($url, $data = [])
|
||||
@@ -253,13 +238,12 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Post
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @param $follow_303_with_post
|
||||
* If true, will cause 303 redirections to be followed using a POST request (default: false).
|
||||
* Note: Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true.
|
||||
*
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @param $follow_303_with_post
|
||||
* If true, will cause 303 redirections to be followed using a POST request
|
||||
* (default: false). Note: Redirections are only followed if the
|
||||
* CURLOPT_FOLLOWLOCATION option is set to true.
|
||||
* @return object
|
||||
*/
|
||||
public function addPost($url, $data = '', $follow_303_with_post = false)
|
||||
@@ -296,10 +280,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Put
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $data
|
||||
*
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @return object
|
||||
*/
|
||||
public function addPut($url, $data = [])
|
||||
@@ -325,10 +307,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Add Search
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $data
|
||||
*
|
||||
* @param $url
|
||||
* @param $data
|
||||
* @return object
|
||||
*/
|
||||
public function addSearch($url, $data = [])
|
||||
@@ -356,9 +336,7 @@ class MultiCurl extends BaseCurl
|
||||
*
|
||||
* Add a Curl instance to the handle queue.
|
||||
*
|
||||
* @access public
|
||||
* @param $curl
|
||||
*
|
||||
* @param $curl
|
||||
* @return object
|
||||
*/
|
||||
public function addCurl(Curl $curl)
|
||||
@@ -369,8 +347,6 @@ class MultiCurl extends BaseCurl
|
||||
|
||||
/**
|
||||
* Close
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
@@ -387,8 +363,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Concurrency
|
||||
*
|
||||
* @access public
|
||||
* @param $concurrency
|
||||
* @param $concurrency
|
||||
*/
|
||||
public function setConcurrency($concurrency)
|
||||
{
|
||||
@@ -398,9 +373,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Cookie
|
||||
*
|
||||
* @access public
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function setCookie($key, $value)
|
||||
{
|
||||
@@ -410,8 +384,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Cookies
|
||||
*
|
||||
* @access public
|
||||
* @param $cookies
|
||||
* @param $cookies
|
||||
*/
|
||||
public function setCookies($cookies)
|
||||
{
|
||||
@@ -423,8 +396,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Cookie String
|
||||
*
|
||||
* @access public
|
||||
* @param $string
|
||||
* @param $string
|
||||
*/
|
||||
public function setCookieString($string)
|
||||
{
|
||||
@@ -434,8 +406,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Cookie File
|
||||
*
|
||||
* @access public
|
||||
* @param $cookie_file
|
||||
* @param $cookie_file
|
||||
*/
|
||||
public function setCookieFile($cookie_file)
|
||||
{
|
||||
@@ -445,8 +416,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Cookie Jar
|
||||
*
|
||||
* @access public
|
||||
* @param $cookie_jar
|
||||
* @param $cookie_jar
|
||||
*/
|
||||
public function setCookieJar($cookie_jar)
|
||||
{
|
||||
@@ -458,9 +428,8 @@ class MultiCurl extends BaseCurl
|
||||
*
|
||||
* Add extra header to include in the request.
|
||||
*
|
||||
* @access public
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function setHeader($key, $value)
|
||||
{
|
||||
@@ -473,8 +442,7 @@ class MultiCurl extends BaseCurl
|
||||
*
|
||||
* Add extra headers to include in the request.
|
||||
*
|
||||
* @access public
|
||||
* @param $headers
|
||||
* @param $headers
|
||||
*/
|
||||
public function setHeaders($headers)
|
||||
{
|
||||
@@ -499,8 +467,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set JSON Decoder
|
||||
*
|
||||
* @access public
|
||||
* @param $mixed boolean|callable
|
||||
* @param $mixed boolean|callable
|
||||
*/
|
||||
public function setJsonDecoder($mixed)
|
||||
{
|
||||
@@ -514,8 +481,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set XML Decoder
|
||||
*
|
||||
* @access public
|
||||
* @param $mixed boolean|callable
|
||||
* @param $mixed boolean|callable
|
||||
*/
|
||||
public function setXmlDecoder($mixed)
|
||||
{
|
||||
@@ -532,9 +498,8 @@ class MultiCurl extends BaseCurl
|
||||
* Set proxies to tunnel requests through. When set, a random proxy will be
|
||||
* used for the request.
|
||||
*
|
||||
* @access public
|
||||
* @param $proxies array - A list of HTTP proxies to tunnel requests
|
||||
* through. May include port number.
|
||||
* @param $proxies array - A list of HTTP proxies to tunnel requests
|
||||
* through. May include port number.
|
||||
*/
|
||||
public function setProxies($proxies)
|
||||
{
|
||||
@@ -544,9 +509,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Opt
|
||||
*
|
||||
* @access public
|
||||
* @param $option
|
||||
* @param $value
|
||||
* @param $option
|
||||
* @param $value
|
||||
*/
|
||||
public function setOpt($option, $value)
|
||||
{
|
||||
@@ -557,8 +521,10 @@ class MultiCurl extends BaseCurl
|
||||
// unexpectedly changing the request url after is has been specified.
|
||||
if ($option === CURLOPT_URL) {
|
||||
foreach ($this->queuedCurls as $curl_id => $curl) {
|
||||
if (!isset($this->instanceSpecificOptions[$curl_id][$option]) ||
|
||||
$this->instanceSpecificOptions[$curl_id][$option] === null) {
|
||||
if (
|
||||
!isset($this->instanceSpecificOptions[$curl_id][$option]) ||
|
||||
$this->instanceSpecificOptions[$curl_id][$option] === null
|
||||
) {
|
||||
$this->instanceSpecificOptions[$curl_id][$option] = $value;
|
||||
}
|
||||
}
|
||||
@@ -568,8 +534,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Opts
|
||||
*
|
||||
* @access public
|
||||
* @param $options
|
||||
* @param $options
|
||||
*/
|
||||
public function setOpts($options)
|
||||
{
|
||||
@@ -581,8 +546,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Rate Limit
|
||||
*
|
||||
* @access public
|
||||
* @param $rate_limit string (e.g. "60/1m").
|
||||
* @param $rate_limit string (e.g. "60/1m").
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function setRateLimit($rate_limit)
|
||||
@@ -641,8 +605,7 @@ class MultiCurl extends BaseCurl
|
||||
* When using a callable decider, the request will be retried until the
|
||||
* function returns a value which evaluates to false.
|
||||
*
|
||||
* @access public
|
||||
* @param $mixed
|
||||
* @param $mixed
|
||||
*/
|
||||
public function setRetry($mixed)
|
||||
{
|
||||
@@ -652,9 +615,8 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Set Url
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $mixed_data
|
||||
* @param $url
|
||||
* @param $mixed_data
|
||||
*/
|
||||
public function setUrl($url, $mixed_data = '')
|
||||
{
|
||||
@@ -672,7 +634,6 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Start
|
||||
*
|
||||
* @access public
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function start()
|
||||
@@ -687,7 +648,8 @@ class MultiCurl extends BaseCurl
|
||||
$this->currentRequestCount = 0;
|
||||
|
||||
do {
|
||||
while (count($this->queuedCurls) &&
|
||||
while (
|
||||
count($this->queuedCurls) &&
|
||||
count($this->activeCurls) < $this->concurrency &&
|
||||
(!$this->rateLimitEnabled || $this->hasRequestQuota())
|
||||
) {
|
||||
@@ -733,8 +695,10 @@ class MultiCurl extends BaseCurl
|
||||
}
|
||||
}
|
||||
|
||||
while ((is_resource($this->multiCurl) || $this->multiCurl instanceof \CurlMultiHandle) &&
|
||||
(($info_array = curl_multi_info_read($this->multiCurl)) !== false)) {
|
||||
while (
|
||||
(is_resource($this->multiCurl) || $this->multiCurl instanceof \CurlMultiHandle) &&
|
||||
(($info_array = curl_multi_info_read($this->multiCurl)) !== false)
|
||||
) {
|
||||
if ($info_array['msg'] === CURLMSG_DONE) {
|
||||
foreach ($this->activeCurls as $key => $curl) {
|
||||
if ($curl->curl === $info_array['handle']) {
|
||||
@@ -782,8 +746,6 @@ class MultiCurl extends BaseCurl
|
||||
|
||||
/**
|
||||
* Stop
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
@@ -810,8 +772,7 @@ class MultiCurl extends BaseCurl
|
||||
*
|
||||
* Remove extra header previously set using Curl::setHeader().
|
||||
*
|
||||
* @access public
|
||||
* @param $key
|
||||
* @param $key
|
||||
*/
|
||||
public function unsetHeader($key)
|
||||
{
|
||||
@@ -820,8 +781,6 @@ class MultiCurl extends BaseCurl
|
||||
|
||||
/**
|
||||
* Set request time accuracy
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function setRequestTimeAccuracy()
|
||||
{
|
||||
@@ -830,8 +789,6 @@ class MultiCurl extends BaseCurl
|
||||
|
||||
/**
|
||||
* Destruct
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
@@ -840,8 +797,6 @@ class MultiCurl extends BaseCurl
|
||||
|
||||
/**
|
||||
* Update Headers
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function updateHeaders()
|
||||
{
|
||||
@@ -853,8 +808,7 @@ class MultiCurl extends BaseCurl
|
||||
/**
|
||||
* Queue Handle
|
||||
*
|
||||
* @access private
|
||||
* @param $curl
|
||||
* @param $curl
|
||||
*/
|
||||
private function queueHandle($curl)
|
||||
{
|
||||
@@ -863,14 +817,16 @@ class MultiCurl extends BaseCurl
|
||||
$curl->childOfMultiCurl = true;
|
||||
$this->queuedCurls[$curl->id] = $curl;
|
||||
|
||||
$curl->setHeaders($this->headers);
|
||||
// Avoid overwriting any existing header.
|
||||
if ($curl->getOpt(CURLOPT_HTTPHEADER) === null) {
|
||||
$curl->setHeaders($this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init Handle
|
||||
*
|
||||
* @access private
|
||||
* @param $curl
|
||||
* @param $curl
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
private function initHandle()
|
||||
@@ -888,6 +844,9 @@ class MultiCurl extends BaseCurl
|
||||
if ($curl->beforeSendCallback === null) {
|
||||
$curl->beforeSend($this->beforeSendCallback);
|
||||
}
|
||||
if ($curl->afterSendCallback === null) {
|
||||
$curl->afterSend($this->afterSendCallback);
|
||||
}
|
||||
if ($curl->successCallback === null) {
|
||||
$curl->success($this->successCallback);
|
||||
}
|
||||
@@ -934,8 +893,6 @@ class MultiCurl extends BaseCurl
|
||||
*
|
||||
* Checks if there is any available quota to make additional requests while
|
||||
* rate limiting is enabled.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function hasRequestQuota()
|
||||
{
|
||||
@@ -965,8 +922,6 @@ class MultiCurl extends BaseCurl
|
||||
* Wait Until Request Quota Available
|
||||
*
|
||||
* Waits until there is available request quota available based on the rate limit.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function waitUntilRequestQuotaAvailable()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Curl;
|
||||
|
||||
@@ -43,10 +45,8 @@ class StringUtil
|
||||
/**
|
||||
* Return true when $haystack starts with $needle.
|
||||
*
|
||||
* @access public
|
||||
* @param $haystack
|
||||
* @param $needle
|
||||
*
|
||||
* @param $haystack
|
||||
* @param $needle
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($haystack, $needle)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Curl;
|
||||
|
||||
use Curl\StringUtil;
|
||||
|
||||
class Url
|
||||
{
|
||||
private $baseUrl = null;
|
||||
@@ -15,7 +15,7 @@ class Url
|
||||
$this->relativeUrl = $relative_url;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->absolutizeUrl();
|
||||
}
|
||||
@@ -24,6 +24,8 @@ class Url
|
||||
* Remove dot segments.
|
||||
*
|
||||
* Interpret and remove the special "." and ".." path segments from a referenced path.
|
||||
*
|
||||
* @param mixed $input
|
||||
*/
|
||||
public static function removeDotSegments($input)
|
||||
{
|
||||
@@ -87,10 +89,8 @@ class Url
|
||||
/**
|
||||
* Build Url
|
||||
*
|
||||
* @access public
|
||||
* @param $url
|
||||
* @param $mixed_data
|
||||
*
|
||||
* @param $url
|
||||
* @param $mixed_data
|
||||
* @return string
|
||||
*/
|
||||
public static function buildUrl($url, $mixed_data = '')
|
||||
@@ -127,29 +127,29 @@ class Url
|
||||
$target = [];
|
||||
if (isset($r['scheme'])) {
|
||||
$target['scheme'] = $r['scheme'];
|
||||
$target['host'] = isset($r['host']) ? $r['host'] : null;
|
||||
$target['port'] = isset($r['port']) ? $r['port'] : null;
|
||||
$target['user'] = isset($r['user']) ? $r['user'] : null;
|
||||
$target['pass'] = isset($r['pass']) ? $r['pass'] : null;
|
||||
$target['host'] = $r['host'] ?? null;
|
||||
$target['port'] = $r['port'] ?? null;
|
||||
$target['user'] = $r['user'] ?? null;
|
||||
$target['pass'] = $r['pass'] ?? 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 {
|
||||
$target['scheme'] = isset($b['scheme']) ? $b['scheme'] : null;
|
||||
$target['scheme'] = $b['scheme'] ?? null;
|
||||
if ($r['authorized']) {
|
||||
$target['host'] = isset($r['host']) ? $r['host'] : null;
|
||||
$target['port'] = isset($r['port']) ? $r['port'] : null;
|
||||
$target['user'] = isset($r['user']) ? $r['user'] : null;
|
||||
$target['pass'] = isset($r['pass']) ? $r['pass'] : null;
|
||||
$target['host'] = $r['host'] ?? null;
|
||||
$target['port'] = $r['port'] ?? null;
|
||||
$target['user'] = $r['user'] ?? null;
|
||||
$target['pass'] = $r['pass'] ?? 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 {
|
||||
$target['host'] = isset($b['host']) ? $b['host'] : null;
|
||||
$target['port'] = isset($b['port']) ? $b['port'] : null;
|
||||
$target['user'] = isset($b['user']) ? $b['user'] : null;
|
||||
$target['pass'] = isset($b['pass']) ? $b['pass'] : null;
|
||||
$target['host'] = $b['host'] ?? null;
|
||||
$target['port'] = $b['port'] ?? null;
|
||||
$target['user'] = $b['user'] ?? null;
|
||||
$target['pass'] = $b['pass'] ?? null;
|
||||
if (!isset($r['path']) || $r['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 {
|
||||
if (StringUtil::startsWith($r['path'], '/')) {
|
||||
$target['path'] = self::removeDotSegments($r['path']);
|
||||
@@ -160,14 +160,14 @@ class Url
|
||||
}
|
||||
$target['path'] = self::removeDotSegments($base . '/' . $r['path']);
|
||||
}
|
||||
$target['query'] = isset($r['query']) ? $r['query'] : null;
|
||||
$target['query'] = $r['query'] ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->relativeUrl === '') {
|
||||
$target['fragment'] = isset($b['fragment']) ? $b['fragment'] : null;
|
||||
$target['fragment'] = $b['fragment'] ?? null;
|
||||
} else {
|
||||
$target['fragment'] = isset($r['fragment']) ? $r['fragment'] : null;
|
||||
$target['fragment'] = $r['fragment'] ?? null;
|
||||
}
|
||||
$absolutized_url = $this->unparseUrl($target);
|
||||
return $absolutized_url;
|
||||
@@ -177,6 +177,8 @@ class Url
|
||||
* Parse url.
|
||||
*
|
||||
* Parse url into components of a URI as specified by RFC 3986.
|
||||
*
|
||||
* @param mixed $url
|
||||
*/
|
||||
public static function parseUrl($url)
|
||||
{
|
||||
@@ -192,6 +194,8 @@ class Url
|
||||
*
|
||||
* Percent-encode characters to represent a data octet in a component when
|
||||
* that octet's corresponding character is outside the allowed set.
|
||||
*
|
||||
* @param mixed $chars
|
||||
*/
|
||||
private static function percentEncodeChars($chars)
|
||||
{
|
||||
@@ -229,16 +233,18 @@ class Url
|
||||
* Unparse url.
|
||||
*
|
||||
* Combine url components into a url.
|
||||
*
|
||||
* @param mixed $parsed_url
|
||||
*/
|
||||
private function unparseUrl($parsed_url)
|
||||
{
|
||||
$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 = ($user || $pass) ? $pass . '@' : '';
|
||||
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
|
||||
$host = $parsed_url['host'] ?? '';
|
||||
$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'] : '';
|
||||
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['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
|
||||
// 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);
|
||||
return array (
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
274
vendor/symfony/cache/DependencyInjection/CachePoolPass.php
vendored
Normal file
274
vendor/symfony/cache/DependencyInjection/CachePoolPass.php
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
<?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\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||
use Symfony\Component\Cache\Adapter\NullAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ParameterNormalizer;
|
||||
use Symfony\Component\Cache\Messenger\EarlyExpirationDispatcher;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CachePoolPass implements CompilerPassInterface
|
||||
{
|
||||
private $cachePoolTag;
|
||||
private $kernelResetTag;
|
||||
private $cacheClearerId;
|
||||
private $cachePoolClearerTag;
|
||||
private $cacheSystemClearerId;
|
||||
private $cacheSystemClearerTag;
|
||||
private $reverseContainerId;
|
||||
private $reversibleTag;
|
||||
private $messageHandlerId;
|
||||
|
||||
public function __construct(string $cachePoolTag = 'cache.pool', string $kernelResetTag = 'kernel.reset', string $cacheClearerId = 'cache.global_clearer', string $cachePoolClearerTag = 'cache.pool.clearer', string $cacheSystemClearerId = 'cache.system_clearer', string $cacheSystemClearerTag = 'kernel.cache_clearer', string $reverseContainerId = 'reverse_container', string $reversibleTag = 'container.reversible', string $messageHandlerId = 'cache.early_expiration_handler')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
|
||||
$this->cachePoolTag = $cachePoolTag;
|
||||
$this->kernelResetTag = $kernelResetTag;
|
||||
$this->cacheClearerId = $cacheClearerId;
|
||||
$this->cachePoolClearerTag = $cachePoolClearerTag;
|
||||
$this->cacheSystemClearerId = $cacheSystemClearerId;
|
||||
$this->cacheSystemClearerTag = $cacheSystemClearerTag;
|
||||
$this->reverseContainerId = $reverseContainerId;
|
||||
$this->reversibleTag = $reversibleTag;
|
||||
$this->messageHandlerId = $messageHandlerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if ($container->hasParameter('cache.prefix.seed')) {
|
||||
$seed = $container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed'));
|
||||
} else {
|
||||
$seed = '_'.$container->getParameter('kernel.project_dir');
|
||||
$seed .= '.'.$container->getParameter('kernel.container_class');
|
||||
}
|
||||
|
||||
$needsMessageHandler = false;
|
||||
$allPools = [];
|
||||
$clearers = [];
|
||||
$attributes = [
|
||||
'provider',
|
||||
'name',
|
||||
'namespace',
|
||||
'default_lifetime',
|
||||
'early_expiration_message_bus',
|
||||
'reset',
|
||||
];
|
||||
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) {
|
||||
$adapter = $pool = $container->getDefinition($id);
|
||||
if ($pool->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
$class = $adapter->getClass();
|
||||
while ($adapter instanceof ChildDefinition) {
|
||||
$adapter = $container->findDefinition($adapter->getParent());
|
||||
$class = $class ?: $adapter->getClass();
|
||||
if ($t = $adapter->getTag($this->cachePoolTag)) {
|
||||
$tags[0] += $t[0];
|
||||
}
|
||||
}
|
||||
$name = $tags[0]['name'] ?? $id;
|
||||
if (!isset($tags[0]['namespace'])) {
|
||||
$namespaceSeed = $seed;
|
||||
if (null !== $class) {
|
||||
$namespaceSeed .= '.'.$class;
|
||||
}
|
||||
|
||||
$tags[0]['namespace'] = $this->getNamespace($namespaceSeed, $name);
|
||||
}
|
||||
if (isset($tags[0]['clearer'])) {
|
||||
$clearer = $tags[0]['clearer'];
|
||||
while ($container->hasAlias($clearer)) {
|
||||
$clearer = (string) $container->getAlias($clearer);
|
||||
}
|
||||
} else {
|
||||
$clearer = null;
|
||||
}
|
||||
unset($tags[0]['clearer'], $tags[0]['name']);
|
||||
|
||||
if (isset($tags[0]['provider'])) {
|
||||
$tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider']));
|
||||
}
|
||||
|
||||
if (ChainAdapter::class === $class) {
|
||||
$adapters = [];
|
||||
foreach ($adapter->getArgument(0) as $provider => $adapter) {
|
||||
if ($adapter instanceof ChildDefinition) {
|
||||
$chainedPool = $adapter;
|
||||
} else {
|
||||
$chainedPool = $adapter = new ChildDefinition($adapter);
|
||||
}
|
||||
|
||||
$chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]];
|
||||
$chainedClass = '';
|
||||
|
||||
while ($adapter instanceof ChildDefinition) {
|
||||
$adapter = $container->findDefinition($adapter->getParent());
|
||||
$chainedClass = $chainedClass ?: $adapter->getClass();
|
||||
if ($t = $adapter->getTag($this->cachePoolTag)) {
|
||||
$chainedTags[0] += $t[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (ChainAdapter::class === $chainedClass) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid service "%s": chain of adapters cannot reference another chain, found "%s".', $id, $chainedPool->getParent()));
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
|
||||
if (isset($chainedTags[0]['provider'])) {
|
||||
$chainedPool->replaceArgument($i++, new Reference(static::getServiceProvider($container, $chainedTags[0]['provider'])));
|
||||
}
|
||||
|
||||
if (isset($tags[0]['namespace']) && !\in_array($adapter->getClass(), [ArrayAdapter::class, NullAdapter::class], true)) {
|
||||
$chainedPool->replaceArgument($i++, $tags[0]['namespace']);
|
||||
}
|
||||
|
||||
if (isset($tags[0]['default_lifetime'])) {
|
||||
$chainedPool->replaceArgument($i++, $tags[0]['default_lifetime']);
|
||||
}
|
||||
|
||||
$adapters[] = $chainedPool;
|
||||
}
|
||||
|
||||
$pool->replaceArgument(0, $adapters);
|
||||
unset($tags[0]['provider'], $tags[0]['namespace']);
|
||||
$i = 1;
|
||||
} else {
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
foreach ($attributes as $attr) {
|
||||
if (!isset($tags[0][$attr])) {
|
||||
// no-op
|
||||
} elseif ('reset' === $attr) {
|
||||
if ($tags[0][$attr]) {
|
||||
$pool->addTag($this->kernelResetTag, ['method' => $tags[0][$attr]]);
|
||||
}
|
||||
} elseif ('early_expiration_message_bus' === $attr) {
|
||||
$needsMessageHandler = true;
|
||||
$pool->addMethodCall('setCallbackWrapper', [(new Definition(EarlyExpirationDispatcher::class))
|
||||
->addArgument(new Reference($tags[0]['early_expiration_message_bus']))
|
||||
->addArgument(new Reference($this->reverseContainerId))
|
||||
->addArgument((new Definition('callable'))
|
||||
->setFactory([new Reference($id), 'setCallbackWrapper'])
|
||||
->addArgument(null)
|
||||
),
|
||||
]);
|
||||
$pool->addTag($this->reversibleTag);
|
||||
} elseif ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class], true)) {
|
||||
$argument = $tags[0][$attr];
|
||||
|
||||
if ('default_lifetime' === $attr && !is_numeric($argument)) {
|
||||
$argument = (new Definition('int', [$argument]))
|
||||
->setFactory([ParameterNormalizer::class, 'normalizeDuration']);
|
||||
}
|
||||
|
||||
$pool->replaceArgument($i++, $argument);
|
||||
}
|
||||
unset($tags[0][$attr]);
|
||||
}
|
||||
if (!empty($tags[0])) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid "%s" tag for service "%s": accepted attributes are "clearer", "provider", "name", "namespace", "default_lifetime", "early_expiration_message_bus" and "reset", found "%s".', $this->cachePoolTag, $id, implode('", "', array_keys($tags[0]))));
|
||||
}
|
||||
|
||||
if (null !== $clearer) {
|
||||
$clearers[$clearer][$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE);
|
||||
}
|
||||
|
||||
$allPools[$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE);
|
||||
}
|
||||
|
||||
if (!$needsMessageHandler) {
|
||||
$container->removeDefinition($this->messageHandlerId);
|
||||
}
|
||||
|
||||
$notAliasedCacheClearerId = $this->cacheClearerId;
|
||||
while ($container->hasAlias($notAliasedCacheClearerId)) {
|
||||
$notAliasedCacheClearerId = (string) $container->getAlias($notAliasedCacheClearerId);
|
||||
}
|
||||
if ($container->hasDefinition($notAliasedCacheClearerId)) {
|
||||
$clearers[$notAliasedCacheClearerId] = $allPools;
|
||||
}
|
||||
|
||||
foreach ($clearers as $id => $pools) {
|
||||
$clearer = $container->getDefinition($id);
|
||||
if ($clearer instanceof ChildDefinition) {
|
||||
$clearer->replaceArgument(0, $pools);
|
||||
} else {
|
||||
$clearer->setArgument(0, $pools);
|
||||
}
|
||||
$clearer->addTag($this->cachePoolClearerTag);
|
||||
|
||||
if ($this->cacheSystemClearerId === $id) {
|
||||
$clearer->addTag($this->cacheSystemClearerTag);
|
||||
}
|
||||
}
|
||||
|
||||
$allPoolsKeys = array_keys($allPools);
|
||||
|
||||
if ($container->hasDefinition('console.command.cache_pool_list')) {
|
||||
$container->getDefinition('console.command.cache_pool_list')->replaceArgument(0, $allPoolsKeys);
|
||||
}
|
||||
|
||||
if ($container->hasDefinition('console.command.cache_pool_clear')) {
|
||||
$container->getDefinition('console.command.cache_pool_clear')->addArgument($allPoolsKeys);
|
||||
}
|
||||
|
||||
if ($container->hasDefinition('console.command.cache_pool_delete')) {
|
||||
$container->getDefinition('console.command.cache_pool_delete')->addArgument($allPoolsKeys);
|
||||
}
|
||||
}
|
||||
|
||||
private function getNamespace(string $seed, string $id)
|
||||
{
|
||||
return substr(str_replace('/', '-', base64_encode(hash('sha256', $id.$seed, true))), 0, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function getServiceProvider(ContainerBuilder $container, string $name)
|
||||
{
|
||||
$container->resolveEnvPlaceholders($name, null, $usedEnvs);
|
||||
|
||||
if ($usedEnvs || preg_match('#^[a-z]++:#', $name)) {
|
||||
$dsn = $name;
|
||||
|
||||
if (!$container->hasDefinition($name = '.cache_connection.'.ContainerBuilder::hash($dsn))) {
|
||||
$definition = new Definition(AbstractAdapter::class);
|
||||
$definition->setPublic(false);
|
||||
$definition->setFactory([AbstractAdapter::class, 'createConnection']);
|
||||
$definition->setArguments([$dsn, ['lazy' => true]]);
|
||||
$container->setDefinition($name, $definition);
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
64
vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php
vendored
Normal file
64
vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<?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\PruneableInterface;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
*/
|
||||
class CachePoolPrunerPass implements CompilerPassInterface
|
||||
{
|
||||
private $cacheCommandServiceId;
|
||||
private $cachePoolTag;
|
||||
|
||||
public function __construct(string $cacheCommandServiceId = 'console.command.cache_pool_prune', string $cachePoolTag = 'cache.pool')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
|
||||
$this->cacheCommandServiceId = $cacheCommandServiceId;
|
||||
$this->cachePoolTag = $cachePoolTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition($this->cacheCommandServiceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$services = [];
|
||||
|
||||
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) {
|
||||
$class = $container->getParameterBag()->resolveValue($container->getDefinition($id)->getClass());
|
||||
|
||||
if (!$reflection = $container->getReflectionClass($class)) {
|
||||
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
|
||||
}
|
||||
|
||||
if ($reflection->implementsInterface(PruneableInterface::class)) {
|
||||
$services[$id] = new Reference($id);
|
||||
}
|
||||
}
|
||||
|
||||
$container->getDefinition($this->cacheCommandServiceId)->replaceArgument(0, new IteratorArgument($services));
|
||||
}
|
||||
}
|
||||
124
vendor/symfony/cache/DoctrineProvider.php
vendored
Normal file
124
vendor/symfony/cache/DoctrineProvider.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?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 Doctrine\Common\Cache\CacheProvider;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
if (!class_exists(CacheProvider::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated Use Doctrine\Common\Cache\Psr6\DoctrineProvider instead
|
||||
*/
|
||||
class DoctrineProvider extends CacheProvider implements PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
|
||||
public function __construct(CacheItemPoolInterface $pool)
|
||||
{
|
||||
trigger_deprecation('symfony/cache', '5.4', '"%s" is deprecated, use "Doctrine\Common\Cache\Psr6\DoctrineProvider" instead.', __CLASS__);
|
||||
|
||||
$this->pool = $pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
return $this->pool instanceof PruneableInterface && $this->pool->prune();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->pool instanceof ResetInterface) {
|
||||
$this->pool->reset();
|
||||
}
|
||||
$this->setNamespace($this->getNamespace());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$item = $this->pool->getItem(rawurlencode($id));
|
||||
|
||||
return $item->isHit() ? $item->get() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return $this->pool->hasItem(rawurlencode($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
$item = $this->pool->getItem(rawurlencode($id));
|
||||
|
||||
if (0 < $lifeTime) {
|
||||
$item->expiresAfter($lifeTime);
|
||||
}
|
||||
|
||||
return $this->pool->save($item->set($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->pool->deleteItem(rawurlencode($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user