Compare commits

...

2 Commits

Author SHA1 Message Date
weiz 84b567688d Merge pull request '新增上传图片接口' (#1) from dev into master
Reviewed-on: #1
2023-10-14 14:49:47 +08:00
unknown c67d0b0efd 新增上传图片接口 2023-10-14 14:48:48 +08:00
77 changed files with 6390 additions and 11 deletions

View File

@ -3,11 +3,12 @@
namespace app\adminapi\controller;
use think\facade\Db;
use think\facade\Filesystem;
use think\response\Json;
class SuYuanController extends BaseAdminController
{
public array $notNeedLogin = ['areaLists','areaDetail','monitorDetail'];
public array $notNeedLogin = ['areaLists','areaDetail','monitorDetail','imageUpload'];
//添加种植基地
public function addArea(): Json
@ -112,9 +113,23 @@ class SuYuanController extends BaseAdminController
if(empty($params['id']) || empty($params['flag'])){
return $this->fail('参数错误');
}
$data = Db::name('environmental_data')->where('id',$params['id'])->where('flag',$params['flag'])->findOrEmpty();
$data = Db::name('environmental_data')->where('id',$params['id'])->where('flag',$params['flag'])->order('id desc')->findOrEmpty();
$content = Db::name('production_base')->where('id',$data['production_base_id'])->field('content')->findOrEmpty();
$data['content'] = $content['content'];
return $this->success('请求成功',$data);
}
//上传图片
public function imageUpload(): Json
{
if(!$this->request->isPost()) {
return $this->fail('请求方式错误');
}
$file = request()->file('file');
if(empty($file)){
return $this->fail('上传文件不存在');
}
$save_name = Filesystem::disk('public')->putFile( '', $file);
return $this->success('上传成功',['image'=>$this->request->domain().$save_name]);
}
}

View File

@ -33,7 +33,8 @@
"alibabacloud/client": "^1.5",
"rmccue/requests": "^2.0",
"w7corp/easywechat": "^6.8",
"tencentcloud/sms": "^3.0"
"tencentcloud/sms": "^3.0",
"topthink/think-filesystem": "^2.0"
},
"require-dev": {
"symfony/var-dumper": "^4.2",

190
composer.lock generated
View File

@ -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": "bc1e827f292ccdd477dd5701ef65d783",
"content-hash": "eb220bf04fd5f72fdb74230c01405bb9",
"packages": [
{
"name": "adbario/php-dot-notation",
@ -1010,6 +1010,148 @@
],
"time": "2021-10-07T12:57:01+00:00"
},
{
"name": "league/flysystem",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8aaffb653c5777781b0f7f69a5d937baf7ab6cdb",
"reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb",
"shasum": ""
},
"require": {
"ext-json": "*",
"league/mime-type-detection": "^1.0.0",
"php": "^7.2 || ^8.0"
},
"conflict": {
"guzzlehttp/ringphp": "<1.1.1"
},
"require-dev": {
"async-aws/s3": "^1.5",
"async-aws/simple-s3": "^1.0",
"aws/aws-sdk-php": "^3.132.4",
"composer/semver": "^3.0",
"ext-fileinfo": "*",
"ext-ftp": "*",
"friendsofphp/php-cs-fixer": "^3.2",
"google/cloud-storage": "^1.23",
"phpseclib/phpseclib": "^2.0",
"phpstan/phpstan": "^0.12.26",
"phpunit/phpunit": "^8.5 || ^9.4",
"sabre/dav": "^4.1"
},
"type": "library",
"autoload": {
"psr-4": {
"League\\Flysystem\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"description": "File storage abstraction for PHP",
"keywords": [
"WebDAV",
"aws",
"cloud",
"file",
"files",
"filesystem",
"filesystems",
"ftp",
"s3",
"sftp",
"storage"
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/2.5.0"
},
"funding": [
{
"url": "https://ecologi.com/frankdejonge",
"type": "custom"
},
{
"url": "https://github.com/frankdejonge",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/league/flysystem",
"type": "tidelift"
}
],
"time": "2022-09-17T21:02:32+00:00"
},
{
"name": "league/mime-type-detection",
"version": "1.13.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/mime-type-detection.git",
"reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/a6dfb1194a2946fcdc1f38219445234f65b35c96",
"reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"phpstan/phpstan": "^0.12.68",
"phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
},
"type": "library",
"autoload": {
"psr-4": {
"League\\MimeTypeDetection\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"description": "Mime-type detection for Flysystem",
"support": {
"issues": "https://github.com/thephpleague/mime-type-detection/issues",
"source": "https://github.com/thephpleague/mime-type-detection/tree/1.13.0"
},
"funding": [
{
"url": "https://github.com/frankdejonge",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/league/flysystem",
"type": "tidelift"
}
],
"time": "2023-08-05T12:09:49+00:00"
},
{
"name": "maennchen/zipstream-php",
"version": "v2.4.0",
@ -4324,6 +4466,52 @@
},
"time": "2023-02-08T02:24:01+00:00"
},
{
"name": "topthink/think-filesystem",
"version": "v2.0.2",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-filesystem.git",
"reference": "c08503232fcae0c3c7fefae5e6b5c841ffe09f2f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-filesystem/zipball/c08503232fcae0c3c7fefae5e6b5c841ffe09f2f",
"reference": "c08503232fcae0c3c7fefae5e6b5c841ffe09f2f",
"shasum": ""
},
"require": {
"league/flysystem": "^2.0",
"topthink/framework": "^6.1|^8.0"
},
"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/v2.0.2"
},
"time": "2023-02-08T01:23:42+00:00"
},
{
"name": "topthink/think-helper",
"version": "v3.1.6",

View File

@ -13,7 +13,7 @@ return [
// 磁盘类型
'type' => 'local',
// 磁盘路径
'root' => app()->getRootPath() . 'public/storage',
'root' => app()->getRootPath() . 'public/uploads',
// 磁盘路径对应的外部URL路径
'url' => '/storage',
// 可见性

View File

@ -14,9 +14,9 @@ return array(
'9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'35fab96057f1bf5e7aba31a8a6d5fdde' => $vendorDir . '/topthink/think-orm/stubs/load_stubs.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'd767e4fc2dc52fe66584ab8c6684783e' => $vendorDir . '/adbario/php-dot-notation/src/helpers.php',

View File

@ -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'),
'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-filesystem/src'),
'clagiordano\\weblibs\\configmanager\\' => array($vendorDir . '/clagiordano/weblibs-configmanager/src'),
'app\\' => array($baseDir . '/app'),
'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'),
@ -52,6 +52,8 @@ return array(
'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'),
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
'JmesPath\\' => array($vendorDir . '/mtdowling/jmespath.php/src'),
'GuzzleHttp\\UriTemplate\\' => array($vendorDir . '/guzzlehttp/uri-template/src'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),

View File

@ -15,9 +15,9 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
'9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'35fab96057f1bf5e7aba31a8a6d5fdde' => __DIR__ . '/..' . '/topthink/think-orm/stubs/load_stubs.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'd767e4fc2dc52fe66584ab8c6684783e' => __DIR__ . '/..' . '/adbario/php-dot-notation/src/helpers.php',
@ -117,6 +117,11 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
'Monolog\\' => 8,
'Matrix\\' => 7,
),
'L' =>
array (
'League\\MimeTypeDetection\\' => 25,
'League\\Flysystem\\' => 17,
),
'J' =>
array (
'JmesPath\\' => 9,
@ -165,6 +170,7 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
1 => __DIR__ . '/..' . '/topthink/think-helper/src',
2 => __DIR__ . '/..' . '/topthink/think-orm/src',
3 => __DIR__ . '/..' . '/topthink/think-template/src',
4 => __DIR__ . '/..' . '/topthink/think-filesystem/src',
),
'clagiordano\\weblibs\\configmanager\\' =>
array (
@ -336,6 +342,14 @@ class ComposerStaticInitd2a74ba94e266cc4f45a64c54a292d7e
array (
0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src',
),
'League\\MimeTypeDetection\\' =>
array (
0 => __DIR__ . '/..' . '/league/mime-type-detection/src',
),
'League\\Flysystem\\' =>
array (
0 => __DIR__ . '/..' . '/league/flysystem/src',
),
'JmesPath\\' =>
array (
0 => __DIR__ . '/..' . '/mtdowling/jmespath.php/src',

View File

@ -1040,6 +1040,154 @@
],
"install-path": "../guzzlehttp/uri-template"
},
{
"name": "league/flysystem",
"version": "2.5.0",
"version_normalized": "2.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/8aaffb653c5777781b0f7f69a5d937baf7ab6cdb",
"reference": "8aaffb653c5777781b0f7f69a5d937baf7ab6cdb",
"shasum": ""
},
"require": {
"ext-json": "*",
"league/mime-type-detection": "^1.0.0",
"php": "^7.2 || ^8.0"
},
"conflict": {
"guzzlehttp/ringphp": "<1.1.1"
},
"require-dev": {
"async-aws/s3": "^1.5",
"async-aws/simple-s3": "^1.0",
"aws/aws-sdk-php": "^3.132.4",
"composer/semver": "^3.0",
"ext-fileinfo": "*",
"ext-ftp": "*",
"friendsofphp/php-cs-fixer": "^3.2",
"google/cloud-storage": "^1.23",
"phpseclib/phpseclib": "^2.0",
"phpstan/phpstan": "^0.12.26",
"phpunit/phpunit": "^8.5 || ^9.4",
"sabre/dav": "^4.1"
},
"time": "2022-09-17T21:02:32+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"League\\Flysystem\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"description": "File storage abstraction for PHP",
"keywords": [
"WebDAV",
"aws",
"cloud",
"file",
"files",
"filesystem",
"filesystems",
"ftp",
"s3",
"sftp",
"storage"
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/2.5.0"
},
"funding": [
{
"url": "https://ecologi.com/frankdejonge",
"type": "custom"
},
{
"url": "https://github.com/frankdejonge",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/league/flysystem",
"type": "tidelift"
}
],
"install-path": "../league/flysystem"
},
{
"name": "league/mime-type-detection",
"version": "1.13.0",
"version_normalized": "1.13.0.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/mime-type-detection.git",
"reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/a6dfb1194a2946fcdc1f38219445234f65b35c96",
"reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"phpstan/phpstan": "^0.12.68",
"phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0"
},
"time": "2023-08-05T12:09:49+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"League\\MimeTypeDetection\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"description": "Mime-type detection for Flysystem",
"support": {
"issues": "https://github.com/thephpleague/mime-type-detection/issues",
"source": "https://github.com/thephpleague/mime-type-detection/tree/1.13.0"
},
"funding": [
{
"url": "https://github.com/frankdejonge",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/league/flysystem",
"type": "tidelift"
}
],
"install-path": "../league/mime-type-detection"
},
{
"name": "maennchen/zipstream-php",
"version": "v2.4.0",
@ -4584,6 +4732,55 @@
},
"install-path": "../topthink/framework"
},
{
"name": "topthink/think-filesystem",
"version": "v2.0.2",
"version_normalized": "2.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-filesystem.git",
"reference": "c08503232fcae0c3c7fefae5e6b5c841ffe09f2f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/top-think/think-filesystem/zipball/c08503232fcae0c3c7fefae5e6b5c841ffe09f2f",
"reference": "c08503232fcae0c3c7fefae5e6b5c841ffe09f2f",
"shasum": ""
},
"require": {
"league/flysystem": "^2.0",
"topthink/framework": "^6.1|^8.0"
},
"require-dev": {
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^8.0"
},
"time": "2023-02-08T01:23:42+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/v2.0.2"
},
"install-path": "../topthink/think-filesystem"
},
{
"name": "topthink/think-helper",
"version": "v3.1.6",

View File

@ -3,7 +3,7 @@
'name' => 'topthink/think',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '9f987ffc5957ff8b3f2579fa8723fb069aa6468f',
'reference' => '8c9212052304a213c45338424760aea2d904f258',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -118,6 +118,24 @@
'aliases' => array(),
'dev_requirement' => false,
),
'league/flysystem' => array(
'pretty_version' => '2.5.0',
'version' => '2.5.0.0',
'reference' => '8aaffb653c5777781b0f7f69a5d937baf7ab6cdb',
'type' => 'library',
'install_path' => __DIR__ . '/../league/flysystem',
'aliases' => array(),
'dev_requirement' => false,
),
'league/mime-type-detection' => array(
'pretty_version' => '1.13.0',
'version' => '1.13.0.0',
'reference' => 'a6dfb1194a2946fcdc1f38219445234f65b35c96',
'type' => 'library',
'install_path' => __DIR__ . '/../league/mime-type-detection',
'aliases' => array(),
'dev_requirement' => false,
),
'maennchen/zipstream-php' => array(
'pretty_version' => 'v2.4.0',
'version' => '2.4.0.0',
@ -598,12 +616,21 @@
'topthink/think' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '9f987ffc5957ff8b3f2579fa8723fb069aa6468f',
'reference' => '8c9212052304a213c45338424760aea2d904f258',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'topthink/think-filesystem' => array(
'pretty_version' => 'v2.0.2',
'version' => '2.0.2.0',
'reference' => 'c08503232fcae0c3c7fefae5e6b5c841ffe09f2f',
'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',

2
vendor/league/flysystem/INFO.md vendored Normal file
View File

@ -0,0 +1,2 @@
View the docs at: https://flysystem.thephpleague.com/v2/
Changelog at: https://github.com/thephpleague/flysystem/blob/2.x/CHANGELOG.md

19
vendor/league/flysystem/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2013-2022 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.

48
vendor/league/flysystem/composer.json vendored Normal file
View File

@ -0,0 +1,48 @@
{
"name": "league/flysystem",
"description": "File storage abstraction for PHP",
"keywords": [
"filesystem", "filesystems", "files", "storage", "aws",
"s3", "ftp", "sftp", "webdav", "file", "cloud"
],
"scripts": {
"phpstan": "vendor/bin/phpstan analyse -l 6 src"
},
"type": "library",
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"League\\Flysystem\\": "src"
}
},
"require": {
"php": "^7.2 || ^8.0",
"ext-json": "*",
"league/mime-type-detection": "^1.0.0"
},
"require-dev": {
"ext-fileinfo": "*",
"ext-ftp": "*",
"phpunit/phpunit": "^8.5 || ^9.4",
"phpstan/phpstan": "^0.12.26",
"phpseclib/phpseclib": "^2.0",
"aws/aws-sdk-php": "^3.132.4",
"composer/semver": "^3.0",
"friendsofphp/php-cs-fixer": "^3.2",
"google/cloud-storage": "^1.23",
"async-aws/s3": "^1.5",
"async-aws/simple-s3": "^1.0",
"sabre/dav": "^4.1"
},
"conflict": {
"guzzlehttp/ringphp": "<1.1.1"
},
"license": "MIT",
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
]
}

