修复导入功能
This commit is contained in:
+2
-1
@@ -23,7 +23,8 @@
|
||||
"phpoffice/phpexcel": "^1.8",
|
||||
"overtrue/wechat": "~4.0",
|
||||
"jaeger/querylist": "^4.2",
|
||||
"topthink/think-queue": "3.0"
|
||||
"topthink/think-queue": "3.0",
|
||||
"topthink/think-filesystem": "1.0.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.2",
|
||||
|
||||
Generated
+100
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1e8675185db7615e99f72a032d32a58c",
|
||||
"content-hash": "1346841c35ed676c7f6819ff68f05fc1",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adbario/php-dot-notation",
|
||||
@@ -1074,6 +1074,57 @@
|
||||
],
|
||||
"time": "2022-10-04T09:16:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-cached-adapter",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem-cached-adapter.git",
|
||||
"reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/d1925efb2207ac4be3ad0c40b8277175f99ffaff",
|
||||
"reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/flysystem": "~1.0",
|
||||
"psr/cache": "^1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.9",
|
||||
"phpspec/phpspec": "^3.4",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"predis/predis": "~1.0",
|
||||
"tedivm/stash": "~0.12"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-phpredis": "Pure C implemented extension for PHP"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\Cached\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "frankdejonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"description": "An adapter decorator to enable meta-data caching.",
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/flysystem-cached-adapter/issues",
|
||||
"source": "https://github.com/thephpleague/flysystem-cached-adapter/tree/master"
|
||||
},
|
||||
"time": "2020-07-25T15:56:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/mime-type-detection",
|
||||
"version": "1.12.0",
|
||||
@@ -3688,6 +3739,54 @@
|
||||
"abandoned": true,
|
||||
"time": "2019-04-15T06:55:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-filesystem",
|
||||
"version": "v1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/think-filesystem.git",
|
||||
"reference": "cfc510520db9bcd22d8d80f51d7e415a2f470af6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/think-filesystem/zipball/cfc510520db9bcd22d8d80f51d7e415a2f470af6",
|
||||
"reference": "cfc510520db9bcd22d8d80f51d7e415a2f470af6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/flysystem": "^1.1.4",
|
||||
"league/flysystem-cached-adapter": "^1.0",
|
||||
"php": ">=7.2.5",
|
||||
"topthink/framework": "^6.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"mockery/mockery": "^1.2",
|
||||
"phpunit/phpunit": "^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"description": "The ThinkPHP6.1 Filesystem Package",
|
||||
"support": {
|
||||
"issues": "https://github.com/top-think/think-filesystem/issues",
|
||||
"source": "https://github.com/top-think/think-filesystem/tree/v1.0.1"
|
||||
},
|
||||
"time": "2022-10-26T03:50:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-helper",
|
||||
"version": "v3.1.6",
|
||||
|
||||
Vendored
+2
-2
@@ -8,11 +8,11 @@ $baseDir = dirname($vendorDir);
|
||||
return array(
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||
|
||||
Vendored
+2
-1
@@ -9,7 +9,7 @@ return array(
|
||||
'think\\view\\driver\\' => array($vendorDir . '/topthink/think-view/src'),
|
||||
'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'),
|
||||
'think\\app\\' => array($vendorDir . '/topthink/think-multi-app/src'),
|
||||
'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-template/src', $vendorDir . '/topthink/think-factory/src', $vendorDir . '/topthink/think-queue/src'),
|
||||
'think\\' => array($vendorDir . '/topthink/framework/src/think', $vendorDir . '/topthink/think-factory/src', $vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/think-queue/src', $vendorDir . '/topthink/think-template/src', $vendorDir . '/topthink/think-filesystem/src'),
|
||||
'clagiordano\\weblibs\\configmanager\\' => array($vendorDir . '/clagiordano/weblibs-configmanager/src'),
|
||||
'app\\' => array($baseDir . '/app'),
|
||||
'Tightenco\\Collect\\' => array($vendorDir . '/tightenco/collect/src/Collect'),
|
||||
@@ -41,6 +41,7 @@ return array(
|
||||
'Overtrue\\Socialite\\' => array($vendorDir . '/overtrue/socialite/src'),
|
||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||
'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'),
|
||||
'JmesPath\\' => array($vendorDir . '/mtdowling/jmespath.php/src'),
|
||||
'Jaeger\\' => array($vendorDir . '/jaeger/g-http/src'),
|
||||
|
||||
Vendored
+13
-7
@@ -9,11 +9,11 @@ class ComposerStaticInit221484bf0c47d5c5287b3869fd49b342
|
||||
public static $files = array (
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
||||
@@ -93,6 +93,7 @@ class ComposerStaticInit221484bf0c47d5c5287b3869fd49b342
|
||||
'L' =>
|
||||
array (
|
||||
'League\\MimeTypeDetection\\' => 25,
|
||||
'League\\Flysystem\\Cached\\' => 24,
|
||||
'League\\Flysystem\\' => 17,
|
||||
),
|
||||
'J' =>
|
||||
@@ -142,11 +143,12 @@ class ComposerStaticInit221484bf0c47d5c5287b3869fd49b342
|
||||
'think\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/topthink/framework/src/think',
|
||||
1 => __DIR__ . '/..' . '/topthink/think-helper/src',
|
||||
2 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
||||
3 => __DIR__ . '/..' . '/topthink/think-template/src',
|
||||
4 => __DIR__ . '/..' . '/topthink/think-factory/src',
|
||||
5 => __DIR__ . '/..' . '/topthink/think-queue/src',
|
||||
1 => __DIR__ . '/..' . '/topthink/think-factory/src',
|
||||
2 => __DIR__ . '/..' . '/topthink/think-helper/src',
|
||||
3 => __DIR__ . '/..' . '/topthink/think-orm/src',
|
||||
4 => __DIR__ . '/..' . '/topthink/think-queue/src',
|
||||
5 => __DIR__ . '/..' . '/topthink/think-template/src',
|
||||
6 => __DIR__ . '/..' . '/topthink/think-filesystem/src',
|
||||
),
|
||||
'clagiordano\\weblibs\\configmanager\\' =>
|
||||
array (
|
||||
@@ -273,6 +275,10 @@ class ComposerStaticInit221484bf0c47d5c5287b3869fd49b342
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/mime-type-detection/src',
|
||||
),
|
||||
'League\\Flysystem\\Cached\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem-cached-adapter/src',
|
||||
),
|
||||
'League\\Flysystem\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
||||
|
||||
Vendored
+105
@@ -1125,6 +1125,60 @@
|
||||
],
|
||||
"install-path": "../league/flysystem"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-cached-adapter",
|
||||
"version": "1.1.0",
|
||||
"version_normalized": "1.1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem-cached-adapter.git",
|
||||
"reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/d1925efb2207ac4be3ad0c40b8277175f99ffaff",
|
||||
"reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/flysystem": "~1.0",
|
||||
"psr/cache": "^1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.9",
|
||||
"phpspec/phpspec": "^3.4",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"predis/predis": "~1.0",
|
||||
"tedivm/stash": "~0.12"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-phpredis": "Pure C implemented extension for PHP"
|
||||
},
|
||||
"time": "2020-07-25T15:56:04+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\Cached\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "frankdejonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"description": "An adapter decorator to enable meta-data caching.",
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/flysystem-cached-adapter/issues",
|
||||
"source": "https://github.com/thephpleague/flysystem-cached-adapter/tree/master"
|
||||
},
|
||||
"install-path": "../league/flysystem-cached-adapter"
|
||||
},
|
||||
{
|
||||
"name": "league/mime-type-detection",
|
||||
"version": "1.12.0",
|
||||
@@ -3895,6 +3949,57 @@
|
||||
"abandoned": true,
|
||||
"install-path": "../topthink/think-factory"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-filesystem",
|
||||
"version": "v1.0.1",
|
||||
"version_normalized": "1.0.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/top-think/think-filesystem.git",
|
||||
"reference": "cfc510520db9bcd22d8d80f51d7e415a2f470af6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/top-think/think-filesystem/zipball/cfc510520db9bcd22d8d80f51d7e415a2f470af6",
|
||||
"reference": "cfc510520db9bcd22d8d80f51d7e415a2f470af6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/flysystem": "^1.1.4",
|
||||
"league/flysystem-cached-adapter": "^1.0",
|
||||
"php": ">=7.2.5",
|
||||
"topthink/framework": "^6.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"mockery/mockery": "^1.2",
|
||||
"phpunit/phpunit": "^8.0"
|
||||
},
|
||||
"time": "2022-10-26T03:50:24+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"description": "The ThinkPHP6.1 Filesystem Package",
|
||||
"support": {
|
||||
"issues": "https://github.com/top-think/think-filesystem/issues",
|
||||
"source": "https://github.com/top-think/think-filesystem/tree/v1.0.1"
|
||||
},
|
||||
"install-path": "../topthink/think-filesystem"
|
||||
},
|
||||
{
|
||||
"name": "topthink/think-helper",
|
||||
"version": "v3.1.6",
|
||||
|
||||
Vendored
+24
-6
@@ -1,9 +1,9 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'hamm/y-admin',
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '95d366412d3c3875146f9e30ccc76c567a4aae39',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -110,9 +110,9 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'hamm/y-admin' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '95d366412d3c3875146f9e30ccc76c567a4aae39',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -154,6 +154,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'league/flysystem-cached-adapter' => array(
|
||||
'pretty_version' => '1.1.0',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => 'd1925efb2207ac4be3ad0c40b8277175f99ffaff',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../league/flysystem-cached-adapter',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'league/mime-type-detection' => array(
|
||||
'pretty_version' => '1.12.0',
|
||||
'version' => '1.12.0.0',
|
||||
@@ -564,6 +573,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-filesystem' => array(
|
||||
'pretty_version' => 'v1.0.1',
|
||||
'version' => '1.0.1.0',
|
||||
'reference' => 'cfc510520db9bcd22d8d80f51d7e415a2f470af6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../topthink/think-filesystem',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'topthink/think-helper' => array(
|
||||
'pretty_version' => 'v3.1.6',
|
||||
'version' => '3.1.6.0',
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
; top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
; Unix-style newlines
|
||||
[*]
|
||||
end_of_line = LF
|
||||
|
||||
[*.php]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
@@ -0,0 +1,4 @@
|
||||
coverage
|
||||
coverage.xml
|
||||
composer.lock
|
||||
vendor
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return Symfony\CS\Config\Config::create()
|
||||
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
|
||||
->fixers(['-yoda_conditions', 'ordered_use', 'short_array_syntax'])
|
||||
->finder(Symfony\CS\Finder\DefaultFinder::create()
|
||||
->in(__DIR__.'/src/'));
|
||||
@@ -0,0 +1,34 @@
|
||||
filter:
|
||||
paths: [src/*]
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
remove_extra_empty_lines: true
|
||||
remove_php_closing_tag: true
|
||||
remove_trailing_whitespace: true
|
||||
fix_use_statements:
|
||||
remove_unused: true
|
||||
preserve_multiple: false
|
||||
preserve_blanklines: true
|
||||
order_alphabetically: true
|
||||
fix_php_opening_tag: true
|
||||
fix_linefeed: true
|
||||
fix_line_ending: true
|
||||
fix_identation_4spaces: true
|
||||
fix_doc_comments: true
|
||||
tools:
|
||||
external_code_coverage:
|
||||
timeout: 900
|
||||
runs: 6
|
||||
php_code_coverage: false
|
||||
php_code_sniffer:
|
||||
config:
|
||||
standard: PSR2
|
||||
filter:
|
||||
paths: ['src']
|
||||
php_loc:
|
||||
enabled: true
|
||||
excluded_dirs: [vendor, spec, stubs]
|
||||
php_cpd:
|
||||
enabled: true
|
||||
excluded_dirs: [vendor, spec, stubs]
|
||||
@@ -0,0 +1,29 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: 5.5
|
||||
|
||||
env:
|
||||
- COMPOSER_OPTS=""
|
||||
- COMPOSER_OPTS="--prefer-lowest"
|
||||
|
||||
install:
|
||||
- if [[ "${TRAVIS_PHP_VERSION}" == "5.5" ]]; then composer require phpunit/phpunit:^4.8.36 phpspec/phpspec:^2 --prefer-dist --update-with-dependencies; fi
|
||||
- if [[ "${TRAVIS_PHP_VERSION}" == "7.2" ]]; then composer require phpunit/phpunit:^6.0 --prefer-dist --update-with-dependencies; fi
|
||||
- travis_retry composer update --prefer-dist $COMPOSER_OPTS
|
||||
|
||||
script:
|
||||
- vendor/bin/phpspec run
|
||||
- vendor/bin/phpunit
|
||||
|
||||
after_script:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar'
|
||||
- php ocular.phar code-coverage:upload --format=php-clover ./clover/phpunit.xml'
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015 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
|
||||
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.
|
||||
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "league/flysystem-cached-adapter",
|
||||
"description": "An adapter decorator to enable meta-data caching.",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\Cached\\": "src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"league/flysystem": "~1.0",
|
||||
"psr/cache": "^1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpspec/phpspec": "^3.4",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"mockery/mockery": "~0.9",
|
||||
"predis/predis": "~1.0",
|
||||
"tedivm/stash": "~0.12"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-phpredis": "Pure C implemented extension for PHP"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "frankdejonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
suites:
|
||||
cached_adapter_suite:
|
||||
namespace: League\Flysystem\Cached
|
||||
psr4_prefix: League\Flysystem\Cached
|
||||
formatter.name: pretty
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
include __DIR__.'/vendor/autoload.php';
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="./phpunit.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="true"
|
||||
verbose="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="flysystem/tests">
|
||||
<directory suffix=".php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true"/>
|
||||
<log type="coverage-html" target="coverage" showUncoveredFiles="true"/>
|
||||
<log type="coverage-clover" target="clover/phpunit.xml" showUncoveredFiles="true"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,20 @@
|
||||
# Flysystem Cached CachedAdapter
|
||||
|
||||
[](https://twitter.com/frankdejonge)
|
||||
[](https://travis-ci.org/thephpleague/flysystem-cached-adapter)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/flysystem-cached-adapter/code-structure)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/flysystem-cached-adapter)
|
||||
[](LICENSE)
|
||||
[](https://packagist.org/packages/league/flysystem-cached-adapter)
|
||||
[](https://packagist.org/packages/league/flysystem-cached-adapter)
|
||||
|
||||
|
||||
The adapter decorator caches metadata and directory listings.
|
||||
|
||||
```bash
|
||||
composer require league/flysystem-cached-adapter
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
[Check out the docs.](https://flysystem.thephpleague.com/docs/advanced/caching/)
|
||||
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
|
||||
namespace spec\League\Flysystem\Cached;
|
||||
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Cached\CacheInterface;
|
||||
use League\Flysystem\Config;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
|
||||
class CachedAdapterSpec extends ObjectBehavior
|
||||
{
|
||||
/**
|
||||
* @var AdapterInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var CacheInterface
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
public function let(AdapterInterface $adapter, CacheInterface $cache)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->cache = $cache;
|
||||
$this->cache->load()->shouldBeCalled();
|
||||
$this->beConstructedWith($adapter, $cache);
|
||||
}
|
||||
|
||||
public function it_is_initializable()
|
||||
{
|
||||
$this->shouldHaveType('League\Flysystem\Cached\CachedAdapter');
|
||||
$this->shouldHaveType('League\Flysystem\AdapterInterface');
|
||||
}
|
||||
|
||||
public function it_should_forward_read_streams()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$response = ['path' => $path];
|
||||
$this->adapter->readStream($path)->willReturn($response);
|
||||
$this->readStream($path)->shouldbe($response);
|
||||
}
|
||||
|
||||
public function it_should_cache_writes()
|
||||
{
|
||||
$type = 'file';
|
||||
$path = 'path.txt';
|
||||
$contents = 'contents';
|
||||
$config = new Config();
|
||||
$response = compact('path', 'contents', 'type');
|
||||
$this->adapter->write($path, $contents, $config)->willReturn($response);
|
||||
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
|
||||
$this->write($path, $contents, $config)->shouldBe($response);
|
||||
}
|
||||
|
||||
public function it_should_cache_streamed_writes()
|
||||
{
|
||||
$type = 'file';
|
||||
$path = 'path.txt';
|
||||
$stream = tmpfile();
|
||||
$config = new Config();
|
||||
$response = compact('path', 'stream', 'type');
|
||||
$this->adapter->writeStream($path, $stream, $config)->willReturn($response);
|
||||
$this->cache->updateObject($path, ['contents' => false] + $response, true)->shouldBeCalled();
|
||||
$this->writeStream($path, $stream, $config)->shouldBe($response);
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function it_should_cache_streamed_updates()
|
||||
{
|
||||
$type = 'file';
|
||||
$path = 'path.txt';
|
||||
$stream = tmpfile();
|
||||
$config = new Config();
|
||||
$response = compact('path', 'stream', 'type');
|
||||
$this->adapter->updateStream($path, $stream, $config)->willReturn($response);
|
||||
$this->cache->updateObject($path, ['contents' => false] + $response, true)->shouldBeCalled();
|
||||
$this->updateStream($path, $stream, $config)->shouldBe($response);
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_writes()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$contents = 'contents';
|
||||
$config = new Config();
|
||||
$this->adapter->write($path, $contents, $config)->willReturn(false);
|
||||
$this->write($path, $contents, $config)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_streamed_writes()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$contents = tmpfile();
|
||||
$config = new Config();
|
||||
$this->adapter->writeStream($path, $contents, $config)->willReturn(false);
|
||||
$this->writeStream($path, $contents, $config)->shouldBe(false);
|
||||
fclose($contents);
|
||||
}
|
||||
|
||||
public function it_should_cache_updated()
|
||||
{
|
||||
$type = 'file';
|
||||
$path = 'path.txt';
|
||||
$contents = 'contents';
|
||||
$config = new Config();
|
||||
$response = compact('path', 'contents', 'type');
|
||||
$this->adapter->update($path, $contents, $config)->willReturn($response);
|
||||
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
|
||||
$this->update($path, $contents, $config)->shouldBe($response);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_updates()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$contents = 'contents';
|
||||
$config = new Config();
|
||||
$this->adapter->update($path, $contents, $config)->willReturn(false);
|
||||
$this->update($path, $contents, $config)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_streamed_updates()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$contents = tmpfile();
|
||||
$config = new Config();
|
||||
$this->adapter->updateStream($path, $contents, $config)->willReturn(false);
|
||||
$this->updateStream($path, $contents, $config)->shouldBe(false);
|
||||
fclose($contents);
|
||||
}
|
||||
|
||||
public function it_should_cache_renames()
|
||||
{
|
||||
$old = 'old.txt';
|
||||
$new = 'new.txt';
|
||||
$this->adapter->rename($old, $new)->willReturn(true);
|
||||
$this->cache->rename($old, $new)->shouldBeCalled();
|
||||
$this->rename($old, $new)->shouldBe(true);
|
||||
}
|
||||
|
||||
public function it_should_ignore_rename_fails()
|
||||
{
|
||||
$old = 'old.txt';
|
||||
$new = 'new.txt';
|
||||
$this->adapter->rename($old, $new)->willReturn(false);
|
||||
$this->rename($old, $new)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_cache_copies()
|
||||
{
|
||||
$old = 'old.txt';
|
||||
$new = 'new.txt';
|
||||
$this->adapter->copy($old, $new)->willReturn(true);
|
||||
$this->cache->copy($old, $new)->shouldBeCalled();
|
||||
$this->copy($old, $new)->shouldBe(true);
|
||||
}
|
||||
|
||||
public function it_should_ignore_copy_fails()
|
||||
{
|
||||
$old = 'old.txt';
|
||||
$new = 'new.txt';
|
||||
$this->adapter->copy($old, $new)->willReturn(false);
|
||||
$this->copy($old, $new)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_cache_deletes()
|
||||
{
|
||||
$delete = 'delete.txt';
|
||||
$this->adapter->delete($delete)->willReturn(true);
|
||||
$this->cache->delete($delete)->shouldBeCalled();
|
||||
$this->delete($delete)->shouldBe(true);
|
||||
}
|
||||
|
||||
public function it_should_ignore_delete_fails()
|
||||
{
|
||||
$delete = 'delete.txt';
|
||||
$this->adapter->delete($delete)->willReturn(false);
|
||||
$this->delete($delete)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_cache_dir_deletes()
|
||||
{
|
||||
$delete = 'delete';
|
||||
$this->adapter->deleteDir($delete)->willReturn(true);
|
||||
$this->cache->deleteDir($delete)->shouldBeCalled();
|
||||
$this->deleteDir($delete)->shouldBe(true);
|
||||
}
|
||||
|
||||
public function it_should_ignore_delete_dir_fails()
|
||||
{
|
||||
$delete = 'delete';
|
||||
$this->adapter->deleteDir($delete)->willReturn(false);
|
||||
$this->deleteDir($delete)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_cache_dir_creates()
|
||||
{
|
||||
$dirname = 'dirname';
|
||||
$config = new Config();
|
||||
$response = ['path' => $dirname, 'type' => 'dir'];
|
||||
$this->adapter->createDir($dirname, $config)->willReturn($response);
|
||||
$this->cache->updateObject($dirname, $response, true)->shouldBeCalled();
|
||||
$this->createDir($dirname, $config)->shouldBe($response);
|
||||
}
|
||||
|
||||
public function it_should_ignore_create_dir_fails()
|
||||
{
|
||||
$dirname = 'dirname';
|
||||
$config = new Config();
|
||||
$this->adapter->createDir($dirname, $config)->willReturn(false);
|
||||
$this->createDir($dirname, $config)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_cache_set_visibility()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$visibility = AdapterInterface::VISIBILITY_PUBLIC;
|
||||
$this->adapter->setVisibility($path, $visibility)->willReturn(true);
|
||||
$this->cache->updateObject($path, ['path' => $path, 'visibility' => $visibility], true)->shouldBeCalled();
|
||||
$this->setVisibility($path, $visibility)->shouldBe(true);
|
||||
}
|
||||
|
||||
public function it_should_ignore_set_visibility_fails()
|
||||
{
|
||||
$dirname = 'delete';
|
||||
$visibility = AdapterInterface::VISIBILITY_PUBLIC;
|
||||
$this->adapter->setVisibility($dirname, $visibility)->willReturn(false);
|
||||
$this->setVisibility($dirname, $visibility)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_indicate_missing_files()
|
||||
{
|
||||
$this->cache->has($path = 'path.txt')->willReturn(false);
|
||||
$this->has($path)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_indicate_file_existance()
|
||||
{
|
||||
$this->cache->has($path = 'path.txt')->willReturn(true);
|
||||
$this->has($path)->shouldBe(true);
|
||||
}
|
||||
|
||||
public function it_should_cache_missing_files()
|
||||
{
|
||||
$this->cache->has($path = 'path.txt')->willReturn(null);
|
||||
$this->adapter->has($path)->willReturn(false);
|
||||
$this->cache->storeMiss($path)->shouldBeCalled();
|
||||
$this->has($path)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_delete_when_metadata_is_missing()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$this->cache->has($path)->willReturn(true);
|
||||
$this->cache->getSize($path)->willReturn(['path' => $path]);
|
||||
$this->adapter->getSize($path)->willReturn($response = ['path' => $path, 'size' => 1024]);
|
||||
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
|
||||
$this->getSize($path)->shouldBe($response);
|
||||
}
|
||||
|
||||
public function it_should_cache_has()
|
||||
{
|
||||
$this->cache->has($path = 'path.txt')->willReturn(null);
|
||||
$this->adapter->has($path)->willReturn(true);
|
||||
$this->cache->updateObject($path, compact('path'), true)->shouldBeCalled();
|
||||
$this->has($path)->shouldBe(true);
|
||||
}
|
||||
|
||||
public function it_should_list_cached_contents()
|
||||
{
|
||||
$this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(true);
|
||||
$response = [['path' => 'path.txt']];
|
||||
$this->cache->listContents($dirname, $recursive)->willReturn($response);
|
||||
$this->listContents($dirname, $recursive)->shouldBe($response);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_list_contents()
|
||||
{
|
||||
$this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(false);
|
||||
$this->adapter->listContents($dirname, $recursive)->willReturn(false);
|
||||
$this->listContents($dirname, $recursive)->shouldBe(false);
|
||||
}
|
||||
|
||||
public function it_should_cache_contents_listings()
|
||||
{
|
||||
$this->cache->isComplete($dirname = 'dirname', $recursive = true)->willReturn(false);
|
||||
$response = [['path' => 'path.txt']];
|
||||
$this->adapter->listContents($dirname, $recursive)->willReturn($response);
|
||||
$this->cache->storeContents($dirname, $response, $recursive)->shouldBeCalled();
|
||||
$this->listContents($dirname, $recursive)->shouldBe($response);
|
||||
}
|
||||
|
||||
public function it_should_use_cached_visibility()
|
||||
{
|
||||
$this->make_it_use_getter_cache('getVisibility', 'path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'visibility' => AdapterInterface::VISIBILITY_PUBLIC,
|
||||
]);
|
||||
}
|
||||
|
||||
public function it_should_cache_get_visibility()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$response = ['visibility' => AdapterInterface::VISIBILITY_PUBLIC, 'path' => $path];
|
||||
$this->make_it_cache_getter('getVisibility', $path, $response);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_get_visibility()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$this->make_it_ignore_failed_getter('getVisibility', $path);
|
||||
}
|
||||
|
||||
public function it_should_use_cached_timestamp()
|
||||
{
|
||||
$this->make_it_use_getter_cache('getTimestamp', 'path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'timestamp' => 1234,
|
||||
]);
|
||||
}
|
||||
|
||||
public function it_should_cache_timestamps()
|
||||
{
|
||||
$this->make_it_cache_getter('getTimestamp', 'path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'timestamp' => 1234,
|
||||
]);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_get_timestamps()
|
||||
{
|
||||
$this->make_it_ignore_failed_getter('getTimestamp', 'path.txt');
|
||||
}
|
||||
|
||||
public function it_should_cache_get_metadata()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$response = ['visibility' => AdapterInterface::VISIBILITY_PUBLIC, 'path' => $path];
|
||||
$this->make_it_cache_getter('getMetadata', $path, $response);
|
||||
}
|
||||
|
||||
public function it_should_use_cached_metadata()
|
||||
{
|
||||
$this->make_it_use_getter_cache('getMetadata', 'path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'timestamp' => 1234,
|
||||
]);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_get_metadata()
|
||||
{
|
||||
$this->make_it_ignore_failed_getter('getMetadata', 'path.txt');
|
||||
}
|
||||
|
||||
public function it_should_cache_get_size()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$response = ['size' => 1234, 'path' => $path];
|
||||
$this->make_it_cache_getter('getSize', $path, $response);
|
||||
}
|
||||
|
||||
public function it_should_use_cached_size()
|
||||
{
|
||||
$this->make_it_use_getter_cache('getSize', 'path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'size' => 1234,
|
||||
]);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_get_size()
|
||||
{
|
||||
$this->make_it_ignore_failed_getter('getSize', 'path.txt');
|
||||
}
|
||||
|
||||
public function it_should_cache_get_mimetype()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$response = ['mimetype' => 'text/plain', 'path' => $path];
|
||||
$this->make_it_cache_getter('getMimetype', $path, $response);
|
||||
}
|
||||
|
||||
public function it_should_use_cached_mimetype()
|
||||
{
|
||||
$this->make_it_use_getter_cache('getMimetype', 'path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'mimetype' => 'text/plain',
|
||||
]);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_get_mimetype()
|
||||
{
|
||||
$this->make_it_ignore_failed_getter('getMimetype', 'path.txt');
|
||||
}
|
||||
|
||||
public function it_should_cache_reads()
|
||||
{
|
||||
$path = 'path.txt';
|
||||
$response = ['path' => $path, 'contents' => 'contents'];
|
||||
$this->make_it_cache_getter('read', $path, $response);
|
||||
}
|
||||
|
||||
public function it_should_use_cached_file_contents()
|
||||
{
|
||||
$this->make_it_use_getter_cache('read', 'path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'contents' => 'contents'
|
||||
]);
|
||||
}
|
||||
|
||||
public function it_should_ignore_failed_reads()
|
||||
{
|
||||
$this->make_it_ignore_failed_getter('read', 'path.txt');
|
||||
}
|
||||
|
||||
protected function make_it_use_getter_cache($method, $path, $response)
|
||||
{
|
||||
$this->cache->{$method}($path)->willReturn($response);
|
||||
$this->{$method}($path)->shouldBe($response);
|
||||
}
|
||||
|
||||
protected function make_it_cache_getter($method, $path, $response)
|
||||
{
|
||||
$this->cache->{$method}($path)->willReturn(false);
|
||||
$this->adapter->{$method}($path)->willReturn($response);
|
||||
$this->cache->updateObject($path, $response, true)->shouldBeCalled();
|
||||
$this->{$method}($path)->shouldBe($response);
|
||||
}
|
||||
|
||||
protected function make_it_ignore_failed_getter($method, $path)
|
||||
{
|
||||
$this->cache->{$method}($path)->willReturn(false);
|
||||
$this->adapter->{$method}($path)->willReturn(false);
|
||||
$this->{$method}($path)->shouldBe(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached;
|
||||
|
||||
use League\Flysystem\ReadInterface;
|
||||
|
||||
interface CacheInterface extends ReadInterface
|
||||
{
|
||||
/**
|
||||
* Check whether the directory listing of a given directory is complete.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isComplete($dirname, $recursive);
|
||||
|
||||
/**
|
||||
* Set a directory to completely listed.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param bool $recursive
|
||||
*/
|
||||
public function setComplete($dirname, $recursive);
|
||||
|
||||
/**
|
||||
* Store the contents of a directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param array $contents
|
||||
* @param bool $recursive
|
||||
*/
|
||||
public function storeContents($directory, array $contents, $recursive);
|
||||
|
||||
/**
|
||||
* Flush the cache.
|
||||
*/
|
||||
public function flush();
|
||||
|
||||
/**
|
||||
* Autosave trigger.
|
||||
*/
|
||||
public function autosave();
|
||||
|
||||
/**
|
||||
* Store the cache.
|
||||
*/
|
||||
public function save();
|
||||
|
||||
/**
|
||||
* Load the cache.
|
||||
*/
|
||||
public function load();
|
||||
|
||||
/**
|
||||
* Rename a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*/
|
||||
public function rename($path, $newpath);
|
||||
|
||||
/**
|
||||
* Copy a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $newpath
|
||||
*/
|
||||
public function copy($path, $newpath);
|
||||
|
||||
/**
|
||||
* Delete an object from cache.
|
||||
*
|
||||
* @param string $path object path
|
||||
*/
|
||||
public function delete($path);
|
||||
|
||||
/**
|
||||
* Delete all objects from from a directory.
|
||||
*
|
||||
* @param string $dirname directory path
|
||||
*/
|
||||
public function deleteDir($dirname);
|
||||
|
||||
/**
|
||||
* Update the metadata for an object.
|
||||
*
|
||||
* @param string $path object path
|
||||
* @param array $object object metadata
|
||||
* @param bool $autosave whether to trigger the autosave routine
|
||||
*/
|
||||
public function updateObject($path, array $object, $autosave = false);
|
||||
|
||||
/**
|
||||
* Store object hit miss.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function storeMiss($path);
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached;
|
||||
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Config;
|
||||
|
||||
class CachedAdapter implements AdapterInterface
|
||||
{
|
||||
/**
|
||||
* @var AdapterInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var CacheInterface
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param AdapterInterface $adapter
|
||||
* @param CacheInterface $cache
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter, CacheInterface $cache)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->cache = $cache;
|
||||
$this->cache->load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying Adapter implementation.
|
||||
*
|
||||
* @return AdapterInterface
|
||||
*/
|
||||
public function getAdapter()
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the used Cache implementation.
|
||||
*
|
||||
* @return CacheInterface
|
||||
*/
|
||||
public function getCache()
|
||||
{
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($path, $contents, Config $config)
|
||||
{
|
||||
$result = $this->adapter->write($path, $contents, $config);
|
||||
|
||||
if ($result !== false) {
|
||||
$result['type'] = 'file';
|
||||
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function writeStream($path, $resource, Config $config)
|
||||
{
|
||||
$result = $this->adapter->writeStream($path, $resource, $config);
|
||||
|
||||
if ($result !== false) {
|
||||
$result['type'] = 'file';
|
||||
$contents = false;
|
||||
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function update($path, $contents, Config $config)
|
||||
{
|
||||
$result = $this->adapter->update($path, $contents, $config);
|
||||
|
||||
if ($result !== false) {
|
||||
$result['type'] = 'file';
|
||||
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateStream($path, $resource, Config $config)
|
||||
{
|
||||
$result = $this->adapter->updateStream($path, $resource, $config);
|
||||
|
||||
if ($result !== false) {
|
||||
$result['type'] = 'file';
|
||||
$contents = false;
|
||||
$this->cache->updateObject($path, $result + compact('path', 'contents'), true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rename($path, $newPath)
|
||||
{
|
||||
$result = $this->adapter->rename($path, $newPath);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->cache->rename($path, $newPath);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
$result = $this->adapter->copy($path, $newpath);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->cache->copy($path, $newpath);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
$result = $this->adapter->delete($path);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->cache->delete($path);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
$result = $this->adapter->deleteDir($dirname);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->cache->deleteDir($dirname);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createDir($dirname, Config $config)
|
||||
{
|
||||
$result = $this->adapter->createDir($dirname, $config);
|
||||
|
||||
if ($result !== false) {
|
||||
$type = 'dir';
|
||||
$path = $dirname;
|
||||
$this->cache->updateObject($dirname, compact('path', 'type'), true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setVisibility($path, $visibility)
|
||||
{
|
||||
$result = $this->adapter->setVisibility($path, $visibility);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->cache->updateObject($path, compact('path', 'visibility'), true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
$cacheHas = $this->cache->has($path);
|
||||
|
||||
if ($cacheHas !== null) {
|
||||
return $cacheHas;
|
||||
}
|
||||
|
||||
$adapterResponse = $this->adapter->has($path);
|
||||
|
||||
if (! $adapterResponse) {
|
||||
$this->cache->storeMiss($path);
|
||||
} else {
|
||||
$cacheEntry = is_array($adapterResponse) ? $adapterResponse : compact('path');
|
||||
$this->cache->updateObject($path, $cacheEntry, true);
|
||||
}
|
||||
|
||||
return $adapterResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
return $this->callWithFallback('contents', $path, 'read');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
return $this->adapter->readStream($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path prefix.
|
||||
*
|
||||
* @return string|null path prefix or null if pathPrefix is empty
|
||||
*/
|
||||
public function getPathPrefix()
|
||||
{
|
||||
return $this->adapter->getPathPrefix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix a path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string prefixed path
|
||||
*/
|
||||
public function applyPathPrefix($path)
|
||||
{
|
||||
return $this->adapter->applyPathPrefix($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
if ($this->cache->isComplete($directory, $recursive)) {
|
||||
return $this->cache->listContents($directory, $recursive);
|
||||
}
|
||||
|
||||
$result = $this->adapter->listContents($directory, $recursive);
|
||||
|
||||
if ($result !== false) {
|
||||
$this->cache->storeContents($directory, $result, $recursive);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
return $this->callWithFallback(null, $path, 'getMetadata');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
return $this->callWithFallback('size', $path, 'getSize');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
return $this->callWithFallback('mimetype', $path, 'getMimetype');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
return $this->callWithFallback('timestamp', $path, 'getTimestamp');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
return $this->callWithFallback('visibility', $path, 'getVisibility');
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a method and cache the response.
|
||||
*
|
||||
* @param string $property
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function callWithFallback($property, $path, $method)
|
||||
{
|
||||
$result = $this->cache->{$method}($path);
|
||||
|
||||
if ($result !== false && ($property === null || array_key_exists($property, $result))) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->adapter->{$method}($path);
|
||||
|
||||
if ($result) {
|
||||
$object = $result + compact('path');
|
||||
$this->cache->updateObject($path, $object, true);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
use League\Flysystem\Cached\CacheInterface;
|
||||
use League\Flysystem\Util;
|
||||
|
||||
abstract class AbstractCache implements CacheInterface
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $autosave = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $complete = [];
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (! $this->autosave) {
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the autosave setting.
|
||||
*
|
||||
* @return bool autosave
|
||||
*/
|
||||
public function getAutosave()
|
||||
{
|
||||
return $this->autosave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the autosave setting.
|
||||
*
|
||||
* @param bool $autosave
|
||||
*/
|
||||
public function setAutosave($autosave)
|
||||
{
|
||||
$this->autosave = $autosave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the contents listing.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param array $contents
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array contents listing
|
||||
*/
|
||||
public function storeContents($directory, array $contents, $recursive = false)
|
||||
{
|
||||
$directories = [$directory];
|
||||
|
||||
foreach ($contents as $object) {
|
||||
$this->updateObject($object['path'], $object);
|
||||
$object = $this->cache[$object['path']];
|
||||
|
||||
if ($recursive && $this->pathIsInDirectory($directory, $object['path'])) {
|
||||
$directories[] = $object['dirname'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_unique($directories) as $directory) {
|
||||
$this->setComplete($directory, $recursive);
|
||||
}
|
||||
|
||||
$this->autosave();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the metadata for an object.
|
||||
*
|
||||
* @param string $path object path
|
||||
* @param array $object object metadata
|
||||
* @param bool $autosave whether to trigger the autosave routine
|
||||
*/
|
||||
public function updateObject($path, array $object, $autosave = false)
|
||||
{
|
||||
if (! $this->has($path)) {
|
||||
$this->cache[$path] = Util::pathinfo($path);
|
||||
}
|
||||
|
||||
$this->cache[$path] = array_merge($this->cache[$path], $object);
|
||||
|
||||
if ($autosave) {
|
||||
$this->autosave();
|
||||
}
|
||||
|
||||
$this->ensureParentDirectories($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store object hit miss.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function storeMiss($path)
|
||||
{
|
||||
$this->cache[$path] = false;
|
||||
$this->autosave();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents listing.
|
||||
*
|
||||
* @param string $dirname
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array contents listing
|
||||
*/
|
||||
public function listContents($dirname = '', $recursive = false)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->cache as $object) {
|
||||
if ($object === false) {
|
||||
continue;
|
||||
}
|
||||
if ($object['dirname'] === $dirname) {
|
||||
$result[] = $object;
|
||||
} elseif ($recursive && $this->pathIsInDirectory($dirname, $object['path'])) {
|
||||
$result[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
if ($path !== false && array_key_exists($path, $this->cache)) {
|
||||
return $this->cache[$path] !== false;
|
||||
}
|
||||
|
||||
if ($this->isComplete(Util::dirname($path), false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
if (isset($this->cache[$path]['contents']) && $this->cache[$path]['contents'] !== false) {
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
if ($this->has($path)) {
|
||||
$object = $this->cache[$path];
|
||||
unset($this->cache[$path]);
|
||||
$object['path'] = $newpath;
|
||||
$object = array_merge($object, Util::pathinfo($newpath));
|
||||
$this->cache[$newpath] = $object;
|
||||
$this->autosave();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
if ($this->has($path)) {
|
||||
$object = $this->cache[$path];
|
||||
$object = array_merge($object, Util::pathinfo($newpath));
|
||||
$this->updateObject($newpath, $object, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
$this->storeMiss($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteDir($dirname)
|
||||
{
|
||||
foreach ($this->cache as $path => $object) {
|
||||
if ($this->pathIsInDirectory($dirname, $path) || $path === $dirname) {
|
||||
unset($this->cache[$path]);
|
||||
}
|
||||
}
|
||||
|
||||
unset($this->complete[$dirname]);
|
||||
|
||||
$this->autosave();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
if (isset($this->cache[$path]['mimetype'])) {
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
if (! $result = $this->read($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mimetype = Util::guessMimeType($path, $result['contents']);
|
||||
$this->cache[$path]['mimetype'] = $mimetype;
|
||||
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
if (isset($this->cache[$path]['size'])) {
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
if (isset($this->cache[$path]['timestamp'])) {
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
if (isset($this->cache[$path]['visibility'])) {
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
if (isset($this->cache[$path]['type'])) {
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isComplete($dirname, $recursive)
|
||||
{
|
||||
if (! array_key_exists($dirname, $this->complete)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($recursive && $this->complete[$dirname] !== 'recursive') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setComplete($dirname, $recursive)
|
||||
{
|
||||
$this->complete[$dirname] = $recursive ? 'recursive' : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the contents from a listing.
|
||||
*
|
||||
* @param array $contents object listing
|
||||
*
|
||||
* @return array filtered contents
|
||||
*/
|
||||
public function cleanContents(array $contents)
|
||||
{
|
||||
$cachedProperties = array_flip([
|
||||
'path', 'dirname', 'basename', 'extension', 'filename',
|
||||
'size', 'mimetype', 'visibility', 'timestamp', 'type',
|
||||
'md5',
|
||||
]);
|
||||
|
||||
foreach ($contents as $path => $object) {
|
||||
if (is_array($object)) {
|
||||
$contents[$path] = array_intersect_key($object, $cachedProperties);
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$this->cache = [];
|
||||
$this->complete = [];
|
||||
$this->autosave();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function autosave()
|
||||
{
|
||||
if ($this->autosave) {
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve serialized cache data.
|
||||
*
|
||||
* @return string serialized data
|
||||
*/
|
||||
public function getForStorage()
|
||||
{
|
||||
$cleaned = $this->cleanContents($this->cache);
|
||||
|
||||
return json_encode([$cleaned, $this->complete]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load from serialized cache data.
|
||||
*
|
||||
* @param string $json
|
||||
*/
|
||||
public function setFromStorage($json)
|
||||
{
|
||||
list($cache, $complete) = json_decode($json, true);
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($cache) && is_array($complete)) {
|
||||
$this->cache = $cache;
|
||||
$this->complete = $complete;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure parent directories of an object.
|
||||
*
|
||||
* @param string $path object path
|
||||
*/
|
||||
public function ensureParentDirectories($path)
|
||||
{
|
||||
$object = $this->cache[$path];
|
||||
|
||||
while ($object['dirname'] !== '' && ! isset($this->cache[$object['dirname']])) {
|
||||
$object = Util::pathinfo($object['dirname']);
|
||||
$object['type'] = 'dir';
|
||||
$this->cache[$object['path']] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the path is inside the directory.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function pathIsInDirectory($directory, $path)
|
||||
{
|
||||
return $directory === '' || strpos($path, $directory . '/') === 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Config;
|
||||
|
||||
class Adapter extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var AdapterInterface An adapter
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var string the file to cache to
|
||||
*/
|
||||
protected $file;
|
||||
|
||||
/**
|
||||
* @var int|null seconds until cache expiration
|
||||
*/
|
||||
protected $expire = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param AdapterInterface $adapter adapter
|
||||
* @param string $file the file to cache to
|
||||
* @param int|null $expire seconds until cache expiration
|
||||
*/
|
||||
public function __construct(AdapterInterface $adapter, $file, $expire = null)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->file = $file;
|
||||
$this->setExpire($expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expiration time in seconds.
|
||||
*
|
||||
* @param int $expire relative expiration time
|
||||
*/
|
||||
protected function setExpire($expire)
|
||||
{
|
||||
if ($expire) {
|
||||
$this->expire = $this->getTime($expire);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expiration time in seconds.
|
||||
*
|
||||
* @param int $time relative expiration time
|
||||
*
|
||||
* @return int actual expiration time
|
||||
*/
|
||||
protected function getTime($time = 0)
|
||||
{
|
||||
return intval(microtime(true)) + $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFromStorage($json)
|
||||
{
|
||||
list($cache, $complete, $expire) = json_decode($json, true);
|
||||
|
||||
if (! $expire || $expire > $this->getTime()) {
|
||||
$this->cache = is_array($cache) ? $cache : [];
|
||||
$this->complete = is_array($complete) ? $complete : [];
|
||||
} else {
|
||||
$this->adapter->delete($this->file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->adapter->has($this->file)) {
|
||||
$file = $this->adapter->read($this->file);
|
||||
if ($file && !empty($file['contents'])) {
|
||||
$this->setFromStorage($file['contents']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForStorage()
|
||||
{
|
||||
$cleaned = $this->cleanContents($this->cache);
|
||||
|
||||
return json_encode([$cleaned, $this->complete, $this->expire]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$config = new Config();
|
||||
$contents = $this->getForStorage();
|
||||
|
||||
if ($this->adapter->has($this->file)) {
|
||||
$this->adapter->update($this->file, $contents, $config);
|
||||
} else {
|
||||
$this->adapter->write($this->file, $contents, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
use Memcached as NativeMemcached;
|
||||
|
||||
class Memcached extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var string storage key
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var int|null seconds until cache expiration
|
||||
*/
|
||||
protected $expire;
|
||||
|
||||
/**
|
||||
* @var \Memcached Memcached instance
|
||||
*/
|
||||
protected $memcached;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Memcached $memcached
|
||||
* @param string $key storage key
|
||||
* @param int|null $expire seconds until cache expiration
|
||||
*/
|
||||
public function __construct(NativeMemcached $memcached, $key = 'flysystem', $expire = null)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->expire = $expire;
|
||||
$this->memcached = $memcached;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$contents = $this->memcached->get($this->key);
|
||||
|
||||
if ($contents !== false) {
|
||||
$this->setFromStorage($contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$contents = $this->getForStorage();
|
||||
$expiration = $this->expire === null ? 0 : time() + $this->expire;
|
||||
$this->memcached->set($this->key, $contents, $expiration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
class Memory extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
// There is nothing to save
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
// There is nothing to load
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
class Noop extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $autosave = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateObject($path, array $object, $autosave = false)
|
||||
{
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isComplete($dirname, $recursive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setComplete($dirname, $recursive)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function copy($path, $newpath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rename($path, $newpath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function storeContents($directory, array $contents, $recursive = false)
|
||||
{
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function storeMiss($path)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function autosave()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function readStream($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function listContents($directory = '', $recursive = false)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSize($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMimetype($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTimestamp($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVisibility($path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
use Redis;
|
||||
|
||||
class PhpRedis extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var Redis PhpRedis Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* @var string storage key
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var int|null seconds until cache expiration
|
||||
*/
|
||||
protected $expire;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Redis|null $client phpredis client
|
||||
* @param string $key storage key
|
||||
* @param int|null $expire seconds until cache expiration
|
||||
*/
|
||||
public function __construct(Redis $client = null, $key = 'flysystem', $expire = null)
|
||||
{
|
||||
$this->client = $client ?: new Redis();
|
||||
$this->key = $key;
|
||||
$this->expire = $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$contents = $this->client->get($this->key);
|
||||
|
||||
if ($contents !== false) {
|
||||
$this->setFromStorage($contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$contents = $this->getForStorage();
|
||||
$this->client->set($this->key, $contents);
|
||||
|
||||
if ($this->expire !== null) {
|
||||
$this->client->expire($this->key, $this->expire);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
class Predis extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var \Predis\Client Predis Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* @var string storage key
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var int|null seconds until cache expiration
|
||||
*/
|
||||
protected $expire;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Predis\Client $client predis client
|
||||
* @param string $key storage key
|
||||
* @param int|null $expire seconds until cache expiration
|
||||
*/
|
||||
public function __construct(Client $client = null, $key = 'flysystem', $expire = null)
|
||||
{
|
||||
$this->client = $client ?: new Client();
|
||||
$this->key = $key;
|
||||
$this->expire = $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if (($contents = $this->executeCommand('get', [$this->key])) !== null) {
|
||||
$this->setFromStorage($contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$contents = $this->getForStorage();
|
||||
$this->executeCommand('set', [$this->key, $contents]);
|
||||
|
||||
if ($this->expire !== null) {
|
||||
$this->executeCommand('expire', [$this->key, $this->expire]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Predis command.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function executeCommand($name, array $arguments)
|
||||
{
|
||||
$command = $this->client->createCommand($name, $arguments);
|
||||
|
||||
return $this->client->executeCommand($command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
class Psr6Cache extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var CacheItemPoolInterface
|
||||
*/
|
||||
private $pool;
|
||||
|
||||
/**
|
||||
* @var string storage key
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var int|null seconds until cache expiration
|
||||
*/
|
||||
protected $expire;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param CacheItemPoolInterface $pool
|
||||
* @param string $key storage key
|
||||
* @param int|null $expire seconds until cache expiration
|
||||
*/
|
||||
public function __construct(CacheItemPoolInterface $pool, $key = 'flysystem', $expire = null)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
$this->key = $key;
|
||||
$this->expire = $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$item = $this->pool->getItem($this->key);
|
||||
$item->set($this->getForStorage());
|
||||
$item->expiresAfter($this->expire);
|
||||
$this->pool->save($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$item = $this->pool->getItem($this->key);
|
||||
if ($item->isHit()) {
|
||||
$this->setFromStorage($item->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace League\Flysystem\Cached\Storage;
|
||||
|
||||
use Stash\Pool;
|
||||
|
||||
class Stash extends AbstractCache
|
||||
{
|
||||
/**
|
||||
* @var string storage key
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var int|null seconds until cache expiration
|
||||
*/
|
||||
protected $expire;
|
||||
|
||||
/**
|
||||
* @var \Stash\Pool Stash pool instance
|
||||
*/
|
||||
protected $pool;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Stash\Pool $pool
|
||||
* @param string $key storage key
|
||||
* @param int|null $expire seconds until cache expiration
|
||||
*/
|
||||
public function __construct(Pool $pool, $key = 'flysystem', $expire = null)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->expire = $expire;
|
||||
$this->pool = $pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$item = $this->pool->getItem($this->key);
|
||||
$contents = $item->get();
|
||||
|
||||
if ($item->isMiss() === false) {
|
||||
$this->setFromStorage($contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$contents = $this->getForStorage();
|
||||
$item = $this->pool->getItem($this->key);
|
||||
$item->set($contents, $this->expire);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\Adapter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AdapterCacheTests extends TestCase
|
||||
{
|
||||
public function testLoadFail()
|
||||
{
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(false);
|
||||
$cache = new Adapter($adapter, 'file.json', 10);
|
||||
$cache->load();
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testLoadExpired()
|
||||
{
|
||||
$response = ['contents' => json_encode([[], ['' => true], 1234567890]), 'path' => 'file.json'];
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(true);
|
||||
$adapter->shouldReceive('read')->once()->with('file.json')->andReturn($response);
|
||||
$adapter->shouldReceive('delete')->once()->with('file.json');
|
||||
$cache = new Adapter($adapter, 'file.json', 10);
|
||||
$cache->load();
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testLoadSuccess()
|
||||
{
|
||||
$response = ['contents' => json_encode([[], ['' => true], 9876543210]), 'path' => 'file.json'];
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(true);
|
||||
$adapter->shouldReceive('read')->once()->with('file.json')->andReturn($response);
|
||||
$cache = new Adapter($adapter, 'file.json', 10);
|
||||
$cache->load();
|
||||
$this->assertTrue($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testSaveExists()
|
||||
{
|
||||
$response = json_encode([[], [], null]);
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(true);
|
||||
$adapter->shouldReceive('update')->once()->with('file.json', $response, Mockery::any());
|
||||
$cache = new Adapter($adapter, 'file.json', null);
|
||||
$cache->save();
|
||||
}
|
||||
|
||||
public function testSaveNew()
|
||||
{
|
||||
$response = json_encode([[], [], null]);
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(false);
|
||||
$adapter->shouldReceive('write')->once()->with('file.json', $response, Mockery::any());
|
||||
$cache = new Adapter($adapter, 'file.json', null);
|
||||
$cache->save();
|
||||
}
|
||||
|
||||
public function testStoreContentsRecursive()
|
||||
{
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$adapter->shouldReceive('has')->once()->with('file.json')->andReturn(false);
|
||||
$adapter->shouldReceive('write')->once()->with('file.json', Mockery::any(), Mockery::any());
|
||||
|
||||
$cache = new Adapter($adapter, 'file.json', null);
|
||||
|
||||
$contents = [
|
||||
['path' => 'foo/bar', 'dirname' => 'foo'],
|
||||
['path' => 'afoo/bang', 'dirname' => 'afoo'],
|
||||
];
|
||||
|
||||
$cache->storeContents('foo', $contents, true);
|
||||
|
||||
$this->assertTrue($cache->isComplete('foo', true));
|
||||
$this->assertFalse($cache->isComplete('afoo', true));
|
||||
}
|
||||
|
||||
public function testDeleteDir()
|
||||
{
|
||||
$cache_data = [
|
||||
'foo' => ['path' => 'foo', 'type' => 'dir', 'dirname' => ''],
|
||||
'foo/bar' => ['path' => 'foo/bar', 'type' => 'file', 'dirname' => 'foo'],
|
||||
'foobaz' => ['path' => 'foobaz', 'type' => 'file', 'dirname' => ''],
|
||||
];
|
||||
|
||||
$response = [
|
||||
'contents' => json_encode([$cache_data, [], null]),
|
||||
'path' => 'file.json',
|
||||
];
|
||||
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$adapter->shouldReceive('has')->zeroOrMoreTimes()->with('file.json')->andReturn(true);
|
||||
$adapter->shouldReceive('read')->once()->with('file.json')->andReturn($response);
|
||||
$adapter->shouldReceive('update')->once()->with('file.json', Mockery::any(), Mockery::any())->andReturn(true);
|
||||
|
||||
$cache = new Adapter($adapter, 'file.json', null);
|
||||
$cache->load();
|
||||
|
||||
$cache->deleteDir('foo', true);
|
||||
|
||||
$this->assertSame(1, count($cache->listContents('', true)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\CachedAdapter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class InspectionTests extends TestCase {
|
||||
|
||||
public function testGetAdapter()
|
||||
{
|
||||
$adapter = Mockery::mock('League\Flysystem\AdapterInterface');
|
||||
$cache = Mockery::mock('League\Flysystem\Cached\CacheInterface');
|
||||
$cache->shouldReceive('load')->once();
|
||||
$cached_adapter = new CachedAdapter($adapter, $cache);
|
||||
$this->assertInstanceOf('League\Flysystem\AdapterInterface', $cached_adapter->getAdapter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\Memcached;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MemcachedTests extends TestCase
|
||||
{
|
||||
public function testLoadFail()
|
||||
{
|
||||
$client = Mockery::mock('Memcached');
|
||||
$client->shouldReceive('get')->once()->andReturn(false);
|
||||
$cache = new Memcached($client);
|
||||
$cache->load();
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testLoadSuccess()
|
||||
{
|
||||
$response = json_encode([[], ['' => true]]);
|
||||
$client = Mockery::mock('Memcached');
|
||||
$client->shouldReceive('get')->once()->andReturn($response);
|
||||
$cache = new Memcached($client);
|
||||
$cache->load();
|
||||
$this->assertTrue($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$response = json_encode([[], []]);
|
||||
$client = Mockery::mock('Memcached');
|
||||
$client->shouldReceive('set')->once()->andReturn($response);
|
||||
$cache = new Memcached($client);
|
||||
$cache->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\Memory;
|
||||
use League\Flysystem\Util;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MemoryCacheTests extends TestCase
|
||||
{
|
||||
public function testAutosave()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->setAutosave(true);
|
||||
$this->assertTrue($cache->getAutosave());
|
||||
$cache->setAutosave(false);
|
||||
$this->assertFalse($cache->getAutosave());
|
||||
}
|
||||
|
||||
public function testCacheMiss()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->storeMiss('path.txt');
|
||||
$this->assertFalse($cache->has('path.txt'));
|
||||
}
|
||||
|
||||
public function testIsComplete()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$this->assertFalse($cache->isComplete('dirname', false));
|
||||
$cache->setComplete('dirname', false);
|
||||
$this->assertFalse($cache->isComplete('dirname', true));
|
||||
$cache->setComplete('dirname', true);
|
||||
$this->assertTrue($cache->isComplete('dirname', true));
|
||||
}
|
||||
|
||||
public function testCleanContents()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$input = [[
|
||||
'path' => 'path.txt',
|
||||
'visibility' => 'public',
|
||||
'invalid' => 'thing',
|
||||
]];
|
||||
|
||||
$expected = [[
|
||||
'path' => 'path.txt',
|
||||
'visibility' => 'public',
|
||||
]];
|
||||
|
||||
$output = $cache->cleanContents($input);
|
||||
$this->assertEquals($expected, $output);
|
||||
}
|
||||
|
||||
public function testGetForStorage()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$input = [[
|
||||
'path' => 'path.txt',
|
||||
'visibility' => 'public',
|
||||
'type' => 'file',
|
||||
]];
|
||||
|
||||
$cache->storeContents('', $input, true);
|
||||
$contents = $cache->listContents('', true);
|
||||
$cached = [];
|
||||
foreach ($contents as $item) {
|
||||
$cached[$item['path']] = $item;
|
||||
}
|
||||
|
||||
$this->assertEquals(json_encode([$cached, ['' => 'recursive']]), $cache->getForStorage());
|
||||
}
|
||||
|
||||
public function testParentCompleteIsUsedDuringHas()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->setComplete('dirname', false);
|
||||
$this->assertFalse($cache->has('dirname/path.txt'));
|
||||
}
|
||||
|
||||
public function testFlush()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->setComplete('dirname', true);
|
||||
$cache->updateObject('path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'visibility' => 'public',
|
||||
]);
|
||||
$cache->flush();
|
||||
$this->assertFalse($cache->isComplete('dirname', true));
|
||||
$this->assertNull($cache->has('path.txt'));
|
||||
}
|
||||
|
||||
public function testSetFromStorage()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$json = [[
|
||||
'path.txt' => ['path' => 'path.txt', 'type' => 'file'],
|
||||
], ['dirname' => 'recursive']];
|
||||
$jsonString = json_encode($json);
|
||||
$cache->setFromStorage($jsonString);
|
||||
$this->assertTrue($cache->has('path.txt'));
|
||||
$this->assertTrue($cache->isComplete('dirname', true));
|
||||
}
|
||||
|
||||
public function testGetMetadataFail()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$this->assertFalse($cache->getMetadata('path.txt'));
|
||||
}
|
||||
|
||||
public function metaGetterProvider()
|
||||
{
|
||||
return [
|
||||
['getTimestamp', 'timestamp', 12344],
|
||||
['getMimetype', 'mimetype', 'text/plain'],
|
||||
['getSize', 'size', 12],
|
||||
['getVisibility', 'visibility', 'private'],
|
||||
['read', 'contents', '__contents__'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider metaGetterProvider
|
||||
*
|
||||
* @param $method
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function testMetaGetters($method, $key, $value)
|
||||
{
|
||||
$cache = new Memory();
|
||||
$this->assertFalse($cache->{$method}('path.txt'));
|
||||
$cache->updateObject('path.txt', $object = [
|
||||
'path' => 'path.txt',
|
||||
'type' => 'file',
|
||||
$key => $value,
|
||||
] + Util::pathinfo('path.txt'), true);
|
||||
$this->assertEquals($object, $cache->{$method}('path.txt'));
|
||||
$this->assertEquals($object, $cache->getMetadata('path.txt'));
|
||||
}
|
||||
|
||||
public function testGetDerivedMimetype()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->updateObject('path.txt', [
|
||||
'contents' => 'something',
|
||||
]);
|
||||
$response = $cache->getMimetype('path.txt');
|
||||
$this->assertEquals('text/plain', $response['mimetype']);
|
||||
}
|
||||
|
||||
public function testCopyFail()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->copy('one', 'two');
|
||||
$this->assertNull($cache->has('two'));
|
||||
$this->assertNull($cache->load());
|
||||
}
|
||||
|
||||
public function testStoreContents()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->storeContents('dirname', [
|
||||
['path' => 'dirname', 'type' => 'dir'],
|
||||
['path' => 'dirname/nested', 'type' => 'dir'],
|
||||
['path' => 'dirname/nested/deep', 'type' => 'dir'],
|
||||
['path' => 'other/nested/deep', 'type' => 'dir'],
|
||||
], true);
|
||||
|
||||
$this->isTrue($cache->isComplete('other/nested', true));
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->updateObject('path.txt', ['type' => 'file']);
|
||||
$this->assertTrue($cache->has('path.txt'));
|
||||
$cache->delete('path.txt');
|
||||
$this->assertFalse($cache->has('path.txt'));
|
||||
}
|
||||
|
||||
public function testDeleteDir()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->storeContents('dirname', [
|
||||
['path' => 'dirname/path.txt', 'type' => 'file'],
|
||||
]);
|
||||
$this->assertTrue($cache->isComplete('dirname', false));
|
||||
$this->assertTrue($cache->has('dirname/path.txt'));
|
||||
$cache->deleteDir('dirname');
|
||||
$this->assertFalse($cache->isComplete('dirname', false));
|
||||
$this->assertNull($cache->has('dirname/path.txt'));
|
||||
}
|
||||
|
||||
public function testReadStream()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$this->assertFalse($cache->readStream('path.txt'));
|
||||
}
|
||||
|
||||
public function testRename()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->updateObject('path.txt', ['type' => 'file']);
|
||||
$cache->rename('path.txt', 'newpath.txt');
|
||||
$this->assertTrue($cache->has('newpath.txt'));
|
||||
}
|
||||
|
||||
public function testCopy()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->updateObject('path.txt', ['type' => 'file']);
|
||||
$cache->copy('path.txt', 'newpath.txt');
|
||||
$this->assertTrue($cache->has('newpath.txt'));
|
||||
}
|
||||
|
||||
public function testComplextListContents()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->storeContents('', [
|
||||
['path' => 'dirname', 'type' => 'dir'],
|
||||
['path' => 'dirname/file.txt', 'type' => 'file'],
|
||||
['path' => 'other', 'type' => 'dir'],
|
||||
['path' => 'other/file.txt', 'type' => 'file'],
|
||||
['path' => 'other/nested/file.txt', 'type' => 'file'],
|
||||
]);
|
||||
|
||||
$this->assertCount(3, $cache->listContents('other', true));
|
||||
}
|
||||
|
||||
public function testComplextListContentsWithDeletedFile()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->storeContents('', [
|
||||
['path' => 'dirname', 'type' => 'dir'],
|
||||
['path' => 'dirname/file.txt', 'type' => 'file'],
|
||||
['path' => 'other', 'type' => 'dir'],
|
||||
['path' => 'other/file.txt', 'type' => 'file'],
|
||||
['path' => 'other/another_file.txt', 'type' => 'file'],
|
||||
]);
|
||||
|
||||
$cache->delete('other/another_file.txt');
|
||||
$this->assertCount(4, $cache->listContents('', true));
|
||||
}
|
||||
|
||||
public function testCacheMissIfContentsIsFalse()
|
||||
{
|
||||
$cache = new Memory();
|
||||
$cache->updateObject('path.txt', [
|
||||
'path' => 'path.txt',
|
||||
'contents' => false,
|
||||
], true);
|
||||
|
||||
$this->assertFalse($cache->read('path.txt'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\Noop;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class NoopCacheTests extends TestCase
|
||||
{
|
||||
public function testNoop()
|
||||
{
|
||||
$cache = new Noop();
|
||||
$this->assertEquals($cache, $cache->storeMiss('file.txt'));
|
||||
$this->assertNull($cache->setComplete('', false));
|
||||
$this->assertNull($cache->load());
|
||||
$this->assertNull($cache->flush());
|
||||
$this->assertNull($cache->has('path.txt'));
|
||||
$this->assertNull($cache->autosave());
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
$this->assertFalse($cache->read('something'));
|
||||
$this->assertFalse($cache->readStream('something'));
|
||||
$this->assertFalse($cache->getMetadata('something'));
|
||||
$this->assertFalse($cache->getMimetype('something'));
|
||||
$this->assertFalse($cache->getSize('something'));
|
||||
$this->assertFalse($cache->getTimestamp('something'));
|
||||
$this->assertFalse($cache->getVisibility('something'));
|
||||
$this->assertEmpty($cache->listContents('', false));
|
||||
$this->assertFalse($cache->rename('', ''));
|
||||
$this->assertFalse($cache->copy('', ''));
|
||||
$this->assertNull($cache->save());
|
||||
$object = ['path' => 'path.ext'];
|
||||
$this->assertEquals($object, $cache->updateObject('path.txt', $object));
|
||||
$this->assertEquals([['path' => 'some/file.txt']], $cache->storeContents('unknwon', [
|
||||
['path' => 'some/file.txt'],
|
||||
], false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\PhpRedis;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PhpRedisTests extends TestCase
|
||||
{
|
||||
public function testLoadFail()
|
||||
{
|
||||
$client = Mockery::mock('Redis');
|
||||
$client->shouldReceive('get')->with('flysystem')->once()->andReturn(false);
|
||||
$cache = new PhpRedis($client);
|
||||
$cache->load();
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testLoadSuccess()
|
||||
{
|
||||
$response = json_encode([[], ['' => true]]);
|
||||
$client = Mockery::mock('Redis');
|
||||
$client->shouldReceive('get')->with('flysystem')->once()->andReturn($response);
|
||||
$cache = new PhpRedis($client);
|
||||
$cache->load();
|
||||
$this->assertTrue($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$data = json_encode([[], []]);
|
||||
$client = Mockery::mock('Redis');
|
||||
$client->shouldReceive('set')->with('flysystem', $data)->once();
|
||||
$cache = new PhpRedis($client);
|
||||
$cache->save();
|
||||
}
|
||||
|
||||
public function testSaveWithExpire()
|
||||
{
|
||||
$data = json_encode([[], []]);
|
||||
$client = Mockery::mock('Redis');
|
||||
$client->shouldReceive('set')->with('flysystem', $data)->once();
|
||||
$client->shouldReceive('expire')->with('flysystem', 20)->once();
|
||||
$cache = new PhpRedis($client, 'flysystem', 20);
|
||||
$cache->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\Predis;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PredisTests extends TestCase
|
||||
{
|
||||
public function testLoadFail()
|
||||
{
|
||||
$client = Mockery::mock('Predis\Client');
|
||||
$command = Mockery::mock('Predis\Command\CommandInterface');
|
||||
$client->shouldReceive('createCommand')->with('get', ['flysystem'])->once()->andReturn($command);
|
||||
$client->shouldReceive('executeCommand')->with($command)->andReturn(null);
|
||||
$cache = new Predis($client);
|
||||
$cache->load();
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testLoadSuccess()
|
||||
{
|
||||
$response = json_encode([[], ['' => true]]);
|
||||
$client = Mockery::mock('Predis\Client');
|
||||
$command = Mockery::mock('Predis\Command\CommandInterface');
|
||||
$client->shouldReceive('createCommand')->with('get', ['flysystem'])->once()->andReturn($command);
|
||||
$client->shouldReceive('executeCommand')->with($command)->andReturn($response);
|
||||
$cache = new Predis($client);
|
||||
$cache->load();
|
||||
$this->assertTrue($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$data = json_encode([[], []]);
|
||||
$client = Mockery::mock('Predis\Client');
|
||||
$command = Mockery::mock('Predis\Command\CommandInterface');
|
||||
$client->shouldReceive('createCommand')->with('set', ['flysystem', $data])->once()->andReturn($command);
|
||||
$client->shouldReceive('executeCommand')->with($command)->once();
|
||||
$cache = new Predis($client);
|
||||
$cache->save();
|
||||
}
|
||||
|
||||
public function testSaveWithExpire()
|
||||
{
|
||||
$data = json_encode([[], []]);
|
||||
$client = Mockery::mock('Predis\Client');
|
||||
$command = Mockery::mock('Predis\Command\CommandInterface');
|
||||
$client->shouldReceive('createCommand')->with('set', ['flysystem', $data])->once()->andReturn($command);
|
||||
$client->shouldReceive('executeCommand')->with($command)->once();
|
||||
$expireCommand = Mockery::mock('Predis\Command\CommandInterface');
|
||||
$client->shouldReceive('createCommand')->with('expire', ['flysystem', 20])->once()->andReturn($expireCommand);
|
||||
$client->shouldReceive('executeCommand')->with($expireCommand)->once();
|
||||
$cache = new Predis($client, 'flysystem', 20);
|
||||
$cache->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\Psr6Cache;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class Psr6CacheTests extends TestCase
|
||||
{
|
||||
public function testLoadFail()
|
||||
{
|
||||
$pool = Mockery::mock('Psr\Cache\CacheItemPoolInterface');
|
||||
$item = Mockery::mock('Psr\Cache\CacheItemInterface');
|
||||
$item->shouldReceive('isHit')->once()->andReturn(false);
|
||||
$pool->shouldReceive('getItem')->once()->andReturn($item);
|
||||
$cache = new Psr6Cache($pool);
|
||||
$cache->load();
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testLoadSuccess()
|
||||
{
|
||||
$response = json_encode([[], ['' => true]]);
|
||||
$pool = Mockery::mock('Psr\Cache\CacheItemPoolInterface');
|
||||
$item = Mockery::mock('Psr\Cache\CacheItemInterface');
|
||||
$item->shouldReceive('get')->once()->andReturn($response);
|
||||
$item->shouldReceive('isHit')->once()->andReturn(true);
|
||||
$pool->shouldReceive('getItem')->once()->andReturn($item);
|
||||
$cache = new Psr6Cache($pool);
|
||||
$cache->load();
|
||||
$this->assertTrue($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$response = json_encode([[], []]);
|
||||
$ttl = 4711;
|
||||
$pool = Mockery::mock('Psr\Cache\CacheItemPoolInterface');
|
||||
$item = Mockery::mock('Psr\Cache\CacheItemInterface');
|
||||
$item->shouldReceive('expiresAfter')->once()->with($ttl);
|
||||
$item->shouldReceive('set')->once()->andReturn($response);
|
||||
$pool->shouldReceive('getItem')->once()->andReturn($item);
|
||||
$pool->shouldReceive('save')->once()->with($item);
|
||||
$cache = new Psr6Cache($pool, 'foo', $ttl);
|
||||
$cache->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use League\Flysystem\Cached\Storage\Stash;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class StashTests extends TestCase
|
||||
{
|
||||
public function testLoadFail()
|
||||
{
|
||||
$pool = Mockery::mock('Stash\Pool');
|
||||
$item = Mockery::mock('Stash\Item');
|
||||
$item->shouldReceive('get')->once()->andReturn(null);
|
||||
$item->shouldReceive('isMiss')->once()->andReturn(true);
|
||||
$pool->shouldReceive('getItem')->once()->andReturn($item);
|
||||
$cache = new Stash($pool);
|
||||
$cache->load();
|
||||
$this->assertFalse($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testLoadSuccess()
|
||||
{
|
||||
$response = json_encode([[], ['' => true]]);
|
||||
$pool = Mockery::mock('Stash\Pool');
|
||||
$item = Mockery::mock('Stash\Item');
|
||||
$item->shouldReceive('get')->once()->andReturn($response);
|
||||
$item->shouldReceive('isMiss')->once()->andReturn(false);
|
||||
$pool->shouldReceive('getItem')->once()->andReturn($item);
|
||||
$cache = new Stash($pool);
|
||||
$cache->load();
|
||||
$this->assertTrue($cache->isComplete('', false));
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$response = json_encode([[], []]);
|
||||
$pool = Mockery::mock('Stash\Pool');
|
||||
$item = Mockery::mock('Stash\Item');
|
||||
$item->shouldReceive('set')->once()->andReturn($response);
|
||||
$pool->shouldReceive('getItem')->once()->andReturn($item);
|
||||
$cache = new Stash($pool);
|
||||
$cache->save();
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// This file is automatically generated at:2024-04-03 22:50:21
|
||||
// This file is automatically generated at:2024-04-05 12:46:10
|
||||
declare (strict_types = 1);
|
||||
return array (
|
||||
0 => 'think\\app\\Service',
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
composer.lock
|
||||
/.idea
|
||||
/vendor
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
# think-filesystem for ThinkPHP6.1
|
||||
|
||||
## 安装
|
||||
|
||||
> composer require topthink/think-filesystem
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "topthink/think-filesystem",
|
||||
"description": "The ThinkPHP6.1 Filesystem Package",
|
||||
"type": "library",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"think\\tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"topthink/framework": "^6.1",
|
||||
"league/flysystem": "^1.1.4",
|
||||
"league/flysystem-cached-adapter": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"mockery/mockery": "^1.2",
|
||||
"phpunit/phpunit": "^8.0"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
verbose="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="ThinkPHP Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./src/think</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use think\filesystem\Driver;
|
||||
use think\filesystem\driver\Local;
|
||||
use think\helper\Arr;
|
||||
|
||||
/**
|
||||
* Class Filesystem
|
||||
* @package think
|
||||
* @mixin Driver
|
||||
* @mixin Local
|
||||
*/
|
||||
class Filesystem extends Manager
|
||||
{
|
||||
protected $namespace = '\\think\\filesystem\\driver\\';
|
||||
|
||||
/**
|
||||
* @param null|string $name
|
||||
* @return Driver
|
||||
*/
|
||||
public function disk(string $name = null): Driver
|
||||
{
|
||||
return $this->driver($name);
|
||||
}
|
||||
|
||||
protected function resolveType(string $name)
|
||||
{
|
||||
return $this->getDiskConfig($name, 'type', 'local');
|
||||
}
|
||||
|
||||
protected function resolveConfig(string $name)
|
||||
{
|
||||
return $this->getDiskConfig($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存配置
|
||||
* @access public
|
||||
* @param null|string $name 名称
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $name = null, $default = null)
|
||||
{
|
||||
if (!is_null($name)) {
|
||||
return $this->app->config->get('filesystem.' . $name, $default);
|
||||
}
|
||||
|
||||
return $this->app->config->get('filesystem');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取磁盘配置
|
||||
* @param string $disk
|
||||
* @param null $name
|
||||
* @param null $default
|
||||
* @return array
|
||||
*/
|
||||
public function getDiskConfig($disk, $name = null, $default = null)
|
||||
{
|
||||
if ($config = $this->getConfig("disks.{$disk}")) {
|
||||
return Arr::get($config, $name, $default);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Disk [$disk] not found.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认驱动
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDefaultDriver()
|
||||
{
|
||||
return $this->getConfig('default');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\facade;
|
||||
|
||||
use think\Facade;
|
||||
use think\filesystem\Driver;
|
||||
|
||||
/**
|
||||
* Class Filesystem
|
||||
* @package think\facade
|
||||
* @mixin \think\Filesystem
|
||||
* @method static Driver disk(string $name = null) , null|string
|
||||
* @method static mixed getConfig(null|string $name = null, mixed $default = null) 获取缓存配置
|
||||
* @method static array getDiskConfig(string $disk, null $name = null, null $default = null) 获取磁盘配置
|
||||
* @method static string|null getDefaultDriver() 默认驱动
|
||||
*/
|
||||
class Filesystem extends Facade
|
||||
{
|
||||
protected static function getFacadeClass()
|
||||
{
|
||||
return \think\Filesystem::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\filesystem;
|
||||
|
||||
use League\Flysystem\Cached\Storage\AbstractCache;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
class CacheStore extends AbstractCache
|
||||
{
|
||||
protected $store;
|
||||
|
||||
protected $key;
|
||||
|
||||
protected $expire;
|
||||
|
||||
public function __construct(CacheInterface $store, $key = 'flysystem', $expire = null)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->store = $store;
|
||||
$this->expire = $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the cache.
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$contents = $this->getForStorage();
|
||||
|
||||
$this->store->set($this->key, $contents, $this->expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the cache.
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$contents = $this->store->get($this->key);
|
||||
|
||||
if (!is_null($contents)) {
|
||||
$this->setFromStorage($contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\filesystem;
|
||||
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Adapter\AbstractAdapter;
|
||||
use League\Flysystem\Cached\CachedAdapter;
|
||||
use League\Flysystem\Cached\Storage\Memory as MemoryStore;
|
||||
use League\Flysystem\Filesystem;
|
||||
use RuntimeException;
|
||||
use think\Cache;
|
||||
use think\File;
|
||||
|
||||
/**
|
||||
* Class Driver
|
||||
* @package think\filesystem
|
||||
* @mixin Filesystem
|
||||
*/
|
||||
abstract class Driver
|
||||
{
|
||||
|
||||
/** @var Cache */
|
||||
protected $cache;
|
||||
|
||||
/** @var Filesystem */
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
public function __construct(Cache $cache, array $config)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->config = array_merge($this->config, $config);
|
||||
|
||||
$adapter = $this->createAdapter();
|
||||
$this->filesystem = $this->createFilesystem($adapter);
|
||||
}
|
||||
|
||||
protected function createCacheStore($config)
|
||||
{
|
||||
if (true === $config) {
|
||||
return new MemoryStore;
|
||||
}
|
||||
|
||||
return new CacheStore(
|
||||
$this->cache->store($config['store']),
|
||||
$config['prefix'] ?? 'flysystem',
|
||||
$config['expire'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
abstract protected function createAdapter(): AdapterInterface;
|
||||
|
||||
protected function createFilesystem(AdapterInterface $adapter): Filesystem
|
||||
{
|
||||
if (!empty($this->config['cache'])) {
|
||||
$adapter = new CachedAdapter($adapter, $this->createCacheStore($this->config['cache']));
|
||||
}
|
||||
|
||||
$config = array_intersect_key($this->config, array_flip(['visibility', 'disable_asserts', 'url']));
|
||||
|
||||
return new Filesystem($adapter, count($config) > 0 ? $config : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件完整路径
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $path): string
|
||||
{
|
||||
$adapter = $this->filesystem->getAdapter();
|
||||
|
||||
if ($adapter instanceof AbstractAdapter) {
|
||||
return $adapter->applyPathPrefix($path);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
protected function concatPathToUrl($url, $path)
|
||||
{
|
||||
return rtrim($url, '/') . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
public function url(string $path): string
|
||||
{
|
||||
throw new RuntimeException('This driver does not support retrieving URLs.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件
|
||||
* @param string $path 路径
|
||||
* @param File $file 文件
|
||||
* @param null|string|\Closure $rule 文件名规则
|
||||
* @param array $options 参数
|
||||
* @return bool|string
|
||||
*/
|
||||
public function putFile(string $path, File $file, $rule = null, array $options = [])
|
||||
{
|
||||
return $this->putFileAs($path, $file, $file->hashName($rule), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定文件名保存文件
|
||||
* @param string $path 路径
|
||||
* @param File $file 文件
|
||||
* @param string $name 文件名
|
||||
* @param array $options 参数
|
||||
* @return bool|string
|
||||
*/
|
||||
public function putFileAs(string $path, File $file, string $name, array $options = [])
|
||||
{
|
||||
$stream = fopen($file->getRealPath(), 'r');
|
||||
$path = trim($path . '/' . $name, '/');
|
||||
|
||||
$result = $this->putStream($path, $stream, $options);
|
||||
|
||||
if (is_resource($stream)) {
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
return $result ? $path : false;
|
||||
}
|
||||
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
return $this->filesystem->$method(...$parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2021 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace think\filesystem\driver;
|
||||
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use League\Flysystem\Adapter\Local as LocalAdapter;
|
||||
use think\filesystem\Driver;
|
||||
|
||||
class Local extends Driver
|
||||
{
|
||||
/**
|
||||
* 配置参数
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [
|
||||
'root' => '',
|
||||
];
|
||||
|
||||
protected function createAdapter(): AdapterInterface
|
||||
{
|
||||
$permissions = $this->config['permissions'] ?? [];
|
||||
|
||||
$links = ($this->config['links'] ?? null) === 'skip'
|
||||
? LocalAdapter::SKIP_LINKS
|
||||
: LocalAdapter::DISALLOW_LINKS;
|
||||
|
||||
return new LocalAdapter(
|
||||
$this->config['root'],
|
||||
LOCK_EX,
|
||||
$links,
|
||||
$permissions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件访问地址
|
||||
* @param string $path 文件路径
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $path): string
|
||||
{
|
||||
$path = str_replace('\\', '/', $path);
|
||||
|
||||
if (isset($this->config['url'])) {
|
||||
return $this->concatPathToUrl($this->config['url'], $path);
|
||||
}
|
||||
return parent::url($path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace think\tests;
|
||||
|
||||
use League\Flysystem\Adapter\NullAdapter;
|
||||
use League\Flysystem\AdapterInterface;
|
||||
use Mockery as m;
|
||||
use Mockery\MockInterface;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStreamDirectory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use think\App;
|
||||
use think\Cache;
|
||||
use think\cache\driver\File;
|
||||
use think\Config;
|
||||
use think\Container;
|
||||
use think\Filesystem;
|
||||
use think\filesystem\Driver;
|
||||
use think\filesystem\driver\Local;
|
||||
|
||||
class FilesystemTest extends TestCase
|
||||
{
|
||||
/** @var App|MockInterface */
|
||||
protected $app;
|
||||
|
||||
/** @var Filesystem */
|
||||
protected $filesystem;
|
||||
|
||||
/** @var Config|MockInterface */
|
||||
protected $config;
|
||||
|
||||
/** @var vfsStreamDirectory */
|
||||
protected $root;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->app = m::mock(App::class)->makePartial();
|
||||
Container::setInstance($this->app);
|
||||
$this->app->shouldReceive('make')->with(App::class)->andReturn($this->app);
|
||||
$this->config = m::mock(Config::class);
|
||||
$this->config->shouldReceive('get')->with('filesystem.default', null)->andReturn('local');
|
||||
$this->app->shouldReceive('get')->with('config')->andReturn($this->config);
|
||||
$this->filesystem = new Filesystem($this->app);
|
||||
|
||||
$this->root = vfsStream::setup('rootDir');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
m::close();
|
||||
}
|
||||
|
||||
public function testDisk()
|
||||
{
|
||||
$this->config->shouldReceive('get')->with('filesystem.disks.local', null)->andReturn([
|
||||
'type' => 'local',
|
||||
'root' => $this->root->url(),
|
||||
]);
|
||||
|
||||
$this->config->shouldReceive('get')->with('filesystem.disks.foo', null)->andReturn([
|
||||
'type' => 'local',
|
||||
'root' => $this->root->url(),
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(Local::class, $this->filesystem->disk());
|
||||
|
||||
$this->assertInstanceOf(Local::class, $this->filesystem->disk('foo'));
|
||||
}
|
||||
|
||||
public function testCache()
|
||||
{
|
||||
$this->config->shouldReceive('get')->with('filesystem.disks.local', null)->andReturn([
|
||||
'type' => 'local',
|
||||
'root' => $this->root->url(),
|
||||
'cache' => true,
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(Local::class, $this->filesystem->disk());
|
||||
|
||||
$this->config->shouldReceive('get')->with('filesystem.disks.cache', null)->andReturn([
|
||||
'type' => NullDriver::class,
|
||||
'root' => $this->root->url(),
|
||||
'cache' => [
|
||||
'store' => 'flysystem',
|
||||
],
|
||||
]);
|
||||
|
||||
$cache = m::mock(Cache::class);
|
||||
|
||||
$cacheDriver = m::mock(File::class);
|
||||
|
||||
$cache->shouldReceive('store')->once()->with('flysystem')->andReturn($cacheDriver);
|
||||
|
||||
$this->app->shouldReceive('make')->with(Cache::class)->andReturn($cache);
|
||||
|
||||
$cacheDriver->shouldReceive('get')->with('flysystem')->once()->andReturn(null);
|
||||
|
||||
$cacheDriver->shouldReceive('set')->withAnyArgs();
|
||||
|
||||
$this->filesystem->disk('cache')->put('test.txt', 'aa');
|
||||
}
|
||||
|
||||
public function testPutFile()
|
||||
{
|
||||
$root = vfsStream::setup('rootDir', null, [
|
||||
'foo.jpg' => 'hello',
|
||||
]);
|
||||
|
||||
$this->config->shouldReceive('get')->with('filesystem.disks.local', null)->andReturn([
|
||||
'type' => NullDriver::class,
|
||||
'root' => $root->url(),
|
||||
'cache' => true,
|
||||
]);
|
||||
|
||||
$file = m::mock(\think\File::class);
|
||||
|
||||
$file->shouldReceive('hashName')->with(null)->once()->andReturn('foo.jpg');
|
||||
|
||||
$file->shouldReceive('getRealPath')->once()->andReturn($root->getChild('foo.jpg')->url());
|
||||
|
||||
$this->filesystem->putFile('test', $file);
|
||||
}
|
||||
}
|
||||
|
||||
class NullDriver extends Driver
|
||||
{
|
||||
protected function createAdapter(): AdapterInterface
|
||||
{
|
||||
return new NullAdapter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
|
||||
Reference in New Issue
Block a user