View File

@ -0,0 +1,49 @@
{
"sub-splits": [
{
"name": "ftp",
"directory": "src/Ftp",
"target": "git@github.com:thephpleague/flysystem-ftp.git"
},
{
"name": "sftp",
"directory": "src/PhpseclibV2",
"target": "git@github.com:thephpleague/flysystem-sftp.git"
},
{
"name": "sftp-v3",
"directory": "src/PhpseclibV3",
"target": "git@github.com:thephpleague/flysystem-sftp-v3.git"
},
{
"name": "memory",
"directory": "src/InMemory",
"target": "git@github.com:thephpleague/flysystem-memory.git"
},
{
"name": "ziparchive",
"directory": "src/ZipArchive",
"target": "git@github.com:thephpleague/flysystem-ziparchive.git"
},
{
"name": "aws-s3-v3",
"directory": "src/AwsS3V3",
"target": "git@github.com:thephpleague/flysystem-aws-s3-v3.git"
},
{
"name": "async-aws-s3",
"directory": "src/AsyncAwsS3",
"target": "git@github.com:thephpleague/flysystem-async-aws-s3.git"
},
{
"name": "google-cloud-storage",
"directory": "src/GoogleCloudStorage",
"target": "git@github.com:thephpleague/flysystem-google-cloud-storage.git"
},
{
"name": "adapter-test-utilities",
"directory": "src/AdapterTestUtilities",
"target": "git@github.com:thephpleague/flysystem-adapter-test-utilities.git"
}
]
}

View File

@ -0,0 +1,58 @@
---
version: "3"
services:
webdav:
image: bytemark/webdav
restart: always
ports:
- "80:80"
environment:
AUTH_TYPE: Digest
USERNAME: alice
PASSWORD: secret1234
sftp:
container_name: sftp
restart: always
image: atmoz/sftp
volumes:
- ./test_files/sftp/users.conf:/etc/sftp/users.conf
- ./test_files/sftp/ssh_host_ed25519_key:/etc/ssh/ssh_host_ed25519_key
- ./test_files/sftp/ssh_host_rsa_key:/etc/ssh/ssh_host_rsa_key
- ./test_files/sftp/id_rsa.pub:/home/bar/.ssh/keys/id_rsa.pub
ports:
- "2222:22"
ftp:
container_name: ftp
restart: always
image: delfer/alpine-ftp-server
environment:
USERS: 'foo|pass|/home/foo/upload'
ADDRESS: 'localhost'
ports:
- "2121:21"
- "21000-21010:21000-21010"
ftpd:
container_name: ftpd
restart: always
environment:
PUBLICHOST: localhost
FTP_USER_NAME: foo
FTP_USER_PASS: pass
FTP_USER_HOME: /home/foo
image: stilliard/pure-ftpd
ports:
- "2122:21"
- "30000-30009:30000-30009"
command: "/run.sh -l puredb:/etc/pure-ftpd/pureftpd.pdb -E -j -P localhost"
toxiproxy:
container_name: toxiproxy
restart: unless-stopped
image: ghcr.io/shopify/toxiproxy
command: "-host 0.0.0.0 -config /opt/toxiproxy/config.json"
volumes:
- ./test_files/toxiproxy/toxiproxy.json:/opt/toxiproxy/config.json:ro
ports:
- "8474:8474" # HTTP API
- "8222:8222" # SFTP
- "8121:8121" # FTP
- "8122:8122" # FTPD

45
vendor/league/flysystem/readme.md vendored Normal file
View File

@ -0,0 +1,45 @@
# League\Flysystem
[![Author](https://img.shields.io/badge/author-@frankdejonge-blue.svg)](https://twitter.com/frankdejonge)
[![Source Code](https://img.shields.io/badge/source-thephpleague/flysystem-blue.svg)](https://github.com/thephpleague/flysystem)
[![Latest Version](https://img.shields.io/github/tag/thephpleague/flysystem.svg)](https://github.com/thephpleague/flysystem/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/thephpleague/flysystem/blob/master/LICENSE)
[![Quality Assurance](https://github.com/thephpleague/flysystem/workflows/Quality%20Assurance/badge.svg?branch=2.x)](https://github.com/thephpleague/flysystem/actions?query=workflow%3A%22Quality+Assurance%22)
[![Total Downloads](https://img.shields.io/packagist/dt/league/flysystem.svg)](https://packagist.org/packages/league/flysystem)
![php 7.2+](https://img.shields.io/badge/php-min%207.2-red.svg)
## About Flysystem
Flysystem is a file storage library for PHP. It provides one interface to
interact with many types of filesystems. When you use Flysystem, you're
not only protected from vendor lock-in, you'll also have a consistent experience
for which ever storage is right for you.
## Getting Started
* **[New in V2](https://flysystem.thephpleague.com/v2/docs/what-is-new/)**: What it new in Flysystem V2?
* **[Architecture](https://flysystem.thephpleague.com/v2/docs/architecture/)**: Flysystem's internal architecture
* **[Flysystem API](https://flysystem.thephpleague.com/v2/docs/usage/filesystem-api/)**: How to interact with your Flysystem instance
* **[Upgrade to V2](https://flysystem.thephpleague.com/v2/docs/advanced/upgrade-to-2.0.0/)**: How to upgrade your Flysystem V1 instance to V2
### Commonly-Used Adapters
* **[AsyncAws S3](https://flysystem.thephpleague.com/v2/docs/adapter/async-aws-s3/)**
* **[AWS S3](https://flysystem.thephpleague.com/v2/docs/adapter/aws-s3-v3/)**
* **[Local](https://flysystem.thephpleague.com/v2/docs/adapter/local/)**
* **[Memory](https://flysystem.thephpleague.com/v2/docs/adapter/in-memory/)**
### Third party Adapters
* **[Gitlab](https://github.com/RoyVoetman/flysystem-gitlab-storage)**
* **[Google Drive (using regular paths)](https://github.com/masbug/flysystem-google-drive-ext)**
You can always [create an adapter](https://flysystem.thephpleague.com/v2/docs/advanced/creating-an-adapter/) yourself.
## Security
If you discover any security related issues, please email info@frankdejonge.nl instead of using the issue tracker.
## Enjoy
Oh, and if you've come down this far, you might as well follow me on [twitter](https://twitter.com/frankdejonge).

43
vendor/league/flysystem/src/Config.php vendored Normal file
View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use function array_merge;
class Config
{
public const OPTION_VISIBILITY = 'visibility';
public const OPTION_DIRECTORY_VISIBILITY = 'directory_visibility';
/**
* @var array
*/
private $options;
public function __construct(array $options = [])
{
$this->options = $options;
}
/**
* @param mixed $default
*
* @return mixed
*/
public function get(string $property, $default = null)
{
return $this->options[$property] ?? $default;
}
public function extend(array $options): Config
{
return new Config(array_merge($this->options, $options));
}
public function withDefaults(array $defaults): Config
{
return new Config($this->options + $defaults);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace League\Flysystem;
use RuntimeException;
final class CorruptedPathDetected extends RuntimeException implements FilesystemException
{
public static function forPath(string $path): CorruptedPathDetected
{
return new CorruptedPathDetected("Corrupted path detected: " . $path);
}
}

View File

@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
class DirectoryAttributes implements StorageAttributes
{
use ProxyArrayAccessToProperties;
/**
* @var string
*/
private $type = StorageAttributes::TYPE_DIRECTORY;
/**
* @var string
*/
private $path;
/**
* @var string|null
*/
private $visibility;
/**
* @var int|null
*/
private $lastModified;
/**
* @var array
*/
private $extraMetadata;
public function __construct(string $path, ?string $visibility = null, ?int $lastModified = null, array $extraMetadata = [])
{
$this->path = $path;
$this->visibility = $visibility;
$this->lastModified = $lastModified;
$this->extraMetadata = $extraMetadata;
}
public function path(): string
{
return $this->path;
}
public function type(): string
{
return StorageAttributes::TYPE_DIRECTORY;
}
public function visibility(): ?string
{
return $this->visibility;
}
public function lastModified(): ?int
{
return $this->lastModified;
}
public function extraMetadata(): array
{
return $this->extraMetadata;
}
public function isFile(): bool
{
return false;
}
public function isDir(): bool
{
return true;
}
public function withPath(string $path): StorageAttributes
{
$clone = clone $this;
$clone->path = $path;
return $clone;
}
public static function fromArray(array $attributes): StorageAttributes
{
return new DirectoryAttributes(
$attributes[StorageAttributes::ATTRIBUTE_PATH],
$attributes[StorageAttributes::ATTRIBUTE_VISIBILITY] ?? null,
$attributes[StorageAttributes::ATTRIBUTE_LAST_MODIFIED] ?? null,
$attributes[StorageAttributes::ATTRIBUTE_EXTRA_METADATA] ?? []
);
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
return [
StorageAttributes::ATTRIBUTE_TYPE => $this->type,
StorageAttributes::ATTRIBUTE_PATH => $this->path,
StorageAttributes::ATTRIBUTE_VISIBILITY => $this->visibility,
StorageAttributes::ATTRIBUTE_LAST_MODIFIED => $this->lastModified,
StorageAttributes::ATTRIBUTE_EXTRA_METADATA => $this->extraMetadata,
];
}
}

View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use ArrayIterator;
use Generator;
use IteratorAggregate;
use Traversable;
/**
* @template T
*/
class DirectoryListing implements IteratorAggregate
{
/**
* @var iterable<T>
*/
private $listing;
/**
* @param iterable<T> $listing
*/
public function __construct(iterable $listing)
{
$this->listing = $listing;
}
public function filter(callable $filter): DirectoryListing
{
$generator = (static function (iterable $listing) use ($filter): Generator {
foreach ($listing as $item) {
if ($filter($item)) {
yield $item;
}
}
})($this->listing);
return new DirectoryListing($generator);
}
public function map(callable $mapper): DirectoryListing
{
$generator = (static function (iterable $listing) use ($mapper): Generator {
foreach ($listing as $item) {
yield $mapper($item);
}
})($this->listing);
return new DirectoryListing($generator);
}
public function sortByPath(): DirectoryListing
{
$listing = $this->toArray();
usort($listing, function (StorageAttributes $a, StorageAttributes $b) {
return $a->path() <=> $b->path();
});
return new DirectoryListing($listing);
}
/**
* @return Traversable<T>
*/
public function getIterator(): Traversable
{
return $this->listing instanceof Traversable
? $this->listing
: new ArrayIterator($this->listing);
}
/**
* @return T[]
*/
public function toArray(): array
{
return $this->listing instanceof Traversable
? iterator_to_array($this->listing, false)
: (array) $this->listing;
}
}

View File

@ -0,0 +1,139 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
class FileAttributes implements StorageAttributes
{
use ProxyArrayAccessToProperties;
/**
* @var string
*/
private $type = StorageAttributes::TYPE_FILE;
/**
* @var string
*/
private $path;
/**
* @var int|null
*/
private $fileSize;
/**
* @var string|null
*/
private $visibility;
/**
* @var int|null
*/
private $lastModified;
/**
* @var string|null
*/
private $mimeType;
/**
* @var array
*/
private $extraMetadata;
public function __construct(
string $path,
?int $fileSize = null,
?string $visibility = null,
?int $lastModified = null,
?string $mimeType = null,
array $extraMetadata = []
) {
$this->path = $path;
$this->fileSize = $fileSize;
$this->visibility = $visibility;
$this->lastModified = $lastModified;
$this->mimeType = $mimeType;
$this->extraMetadata = $extraMetadata;
}
public function type(): string
{
return $this->type;
}
public function path(): string
{
return $this->path;
}
public function fileSize(): ?int
{
return $this->fileSize;
}
public function visibility(): ?string
{
return $this->visibility;
}
public function lastModified(): ?int
{
return $this->lastModified;
}
public function mimeType(): ?string
{
return $this->mimeType;
}
public function extraMetadata(): array
{
return $this->extraMetadata;
}
public function isFile(): bool
{
return true;
}
public function isDir(): bool
{
return false;
}
public function withPath(string $path): StorageAttributes
{
$clone = clone $this;
$clone->path = $path;
return $clone;
}
public static function fromArray(array $attributes): StorageAttributes
{
return new FileAttributes(
$attributes[StorageAttributes::ATTRIBUTE_PATH],
$attributes[StorageAttributes::ATTRIBUTE_FILE_SIZE] ?? null,
$attributes[StorageAttributes::ATTRIBUTE_VISIBILITY] ?? null,
$attributes[StorageAttributes::ATTRIBUTE_LAST_MODIFIED] ?? null,
$attributes[StorageAttributes::ATTRIBUTE_MIME_TYPE] ?? null,
$attributes[StorageAttributes::ATTRIBUTE_EXTRA_METADATA] ?? []
);
}
public function jsonSerialize(): array
{
return [
StorageAttributes::ATTRIBUTE_TYPE => self::TYPE_FILE,
StorageAttributes::ATTRIBUTE_PATH => $this->path,
StorageAttributes::ATTRIBUTE_FILE_SIZE => $this->fileSize,
StorageAttributes::ATTRIBUTE_VISIBILITY => $this->visibility,
StorageAttributes::ATTRIBUTE_LAST_MODIFIED => $this->lastModified,
StorageAttributes::ATTRIBUTE_MIME_TYPE => $this->mimeType,
StorageAttributes::ATTRIBUTE_EXTRA_METADATA => $this->extraMetadata,
];
}
}

View File

@ -0,0 +1,163 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
class Filesystem implements FilesystemOperator
{
/**
* @var FilesystemAdapter
*/
private $adapter;
/**
* @var Config
*/
private $config;
/**
* @var PathNormalizer
*/
private $pathNormalizer;
public function __construct(
FilesystemAdapter $adapter,
array $config = [],
PathNormalizer $pathNormalizer = null
) {
$this->adapter = $adapter;
$this->config = new Config($config);
$this->pathNormalizer = $pathNormalizer ?: new WhitespacePathNormalizer();
}
public function fileExists(string $location): bool
{
return $this->adapter->fileExists($this->pathNormalizer->normalizePath($location));
}
public function write(string $location, string $contents, array $config = []): void
{
$this->adapter->write(
$this->pathNormalizer->normalizePath($location),
$contents,
$this->config->extend($config)
);
}
public function writeStream(string $location, $contents, array $config = []): void
{
/* @var resource $contents */
$this->assertIsResource($contents);
$this->rewindStream($contents);
$this->adapter->writeStream(
$this->pathNormalizer->normalizePath($location),
$contents,
$this->config->extend($config)
);
}
public function read(string $location): string
{
return $this->adapter->read($this->pathNormalizer->normalizePath($location));
}
public function readStream(string $location)
{
return $this->adapter->readStream($this->pathNormalizer->normalizePath($location));
}
public function delete(string $location): void
{
$this->adapter->delete($this->pathNormalizer->normalizePath($location));
}
public function deleteDirectory(string $location): void
{
$this->adapter->deleteDirectory($this->pathNormalizer->normalizePath($location));
}
public function createDirectory(string $location, array $config = []): void
{
$this->adapter->createDirectory(
$this->pathNormalizer->normalizePath($location),
$this->config->extend($config)
);
}
public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing
{
$path = $this->pathNormalizer->normalizePath($location);
return new DirectoryListing($this->adapter->listContents($path, $deep));
}
public function move(string $source, string $destination, array $config = []): void
{
$this->adapter->move(
$this->pathNormalizer->normalizePath($source),
$this->pathNormalizer->normalizePath($destination),
$this->config->extend($config)
);
}
public function copy(string $source, string $destination, array $config = []): void
{
$this->adapter->copy(
$this->pathNormalizer->normalizePath($source),
$this->pathNormalizer->normalizePath($destination),
$this->config->extend($config)
);
}
public function lastModified(string $path): int
{
return $this->adapter->lastModified($this->pathNormalizer->normalizePath($path))->lastModified();
}
public function fileSize(string $path): int
{
return $this->adapter->fileSize($this->pathNormalizer->normalizePath($path))->fileSize();
}
public function mimeType(string $path): string
{
return $this->adapter->mimeType($this->pathNormalizer->normalizePath($path))->mimeType();
}
public function setVisibility(string $path, string $visibility): void
{
$this->adapter->setVisibility($this->pathNormalizer->normalizePath($path), $visibility);
}
public function visibility(string $path): string
{
return $this->adapter->visibility($this->pathNormalizer->normalizePath($path))->visibility();
}
/**
* @param mixed $contents
*/
private function assertIsResource($contents): void
{
if (is_resource($contents) === false) {
throw new InvalidStreamProvided(
"Invalid stream provided, expected stream resource, received " . gettype($contents)
);
} elseif ($type = get_resource_type($contents) !== 'stream') {
throw new InvalidStreamProvided(
"Invalid stream provided, expected stream resource, received resource of type " . $type
);
}
}
/**
* @param resource $resource
*/
private function rewindStream($resource): void
{
if (ftell($resource) !== 0 && stream_get_meta_data($resource)['seekable']) {
rewind($resource);
}
}
}

View File

@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
interface FilesystemAdapter
{
/**
* @throws FilesystemException
*/
public function fileExists(string $path): bool;
/**
* @throws UnableToWriteFile
* @throws FilesystemException
*/
public function write(string $path, string $contents, Config $config): void;
/**
* @param resource $contents
*
* @throws UnableToWriteFile
* @throws FilesystemException
*/
public function writeStream(string $path, $contents, Config $config): void;
/**
* @throws UnableToReadFile
* @throws FilesystemException
*/
public function read(string $path): string;
/**
* @return resource
*
* @throws UnableToReadFile
* @throws FilesystemException
*/
public function readStream(string $path);
/**
* @throws UnableToDeleteFile
* @throws FilesystemException
*/
public function delete(string $path): void;
/**
* @throws UnableToDeleteDirectory
* @throws FilesystemException
*/
public function deleteDirectory(string $path): void;
/**
* @throws UnableToCreateDirectory
* @throws FilesystemException
*/
public function createDirectory(string $path, Config $config): void;
/**
* @throws InvalidVisibilityProvided
* @throws FilesystemException
*/
public function setVisibility(string $path, string $visibility): void;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function visibility(string $path): FileAttributes;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function mimeType(string $path): FileAttributes;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function lastModified(string $path): FileAttributes;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function fileSize(string $path): FileAttributes;
/**
* @return iterable<StorageAttributes>
*
* @throws FilesystemException
*/
public function listContents(string $path, bool $deep): iterable;
/**
* @throws UnableToMoveFile
* @throws FilesystemException
*/
public function move(string $source, string $destination, Config $config): void;
/**
* @throws UnableToCopyFile
* @throws FilesystemException
*/
public function copy(string $source, string $destination, Config $config): void;
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use Throwable;
interface FilesystemException extends Throwable
{
}

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
interface FilesystemOperationFailed extends FilesystemException
{
public const OPERATION_WRITE = 'WRITE';
public const OPERATION_UPDATE = 'UPDATE';
public const OPERATION_FILE_EXISTS = 'FILE_EXISTS';
public const OPERATION_CREATE_DIRECTORY = 'CREATE_DIRECTORY';
public const OPERATION_DELETE = 'DELETE';
public const OPERATION_DELETE_DIRECTORY = 'DELETE_DIRECTORY';
public const OPERATION_MOVE = 'MOVE';
public const OPERATION_RETRIEVE_METADATA = 'RETRIEVE_METADATA';
public const OPERATION_COPY = 'COPY';
public const OPERATION_READ = 'READ';
public const OPERATION_SET_VISIBILITY = 'SET_VISIBILITY';
public function operation(): string;
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
interface FilesystemOperator extends FilesystemReader, FilesystemWriter
{
}

View File

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
/**
* This interface contains everything to read from and inspect
* a filesystem. All methods containing are non-destructive.
*/
interface FilesystemReader
{
public const LIST_SHALLOW = false;
public const LIST_DEEP = true;
/**
* @throws FilesystemException
* @throws UnableToCheckFileExistence
*/
public function fileExists(string $location): bool;
/**
* @throws UnableToReadFile
* @throws FilesystemException
*/
public function read(string $location): string;
/**
* @return resource
*
* @throws UnableToReadFile
* @throws FilesystemException
*/
public function readStream(string $location);
/**
* @return DirectoryListing<StorageAttributes>
*
* @throws FilesystemException
*/
public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function lastModified(string $path): int;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function fileSize(string $path): int;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function mimeType(string $path): string;
/**
* @throws UnableToRetrieveMetadata
* @throws FilesystemException
*/
public function visibility(string $path): string;
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
interface FilesystemWriter
{
/**
* @throws UnableToWriteFile
* @throws FilesystemException
*/
public function write(string $location, string $contents, array $config = []): void;
/**
* @param mixed $contents
*
* @throws UnableToWriteFile
* @throws FilesystemException
*/
public function writeStream(string $location, $contents, array $config = []): void;
/**
* @throws UnableToSetVisibility
* @throws FilesystemException
*/
public function setVisibility(string $path, string $visibility): void;
/**
* @throws UnableToDeleteFile
* @throws FilesystemException
*/
public function delete(string $location): void;
/**
* @throws UnableToDeleteDirectory
* @throws FilesystemException
*/
public function deleteDirectory(string $location): void;
/**
* @throws UnableToCreateDirectory
* @throws FilesystemException
*/
public function createDirectory(string $location, array $config = []): void;
/**
* @throws UnableToMoveFile
* @throws FilesystemException
*/
public function move(string $source, string $destination, array $config = []): void;
/**
* @throws UnableToCopyFile
* @throws FilesystemException
*/
public function copy(string $source, string $destination, array $config = []): void;
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use InvalidArgumentException as BaseInvalidArgumentException;
class InvalidStreamProvided extends BaseInvalidArgumentException implements FilesystemException
{
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use InvalidArgumentException;
use function var_export;
class InvalidVisibilityProvided extends InvalidArgumentException implements FilesystemException
{
public static function withVisibility(string $visibility, string $expectedMessage): InvalidVisibilityProvided
{
$provided = var_export($visibility, true);
$message = "Invalid visibility provided. Expected {$expectedMessage}, received {$provided}";
throw new InvalidVisibilityProvided($message);
}
}

View File

@ -0,0 +1,419 @@
<?php
declare(strict_types=1);
namespace League\Flysystem\Local;
use function file_put_contents;
use const DIRECTORY_SEPARATOR;
use const LOCK_EX;
use DirectoryIterator;
use FilesystemIterator;
use Generator;
use League\Flysystem\Config;
use League\Flysystem\DirectoryAttributes;
use League\Flysystem\FileAttributes;
use League\Flysystem\FilesystemAdapter;
use League\Flysystem\PathPrefixer;
use League\Flysystem\SymbolicLinkEncountered;
use League\Flysystem\UnableToCopyFile;
use League\Flysystem\UnableToCreateDirectory;
use League\Flysystem\UnableToDeleteDirectory;
use League\Flysystem\UnableToDeleteFile;
use League\Flysystem\UnableToMoveFile;
use League\Flysystem\UnableToReadFile;
use League\Flysystem\UnableToRetrieveMetadata;
use League\Flysystem\UnableToSetVisibility;
use League\Flysystem\UnableToWriteFile;
use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
use League\Flysystem\UnixVisibility\VisibilityConverter;
use League\MimeTypeDetection\FinfoMimeTypeDetector;
use League\MimeTypeDetection\MimeTypeDetector;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use function chmod;
use function clearstatcache;
use function dirname;
use function error_clear_last;
use function error_get_last;
use function file_exists;
use function is_dir;
use function is_file;
use function mkdir;
use function rename;
use function stream_copy_to_stream;
class LocalFilesystemAdapter implements FilesystemAdapter
{
/**
* @var int
*/
public const SKIP_LINKS = 0001;
/**
* @var int
*/
public const DISALLOW_LINKS = 0002;
/**
* @var PathPrefixer
*/
private $prefixer;
/**
* @var int
*/
private $writeFlags;
/**
* @var int
*/
private $linkHandling;
/**
* @var VisibilityConverter
*/
private $visibility;
/**
* @var MimeTypeDetector
*/
private $mimeTypeDetector;
public function __construct(
string $location,
VisibilityConverter $visibility = null,
int $writeFlags = LOCK_EX,
int $linkHandling = self::DISALLOW_LINKS,
MimeTypeDetector $mimeTypeDetector = null
) {
$this->prefixer = new PathPrefixer($location, DIRECTORY_SEPARATOR);
$this->writeFlags = $writeFlags;
$this->linkHandling = $linkHandling;
$this->visibility = $visibility ?: new PortableVisibilityConverter();
$this->ensureDirectoryExists($location, $this->visibility->defaultForDirectories());
$this->mimeTypeDetector = $mimeTypeDetector ?: new FinfoMimeTypeDetector();
}
public function write(string $path, string $contents, Config $config): void
{
$this->writeToFile($path, $contents, $config);
}
public function writeStream(string $path, $contents, Config $config): void
{
$this->writeToFile($path, $contents, $config);
}
/**
* @param resource|string $contents
*/
private function writeToFile(string $path, $contents, Config $config): void
{
$prefixedLocation = $this->prefixer->prefixPath($path);
$this->ensureDirectoryExists(
dirname($prefixedLocation),
$this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY))
);
error_clear_last();
if (@file_put_contents($prefixedLocation, $contents, $this->writeFlags) === false) {
throw UnableToWriteFile::atLocation($path, error_get_last()['message'] ?? '');
}
if ($visibility = $config->get(Config::OPTION_VISIBILITY)) {
$this->setVisibility($path, (string) $visibility);
}
}
public function delete(string $path): void
{
$location = $this->prefixer->prefixPath($path);
if ( ! file_exists($location)) {
return;
}
error_clear_last();
if ( ! @unlink($location)) {
throw UnableToDeleteFile::atLocation($location, error_get_last()['message'] ?? '');
}
}
public function deleteDirectory(string $prefix): void
{
$location = $this->prefixer->prefixPath($prefix);
if ( ! is_dir($location)) {
return;
}
$contents = $this->listDirectoryRecursively($location, RecursiveIteratorIterator::CHILD_FIRST);
/** @var SplFileInfo $file */
foreach ($contents as $file) {
if ( ! $this->deleteFileInfoObject($file)) {
throw UnableToDeleteDirectory::atLocation($prefix, "Unable to delete file at " . $file->getPathname());
}
}
unset($contents);
if ( ! @rmdir($location)) {
throw UnableToDeleteDirectory::atLocation($prefix, error_get_last()['message'] ?? '');
}
}
private function listDirectoryRecursively(
string $path,
int $mode = RecursiveIteratorIterator::SELF_FIRST
): Generator {
yield from new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
$mode
);
}
protected function deleteFileInfoObject(SplFileInfo $file): bool
{
switch ($file->getType()) {
case 'dir':
return @rmdir((string) $file->getRealPath());
case 'link':
return @unlink((string) $file->getPathname());
default:
return @unlink((string) $file->getRealPath());
}
}
public function listContents(string $path, bool $deep): iterable
{
$location = $this->prefixer->prefixPath($path);
if ( ! is_dir($location)) {
return;
}
/** @var SplFileInfo[] $iterator */
$iterator = $deep ? $this->listDirectoryRecursively($location) : $this->listDirectory($location);
foreach ($iterator as $fileInfo) {
if ($fileInfo->isLink()) {
if ($this->linkHandling & self::SKIP_LINKS) {
continue;
}
throw SymbolicLinkEncountered::atLocation($fileInfo->getPathname());
}
$path = $this->prefixer->stripPrefix($fileInfo->getPathname());
$lastModified = $fileInfo->getMTime();
$isDirectory = $fileInfo->isDir();
$permissions = octdec(substr(sprintf('%o', $fileInfo->getPerms()), -4));
$visibility = $isDirectory ? $this->visibility->inverseForDirectory($permissions) : $this->visibility->inverseForFile($permissions);
yield $isDirectory ? new DirectoryAttributes($path, $visibility, $lastModified) : new FileAttributes(
str_replace('\\', '/', $path),
$fileInfo->getSize(),
$visibility,
$lastModified
);
}
}
public function move(string $source, string $destination, Config $config): void
{
$sourcePath = $this->prefixer->prefixPath($source);
$destinationPath = $this->prefixer->prefixPath($destination);
$this->ensureDirectoryExists(
dirname($destinationPath),
$this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY))
);
if ( ! @rename($sourcePath, $destinationPath)) {
throw UnableToMoveFile::fromLocationTo($sourcePath, $destinationPath);
}
}
public function copy(string $source, string $destination, Config $config): void
{
$sourcePath = $this->prefixer->prefixPath($source);
$destinationPath = $this->prefixer->prefixPath($destination);
$this->ensureDirectoryExists(
dirname($destinationPath),
$this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY))
);
if ( ! @copy($sourcePath, $destinationPath)) {
throw UnableToCopyFile::fromLocationTo($sourcePath, $destinationPath);
}
}
public function read(string $path): string
{
$location = $this->prefixer->prefixPath($path);
error_clear_last();
$contents = @file_get_contents($location);
if ($contents === false) {
throw UnableToReadFile::fromLocation($path, error_get_last()['message'] ?? '');
}
return $contents;
}
public function readStream(string $path)
{
$location = $this->prefixer->prefixPath($path);
error_clear_last();
$contents = @fopen($location, 'rb');
if ($contents === false) {
throw UnableToReadFile::fromLocation($path, error_get_last()['message'] ?? '');
}
return $contents;
}
protected function ensureDirectoryExists(string $dirname, int $visibility): void
{
if (is_dir($dirname)) {
return;
}
error_clear_last();
if ( ! @mkdir($dirname, $visibility, true)) {
$mkdirError = error_get_last();
}
clearstatcache(true, $dirname);
if ( ! is_dir($dirname)) {
$errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
throw UnableToCreateDirectory::atLocation($dirname, $errorMessage);
}
}
public function fileExists(string $location): bool
{
$location = $this->prefixer->prefixPath($location);
return is_file($location);
}
public function createDirectory(string $path, Config $config): void
{
$location = $this->prefixer->prefixPath($path);
$visibility = $config->get(Config::OPTION_VISIBILITY, $config->get(Config::OPTION_DIRECTORY_VISIBILITY));
$permissions = $this->resolveDirectoryVisibility($visibility);
if (is_dir($location)) {
$this->setPermissions($location, $permissions);
return;
}
error_clear_last();
if ( ! @mkdir($location, $permissions, true)) {
throw UnableToCreateDirectory::atLocation($path, error_get_last()['message'] ?? '');
}
}
public function setVisibility(string $path, string $visibility): void
{
$path = $this->prefixer->prefixPath($path);
$visibility = is_dir($path) ? $this->visibility->forDirectory($visibility) : $this->visibility->forFile(
$visibility
);
$this->setPermissions($path, $visibility);
}
public function visibility(string $path): FileAttributes
{
$location = $this->prefixer->prefixPath($path);
clearstatcache(false, $location);
error_clear_last();
$fileperms = @fileperms($location);
if ($fileperms === false) {
throw UnableToRetrieveMetadata::visibility($path, error_get_last()['message'] ?? '');
}
$permissions = $fileperms & 0777;
$visibility = $this->visibility->inverseForFile($permissions);
return new FileAttributes($path, null, $visibility);
}
private function resolveDirectoryVisibility(?string $visibility): int
{
return $visibility === null ? $this->visibility->defaultForDirectories() : $this->visibility->forDirectory(
$visibility
);
}
public function mimeType(string $path): FileAttributes
{
$location = $this->prefixer->prefixPath($path);
error_clear_last();
$mimeType = $this->mimeTypeDetector->detectMimeTypeFromFile($location);
if ($mimeType === null) {
throw UnableToRetrieveMetadata::mimeType($path, error_get_last()['message'] ?? '');
}
return new FileAttributes($path, null, null, null, $mimeType);
}
public function lastModified(string $path): FileAttributes
{
$location = $this->prefixer->prefixPath($path);
error_clear_last();
$lastModified = @filemtime($location);
if ($lastModified === false) {
throw UnableToRetrieveMetadata::lastModified($path, error_get_last()['message'] ?? '');
}
return new FileAttributes($path, null, null, $lastModified);
}
public function fileSize(string $path): FileAttributes
{
$location = $this->prefixer->prefixPath($path);
error_clear_last();
if (is_file($location) && ($fileSize = @filesize($location)) !== false) {
return new FileAttributes($path, $fileSize);
}
throw UnableToRetrieveMetadata::fileSize($path, error_get_last()['message'] ?? '');
}
private function listDirectory(string $location): Generator
{
$iterator = new DirectoryIterator($location);
foreach ($iterator as $item) {
if ($item->isDot()) {
continue;
}
yield $item;
}
}
private function setPermissions(string $location, int $visibility): void
{
error_clear_last();
if ( ! @chmod($location, $visibility)) {
$extraMessage = error_get_last()['message'] ?? '';
throw UnableToSetVisibility::atLocation($this->prefixer->stripPrefix($location), $extraMessage);
}
}
}

View File

@ -0,0 +1,334 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use function sprintf;
class MountManager implements FilesystemOperator
{
/**
* @var array<string, FilesystemOperator>
*/
private $filesystems = [];
/**
* MountManager constructor.
*
* @param array<string,FilesystemOperator> $filesystems
*/
public function __construct(array $filesystems = [])
{
$this->mountFilesystems($filesystems);
}
public function fileExists(string $location): bool
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
return $filesystem->fileExists($path);
} catch (UnableToCheckFileExistence $exception) {
throw UnableToCheckFileExistence::forLocation($location, $exception);
}
}
public function read(string $location): string
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
return $filesystem->read($path);
} catch (UnableToReadFile $exception) {
throw UnableToReadFile::fromLocation($location, $exception->reason(), $exception);
}
}
public function readStream(string $location)
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
return $filesystem->readStream($path);
} catch (UnableToReadFile $exception) {
throw UnableToReadFile::fromLocation($location, $exception->reason(), $exception);
}
}
public function listContents(string $location, bool $deep = self::LIST_SHALLOW): DirectoryListing
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path, $mountIdentifier] = $this->determineFilesystemAndPath($location);
return
$filesystem
->listContents($path, $deep)
->map(
function (StorageAttributes $attributes) use ($mountIdentifier) {
return $attributes->withPath(sprintf('%s://%s', $mountIdentifier, $attributes->path()));
}
);
}
public function lastModified(string $location): int
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
return $filesystem->lastModified($path);
} catch (UnableToRetrieveMetadata $exception) {
throw UnableToRetrieveMetadata::lastModified($location, $exception->reason(), $exception);
}
}
public function fileSize(string $location): int
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
return $filesystem->fileSize($path);
} catch (UnableToRetrieveMetadata $exception) {
throw UnableToRetrieveMetadata::fileSize($location, $exception->reason(), $exception);
}
}
public function mimeType(string $location): string
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
return $filesystem->mimeType($path);
} catch (UnableToRetrieveMetadata $exception) {
throw UnableToRetrieveMetadata::mimeType($location, $exception->reason(), $exception);
}
}
public function visibility(string $location): string
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
return $filesystem->visibility($path);
} catch (UnableToRetrieveMetadata $exception) {
throw UnableToRetrieveMetadata::visibility($location, $exception->reason(), $exception);
}
}
public function write(string $location, string $contents, array $config = []): void
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
$filesystem->write($path, $contents, $config);
} catch (UnableToWriteFile $exception) {
throw UnableToWriteFile::atLocation($location, $exception->reason(), $exception);
}
}
public function writeStream(string $location, $contents, array $config = []): void
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
$filesystem->writeStream($path, $contents, $config);
}
public function setVisibility(string $path, string $visibility): void
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($path);
$filesystem->setVisibility($path, $visibility);
}
public function delete(string $location): void
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
$filesystem->delete($path);
} catch (UnableToDeleteFile $exception) {
throw UnableToDeleteFile::atLocation($location, '', $exception);
}
}
public function deleteDirectory(string $location): void
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
$filesystem->deleteDirectory($path);
} catch (UnableToDeleteDirectory $exception) {
throw UnableToDeleteDirectory::atLocation($location, '', $exception);
}
}
public function createDirectory(string $location, array $config = []): void
{
/** @var FilesystemOperator $filesystem */
[$filesystem, $path] = $this->determineFilesystemAndPath($location);
try {
$filesystem->createDirectory($path, $config);
} catch (UnableToCreateDirectory $exception) {
throw UnableToCreateDirectory::dueToFailure($location, $exception);
}
}
public function move(string $source, string $destination, array $config = []): void
{
/** @var FilesystemOperator $sourceFilesystem */
/* @var FilesystemOperator $destinationFilesystem */
[$sourceFilesystem, $sourcePath] = $this->determineFilesystemAndPath($source);
[$destinationFilesystem, $destinationPath] = $this->determineFilesystemAndPath($destination);
$sourceFilesystem === $destinationFilesystem ? $this->moveInTheSameFilesystem(
$sourceFilesystem,
$sourcePath,
$destinationPath,
$source,
$destination
) : $this->moveAcrossFilesystems($source, $destination);
}
public function copy(string $source, string $destination, array $config = []): void
{
/** @var FilesystemOperator $sourceFilesystem */
/* @var FilesystemOperator $destinationFilesystem */
[$sourceFilesystem, $sourcePath] = $this->determineFilesystemAndPath($source);
[$destinationFilesystem, $destinationPath] = $this->determineFilesystemAndPath($destination);
$sourceFilesystem === $destinationFilesystem ? $this->copyInSameFilesystem(
$sourceFilesystem,
$sourcePath,
$destinationPath,
$source,
$destination
) : $this->copyAcrossFilesystem(
$config['visibility'] ?? null,
$sourceFilesystem,
$sourcePath,
$destinationFilesystem,
$destinationPath,
$source,
$destination
);
}
private function mountFilesystems(array $filesystems): void
{
foreach ($filesystems as $key => $filesystem) {
$this->guardAgainstInvalidMount($key, $filesystem);
/* @var string $key */
/* @var FilesystemOperator $filesystem */
$this->mountFilesystem($key, $filesystem);
}
}
/**
* @param mixed $key
* @param mixed $filesystem
*/
private function guardAgainstInvalidMount($key, $filesystem): void
{
if ( ! is_string($key)) {
throw UnableToMountFilesystem::becauseTheKeyIsNotValid($key);
}
if ( ! $filesystem instanceof FilesystemOperator) {
throw UnableToMountFilesystem::becauseTheFilesystemWasNotValid($filesystem);
}
}
private function mountFilesystem(string $key, FilesystemOperator $filesystem): void
{
$this->filesystems[$key] = $filesystem;
}
/**
* @param string $path
*
* @return array{0:FilesystemOperator, 1:string}
*/
private function determineFilesystemAndPath(string $path): array
{
if (strpos($path, '://') < 1) {
throw UnableToResolveFilesystemMount::becauseTheSeparatorIsMissing($path);
}
/** @var string $mountIdentifier */
/** @var string $mountPath */
[$mountIdentifier, $mountPath] = explode('://', $path, 2);
if ( ! array_key_exists($mountIdentifier, $this->filesystems)) {
throw UnableToResolveFilesystemMount::becauseTheMountWasNotRegistered($mountIdentifier);
}
return [$this->filesystems[$mountIdentifier], $mountPath, $mountIdentifier];
}
private function copyInSameFilesystem(
FilesystemOperator $sourceFilesystem,
string $sourcePath,
string $destinationPath,
string $source,
string $destination
): void {
try {
$sourceFilesystem->copy($sourcePath, $destinationPath);
} catch (UnableToCopyFile $exception) {
throw UnableToCopyFile::fromLocationTo($source, $destination, $exception);
}
}
private function copyAcrossFilesystem(
?string $visibility,
FilesystemOperator $sourceFilesystem,
string $sourcePath,
FilesystemOperator $destinationFilesystem,
string $destinationPath,
string $source,
string $destination
): void {
try {
$visibility = $visibility ?? $sourceFilesystem->visibility($sourcePath);
$stream = $sourceFilesystem->readStream($sourcePath);
$destinationFilesystem->writeStream($destinationPath, $stream, compact('visibility'));
} catch (UnableToRetrieveMetadata | UnableToReadFile | UnableToWriteFile $exception) {
throw UnableToCopyFile::fromLocationTo($source, $destination, $exception);
}
}
private function moveInTheSameFilesystem(
FilesystemOperator $sourceFilesystem,
string $sourcePath,
string $destinationPath,
string $source,
string $destination
): void {
try {
$sourceFilesystem->move($sourcePath, $destinationPath);
} catch (UnableToMoveFile $exception) {
throw UnableToMoveFile::fromLocationTo($source, $destination, $exception);
}
}
private function moveAcrossFilesystems(string $source, string $destination): void
{
try {
$this->copy($source, $destination);
$this->delete($source);
} catch (UnableToCopyFile | UnableToDeleteFile $exception) {
throw UnableToMoveFile::fromLocationTo($source, $destination, $exception);
}
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
interface PathNormalizer
{
public function normalizePath(string $path): string;
}

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use function rtrim;
use function strlen;
use function substr;
final class PathPrefixer
{
/**
* @var string
*/
private $prefix = '';
/**
* @var string
*/
private $separator = '/';
public function __construct(string $prefix, string $separator = '/')
{
$this->prefix = rtrim($prefix, '\\/');
if ($this->prefix !== '' || $prefix === $separator) {
$this->prefix .= $separator;
}
$this->separator = $separator;
}
public function prefixPath(string $path): string
{
return $this->prefix . ltrim($path, '\\/');
}
public function stripPrefix(string $path): string
{
/* @var string */
return substr($path, strlen($this->prefix));
}
public function stripDirectoryPrefix(string $path): string
{
return rtrim($this->stripPrefix($path), '\\/');
}
public function prefixDirectoryPath(string $path): string
{
$prefixedPath = $this->prefixPath(rtrim($path, '\\/'));
if ((substr($prefixedPath, -1) === $this->separator) || $prefixedPath === '') {
return $prefixedPath;
}
return $prefixedPath . $this->separator;
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
class PathTraversalDetected extends RuntimeException implements FilesystemException
{
/**
* @var string
*/
private $path;
public function path(): string
{
return $this->path;
}
public static function forPath(string $path): PathTraversalDetected
{
$e = new PathTraversalDetected("Path traversal detected: {$path}");
$e->path = $path;
return $e;
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
final class PortableVisibilityGuard
{
public static function guardAgainstInvalidInput(string $visibility): void
{
if ($visibility !== Visibility::PUBLIC && $visibility !== Visibility::PRIVATE) {
$className = Visibility::class;
throw InvalidVisibilityProvided::withVisibility(
$visibility,
"either {$className}::PUBLIC or {$className}::PRIVATE"
);
}
}
}

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
/**
* @internal
*/
trait ProxyArrayAccessToProperties
{
private function formatPropertyName(string $offset): string
{
return str_replace('_', '', lcfirst(ucwords($offset, '_')));
}
/**
* @param mixed $offset
*
* @return bool
*/
public function offsetExists($offset): bool
{
$property = $this->formatPropertyName((string) $offset);
return isset($this->{$property});
}
/**
* @param mixed $offset
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
$property = $this->formatPropertyName((string) $offset);
return $this->{$property};
}
/**
* @param mixed $offset
* @param mixed $value
*/
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value): void
{
throw new RuntimeException('Properties can not be manipulated');
}
/**
* @param mixed $offset
*/
#[\ReturnTypeWillChange]
public function offsetUnset($offset): void
{
throw new RuntimeException('Properties can not be manipulated');
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use ArrayAccess;
use JsonSerializable;
interface StorageAttributes extends JsonSerializable, ArrayAccess
{
public const ATTRIBUTE_PATH = 'path';
public const ATTRIBUTE_TYPE = 'type';
public const ATTRIBUTE_FILE_SIZE = 'file_size';
public const ATTRIBUTE_VISIBILITY = 'visibility';
public const ATTRIBUTE_LAST_MODIFIED = 'last_modified';
public const ATTRIBUTE_MIME_TYPE = 'mime_type';
public const ATTRIBUTE_EXTRA_METADATA = 'extra_metadata';
public const TYPE_FILE = 'file';
public const TYPE_DIRECTORY = 'dir';
public function path(): string;
public function type(): string;
public function visibility(): ?string;
public function lastModified(): ?int;
public static function fromArray(array $attributes): StorageAttributes;
public function isFile(): bool;
public function isDir(): bool;
public function withPath(string $path): StorageAttributes;
public function extraMetadata(): array;
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
final class SymbolicLinkEncountered extends RuntimeException implements FilesystemException
{
/**
* @var string
*/
private $location;
public function location(): string
{
return $this->location;
}
public static function atLocation(string $pathName): SymbolicLinkEncountered
{
$e = new static("Unsupported symbolic link encountered at location $pathName");
$e->location = $pathName;
return $e;
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
class UnableToCheckFileExistence extends RuntimeException implements FilesystemOperationFailed
{
public static function forLocation(string $path, Throwable $exception = null): UnableToCheckFileExistence
{
return new UnableToCheckFileExistence("Unable to check file existence for: ${path}", 0, $exception);
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_FILE_EXISTS;
}
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToCopyFile extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $source;
/**
* @var string
*/
private $destination;
public function source(): string
{
return $this->source;
}
public function destination(): string
{
return $this->destination;
}
public static function fromLocationTo(
string $sourcePath,
string $destinationPath,
Throwable $previous = null
): UnableToCopyFile {
$e = new static("Unable to copy file from $sourcePath to $destinationPath", 0 , $previous);
$e->source = $sourcePath;
$e->destination = $destinationPath;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_COPY;
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToCreateDirectory extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $location;
public static function atLocation(string $dirname, string $errorMessage = ''): UnableToCreateDirectory
{
$message = "Unable to create a directory at {$dirname}. ${errorMessage}";
$e = new static(rtrim($message));
$e->location = $dirname;
return $e;
}
public static function dueToFailure(string $dirname, Throwable $previous): UnableToCreateDirectory
{
$message = "Unable to create a directory at {$dirname}";
$e = new static($message, 0, $previous);
$e->location = $dirname;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_CREATE_DIRECTORY;
}
public function location(): string
{
return $this->location;
}
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToDeleteDirectory extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $location = '';
/**
* @var string
*/
private $reason;
public static function atLocation(
string $location,
string $reason = '',
Throwable $previous = null
): UnableToDeleteDirectory {
$e = new static(rtrim("Unable to delete directory located at: {$location}. {$reason}"), 0, $previous);
$e->location = $location;
$e->reason = $reason;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_DELETE_DIRECTORY;
}
public function reason(): string
{
return $this->reason;
}
public function location(): string
{
return $this->location;
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToDeleteFile extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $location = '';
/**
* @var string
*/
private $reason;
public static function atLocation(string $location, string $reason = '', Throwable $previous = null): UnableToDeleteFile
{
$e = new static(rtrim("Unable to delete file located at: {$location}. {$reason}"), 0, $previous);
$e->location = $location;
$e->reason = $reason;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_DELETE;
}
public function reason(): string
{
return $this->reason;
}
public function location(): string
{
return $this->location;
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use LogicException;
class UnableToMountFilesystem extends LogicException implements FilesystemException
{
/**
* @param mixed $key
*/
public static function becauseTheKeyIsNotValid($key): UnableToMountFilesystem
{
return new UnableToMountFilesystem(
'Unable to mount filesystem, key was invalid. String expected, received: ' . gettype($key)
);
}
/**
* @param mixed $filesystem
*/
public static function becauseTheFilesystemWasNotValid($filesystem): UnableToMountFilesystem
{
$received = is_object($filesystem) ? get_class($filesystem) : gettype($filesystem);
return new UnableToMountFilesystem(
'Unable to mount filesystem, filesystem was invalid. Instance of ' . FilesystemOperator::class . ' expected, received: ' . $received
);
}
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToMoveFile extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $source;
/**
* @var string
*/
private $destination;
public function source(): string
{
return $this->source;
}
public function destination(): string
{
return $this->destination;
}
public static function fromLocationTo(
string $sourcePath,
string $destinationPath,
Throwable $previous = null
): UnableToMoveFile {
$e = new static("Unable to move file from $sourcePath to $destinationPath", 0, $previous);
$e->source = $sourcePath;
$e->destination = $destinationPath;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_MOVE;
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToReadFile extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $location = '';
/**
* @var string
*/
private $reason = '';
public static function fromLocation(string $location, string $reason = '', Throwable $previous = null): UnableToReadFile
{
$e = new static(rtrim("Unable to read file from location: {$location}. {$reason}"), 0, $previous);
$e->location = $location;
$e->reason = $reason;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_READ;
}
public function reason(): string
{
return $this->reason;
}
public function location(): string
{
return $this->location;
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
class UnableToResolveFilesystemMount extends RuntimeException implements FilesystemException
{
public static function becauseTheSeparatorIsMissing(string $path): UnableToResolveFilesystemMount
{
return new UnableToResolveFilesystemMount("Unable to resolve the filesystem mount because the path ($path) is missing a separator (://).");
}
public static function becauseTheMountWasNotRegistered(string $mountIdentifier): UnableToResolveFilesystemMount
{
return new UnableToResolveFilesystemMount("Unable to resolve the filesystem mount because the mount ($mountIdentifier) was not registered.");
}
}

View File

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToRetrieveMetadata extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $location;
/**
* @var string
*/
private $metadataType;
/**
* @var string
*/
private $reason;
public static function lastModified(string $location, string $reason = '', Throwable $previous = null): self
{
return static::create($location, FileAttributes::ATTRIBUTE_LAST_MODIFIED, $reason, $previous);
}
public static function visibility(string $location, string $reason = '', Throwable $previous = null): self
{
return static::create($location, FileAttributes::ATTRIBUTE_VISIBILITY, $reason, $previous);
}
public static function fileSize(string $location, string $reason = '', Throwable $previous = null): self
{
return static::create($location, FileAttributes::ATTRIBUTE_FILE_SIZE, $reason, $previous);
}
public static function mimeType(string $location, string $reason = '', Throwable $previous = null): self
{
return static::create($location, FileAttributes::ATTRIBUTE_MIME_TYPE, $reason, $previous);
}
public static function create(string $location, string $type, string $reason = '', Throwable $previous = null): self
{
$e = new static("Unable to retrieve the $type for file at location: $location. {$reason}", 0, $previous);
$e->reason = $reason;
$e->location = $location;
$e->metadataType = $type;
return $e;
}
public function reason(): string
{
return $this->reason;
}
public function location(): string
{
return $this->location;
}
public function metadataType(): string
{
return $this->metadataType;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_RETRIEVE_METADATA;
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
use function rtrim;
final class UnableToSetVisibility extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $location;
/**
* @var string
*/
private $reason;
public function reason(): string
{
return $this->reason;
}
public static function atLocation(string $filename, string $extraMessage = '', Throwable $previous = null): self
{
$message = "Unable to set visibility for file {$filename}. $extraMessage";
$e = new static(rtrim($message), 0, $previous);
$e->reason = $extraMessage;
$e->location = $filename;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_SET_VISIBILITY;
}
public function location(): string
{
return $this->location;
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
use Throwable;
final class UnableToWriteFile extends RuntimeException implements FilesystemOperationFailed
{
/**
* @var string
*/
private $location = '';
/**
* @var string
*/
private $reason;
public static function atLocation(string $location, string $reason = '', Throwable $previous = null): UnableToWriteFile
{
$e = new static(rtrim("Unable to write file at location: {$location}. {$reason}"), 0, $previous);
$e->location = $location;
$e->reason = $reason;
return $e;
}
public function operation(): string
{
return FilesystemOperationFailed::OPERATION_WRITE;
}
public function reason(): string
{
return $this->reason;
}
public function location(): string
{
return $this->location;
}
}

View File

@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace League\Flysystem\UnixVisibility;
use League\Flysystem\PortableVisibilityGuard;
use League\Flysystem\Visibility;
class PortableVisibilityConverter implements VisibilityConverter
{
/**
* @var int
*/
private $filePublic;
/**
* @var int
*/
private $filePrivate;
/**
* @var int
*/
private $directoryPublic;
/**
* @var int
*/
private $directoryPrivate;
/**
* @var string
*/
private $defaultForDirectories;
public function __construct(
int $filePublic = 0644,
int $filePrivate = 0600,
int $directoryPublic = 0755,
int $directoryPrivate = 0700,
string $defaultForDirectories = Visibility::PRIVATE
) {
$this->filePublic = $filePublic;
$this->filePrivate = $filePrivate;
$this->directoryPublic = $directoryPublic;
$this->directoryPrivate = $directoryPrivate;
$this->defaultForDirectories = $defaultForDirectories;
}
public function forFile(string $visibility): int
{
PortableVisibilityGuard::guardAgainstInvalidInput($visibility);
return $visibility === Visibility::PUBLIC
? $this->filePublic
: $this->filePrivate;
}
public function forDirectory(string $visibility): int
{
PortableVisibilityGuard::guardAgainstInvalidInput($visibility);
return $visibility === Visibility::PUBLIC
? $this->directoryPublic
: $this->directoryPrivate;
}
public function inverseForFile(int $visibility): string
{
if ($visibility === $this->filePublic) {
return Visibility::PUBLIC;
} elseif ($visibility === $this->filePrivate) {
return Visibility::PRIVATE;
}
return Visibility::PUBLIC; // default
}
public function inverseForDirectory(int $visibility): string
{
if ($visibility === $this->directoryPublic) {
return Visibility::PUBLIC;
} elseif ($visibility === $this->directoryPrivate) {
return Visibility::PRIVATE;
}
return Visibility::PUBLIC; // default
}
public function defaultForDirectories(): int
{
return $this->defaultForDirectories === Visibility::PUBLIC ? $this->directoryPublic : $this->directoryPrivate;
}
/**
* @param array<mixed> $permissionMap
*/
public static function fromArray(array $permissionMap, string $defaultForDirectories = Visibility::PRIVATE): PortableVisibilityConverter
{
return new PortableVisibilityConverter(
$permissionMap['file']['public'] ?? 0644,
$permissionMap['file']['private'] ?? 0600,
$permissionMap['dir']['public'] ?? 0755,
$permissionMap['dir']['private'] ?? 0700,
$defaultForDirectories
);
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace League\Flysystem\UnixVisibility;
interface VisibilityConverter
{
public function forFile(string $visibility): int;
public function forDirectory(string $visibility): int;
public function inverseForFile(int $visibility): string;
public function inverseForDirectory(int $visibility): string;
public function defaultForDirectories(): int;
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
use RuntimeException;
final class UnreadableFileEncountered extends RuntimeException implements FilesystemException
{
/**
* @var string
*/
private $location;
public function location(): string
{
return $this->location;
}
public static function atLocation(string $location): UnreadableFileEncountered
{
$e = new static("Unreadable file encountered at location {$location}.");
$e->location = $location;
return $e;
}
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
final class Visibility
{
public const PUBLIC = 'public';
public const PRIVATE = 'private';
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace League\Flysystem;
class WhitespacePathNormalizer implements PathNormalizer
{
public function normalizePath(string $path): string
{
$path = str_replace('\\', '/', $path);
$this->rejectFunkyWhiteSpace($path);
return $this->normalizeRelativePath($path);
}
private function rejectFunkyWhiteSpace(string $path): void
{
if (preg_match('#\p{C}+#u', $path)) {
throw CorruptedPathDetected::forPath($path);
}
}
private function normalizeRelativePath(string $path): string
{
$parts = [];
foreach (explode('/', $path) as $part) {
switch ($part) {
case '':
case '.':
break;
case '..':
if (empty($parts)) {
throw PathTraversalDetected::forPath($path);
}
array_pop($parts);
break;
default:
$parts[] = $part;
break;
}
}
return implode('/', $parts);
}
}

View File

@ -0,0 +1,49 @@
# Changelog
## 1.13.0 - 2022-08-05
### Added
- A reverse lookup mechanism to fetch one or all extensions for a given mimetype
## 1.12.0 - 2022-08-03
### Updated
- Updated lookup
## 1.11.0 - 2022-04-17
### Updated
- Updated lookup
## 1.10.0 - 2022-04-11
### Fixed
- Added Flysystem v1 inconclusive mime-types and made it configurable as a constructor parameter.
## 1.9.0 - 2021-11-21
### Updated
- Updated lookup
## 1.8.0 - 2021-09-25
### Added
- Added the decorator `OverridingExtensionToMimeTypeMap` which allows you to override values.
## 1.7.0 - 2021-01-18
### Added
- Added a `bufferSampleSize` parameter to the `FinfoMimeTypeDetector` class that allows you to send a reduced content sample which costs less memory.
## 1.6.0 - 2021-01-18
### Changes
- Updated generated mime-type map

View File

@ -0,0 +1,19 @@
Copyright (c) 2013-2023 Frank de Jonge
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
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.

View File

@ -0,0 +1,34 @@
{
"name": "league/mime-type-detection",
"description": "Mime-type detection for Flysystem",
"license": "MIT",
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"scripts": {
"test": "vendor/bin/phpunit",
"phpstan": "vendor/bin/phpstan analyse -l 6 src"
},
"require": {
"php": "^7.4 || ^8.0",
"ext-fileinfo": "*"
},
"require-dev": {
"phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0",
"phpstan/phpstan": "^0.12.68",
"friendsofphp/php-cs-fixer": "^3.2"
},
"autoload": {
"psr-4": {
"League\\MimeTypeDetection\\": "src"
}
},
"config": {
"platform": {
"php": "7.4.0"
}
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
class EmptyExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
{
public function lookupMimeType(string $extension): ?string
{
return null;
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
interface ExtensionLookup
{
public function lookupExtension(string $mimetype): ?string;
/**
* @return string[]
*/
public function lookupAllExtensions(string $mimetype): array;
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
use const PATHINFO_EXTENSION;
class ExtensionMimeTypeDetector implements MimeTypeDetector, ExtensionLookup
{
/**
* @var ExtensionToMimeTypeMap
*/
private $extensions;
public function __construct(ExtensionToMimeTypeMap $extensions = null)
{
$this->extensions = $extensions ?: new GeneratedExtensionToMimeTypeMap();
}
public function detectMimeType(string $path, $contents): ?string
{
return $this->detectMimeTypeFromPath($path);
}
public function detectMimeTypeFromPath(string $path): ?string
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
return $this->extensions->lookupMimeType($extension);
}
public function detectMimeTypeFromFile(string $path): ?string
{
return $this->detectMimeTypeFromPath($path);
}
public function detectMimeTypeFromBuffer(string $contents): ?string
{
return null;
}
public function lookupExtension(string $mimetype): ?string
{
return $this->extensions instanceof ExtensionLookup
? $this->extensions->lookupExtension($mimetype)
: null;
}
public function lookupAllExtensions(string $mimetype): array
{
return $this->extensions instanceof ExtensionLookup
? $this->extensions->lookupAllExtensions($mimetype)
: [];
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
interface ExtensionToMimeTypeMap
{
public function lookupMimeType(string $extension): ?string;
}

View File

@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
use const FILEINFO_MIME_TYPE;
use const PATHINFO_EXTENSION;
use finfo;
class FinfoMimeTypeDetector implements MimeTypeDetector, ExtensionLookup
{
private const INCONCLUSIVE_MIME_TYPES = [
'application/x-empty',
'text/plain',
'text/x-asm',
'application/octet-stream',
'inode/x-empty',
];
/**
* @var finfo
*/
private $finfo;
/**
* @var ExtensionToMimeTypeMap
*/
private $extensionMap;
/**
* @var int|null
*/
private $bufferSampleSize;
/**
* @var array<string>
*/
private $inconclusiveMimetypes;
public function __construct(
string $magicFile = '',
ExtensionToMimeTypeMap $extensionMap = null,
?int $bufferSampleSize = null,
array $inconclusiveMimetypes = self::INCONCLUSIVE_MIME_TYPES
) {
$this->finfo = new finfo(FILEINFO_MIME_TYPE, $magicFile);
$this->extensionMap = $extensionMap ?: new GeneratedExtensionToMimeTypeMap();
$this->bufferSampleSize = $bufferSampleSize;
$this->inconclusiveMimetypes = $inconclusiveMimetypes;
}
public function detectMimeType(string $path, $contents): ?string
{
$mimeType = is_string($contents)
? (@$this->finfo->buffer($this->takeSample($contents)) ?: null)
: null;
if ($mimeType !== null && ! in_array($mimeType, $this->inconclusiveMimetypes)) {
return $mimeType;
}
return $this->detectMimeTypeFromPath($path);
}
public function detectMimeTypeFromPath(string $path): ?string
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
return $this->extensionMap->lookupMimeType($extension);
}
public function detectMimeTypeFromFile(string $path): ?string
{
return @$this->finfo->file($path) ?: null;
}
public function detectMimeTypeFromBuffer(string $contents): ?string
{
return @$this->finfo->buffer($this->takeSample($contents)) ?: null;
}
private function takeSample(string $contents): string
{
if ($this->bufferSampleSize === null) {
return $contents;
}
return (string) substr($contents, 0, $this->bufferSampleSize);
}
public function lookupExtension(string $mimetype): ?string
{
return $this->extensionMap instanceof ExtensionLookup
? $this->extensionMap->lookupExtension($mimetype)
: null;
}
public function lookupAllExtensions(string $mimetype): array
{
return $this->extensionMap instanceof ExtensionLookup
? $this->extensionMap->lookupAllExtensions($mimetype)
: [];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
interface MimeTypeDetector
{
/**
* @param string|resource $contents
*/
public function detectMimeType(string $path, $contents): ?string;
public function detectMimeTypeFromBuffer(string $contents): ?string;
public function detectMimeTypeFromPath(string $path): ?string;
public function detectMimeTypeFromFile(string $path): ?string;
}

View File

@ -0,0 +1,30 @@
<?php
namespace League\MimeTypeDetection;
class OverridingExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
{
/**
* @var ExtensionToMimeTypeMap
*/
private $innerMap;
/**
* @var string[]
*/
private $overrides;
/**
* @param array<string, string> $overrides
*/
public function __construct(ExtensionToMimeTypeMap $innerMap, array $overrides)
{
$this->innerMap = $innerMap;
$this->overrides = $overrides;
}
public function lookupMimeType(string $extension): ?string
{
return $this->overrides[$extension] ?? $this->innerMap->lookupMimeType($extension);
}
}

2
vendor/services.php vendored
View File

@ -1,5 +1,5 @@
<?php
// This file is automatically generated at:2023-10-09 17:27:20
// This file is automatically generated at:2023-10-14 14:37:05
declare (strict_types = 1);
return array (
0 => 'think\\app\\Service',

View File

@ -0,0 +1,3 @@
composer.lock
/.idea
/vendor

View File

@ -0,0 +1,5 @@
# think-filesystem for ThinkPHP6.1
## 安装
> composer require topthink/think-filesystem

View File

@ -0,0 +1,33 @@
{
"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": {
"topthink/framework": "^6.1|^8.0",
"league/flysystem": "^2.0"
},
"require-dev": {
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^8.0"
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@ -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>

View File

@ -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');
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,130 @@
<?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\Filesystem;
use League\Flysystem\FilesystemAdapter;
use League\Flysystem\UnableToSetVisibility;
use League\Flysystem\UnableToWriteFile;
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);
}
abstract protected function createAdapter(): FilesystemAdapter;
protected function createFilesystem(FilesystemAdapter $adapter): Filesystem
{
$config = array_intersect_key($this->config, array_flip(['visibility', 'disable_asserts', 'url']));
return new Filesystem($adapter, $config);
}
/**
* 获取文件完整路径
* @param string $path
* @return string
*/
public function path(string $path): string
{
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->put($path, $stream, $options);
if (is_resource($stream)) {
fclose($stream);
}
return $result ? $path : false;
}
protected function put(string $path, $contents, array $options = [])
{
try {
$this->writeStream($path, $contents, $options);
} catch (UnableToWriteFile|UnableToSetVisibility $e) {
return false;
}
return true;
}
public function __call($method, $parameters)
{
return $this->filesystem->$method(...$parameters);
}
}

View File

@ -0,0 +1,98 @@
<?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\FilesystemAdapter;
use League\Flysystem\Local\LocalFilesystemAdapter;
use League\Flysystem\PathNormalizer;
use League\Flysystem\PathPrefixer;
use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
use League\Flysystem\Visibility;
use League\Flysystem\WhitespacePathNormalizer;
use think\filesystem\Driver;
class Local extends Driver
{
/**
* 配置参数
* @var array
*/
protected $config = [
'root' => '',
];
/**
* @var PathPrefixer
*/
protected $prefixer;
/**
* @var PathNormalizer
*/
protected $normalizer;
protected function createAdapter(): FilesystemAdapter
{
$visibility = PortableVisibilityConverter::fromArray(
$this->config['permissions'] ?? [],
$this->config['visibility'] ?? Visibility::PRIVATE
);
$links = ($this->config['links'] ?? null) === 'skip'
? LocalFilesystemAdapter::SKIP_LINKS
: LocalFilesystemAdapter::DISALLOW_LINKS;
return new LocalFilesystemAdapter(
$this->config['root'],
$visibility,
$this->config['lock'] ?? LOCK_EX,
$links
);
}
protected function prefixer()
{
if (!$this->prefixer) {
$this->prefixer = new PathPrefixer($this->config['root'], DIRECTORY_SEPARATOR);
}
return $this->prefixer;
}
protected function normalizer()
{
if (!$this->normalizer) {
$this->normalizer = new WhitespacePathNormalizer();
}
return $this->normalizer;
}
/**
* 获取文件访问地址
* @param string $path 文件路径
* @return string
*/
public function url(string $path): string
{
$path = $this->normalizer()->normalizePath($path);
if (isset($this->config['url'])) {
return $this->concatPathToUrl($this->config['url'], $path);
}
return parent::url($path);
}
public function path(string $path): string
{
return $this->prefixer()->prefixPath($path);
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace think\tests;
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\Config;
use think\Container;
use think\Filesystem;
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'));
}
}

View File

@ -0,0 +1,2 @@
<?php