zenstruck/filesystem

league/flysystem 的包装器,具有替代 API 和附加功能。

资助包维护!
kbond

1.x-dev 2024-08-16 00:33 UTC

This package is auto-updated.

Last update: 2024-09-16 00:40:32 UTC


README

CI codecov

此库是对优秀的 league/flysystem 文件存储抽象 库的包装。它提供了一个 替代 API,以下是一些主要变更:

  1. 主要区别在于 DirectoryFileImage 对象的概念。这些都是单个 文件系统节点 的包装器,提供信息、元数据以及更多功能。它们可以传递(即发送到您的模板)或甚至用作 Doctrine 类型
  2. 合并某些 Flysystem 方法。例如,delete() 删除文件和目录,write() 可以写入字符串和流(+更多)。
  3. 简化了将文件系统文件用作 真实、本地文件 的使用。许多第三方库在处理文件时需要本地文件。

此外,还提供了以下功能:

  1. 文件系统 包装器 以添加附加功能(例如 MultiFilesystemLoggableFilesystem)。
  2. 强大的 测试辅助工具
  3. ZipFile/TarFile 代表一个本地 zip/tar(.gz/bz2) 文件,它同时作为文件系统 真实文件。
  4. Doctrine 集成.
  5. Symfony 集成

安装

composer require zenstruck/filesystem

API

文件系统

/** @var \Zenstruck\Filesystem $filesystem */

// read operations
$filesystem->has('some/path'); // bool
$filesystem->node('some/path'); // Zenstruck\Filesystem\Node\File|Zenstruck\Filesystem\Node\Directory or throws NodeNotFound
$filesystem->file('some/path.txt'); // Zenstruck\Filesystem\Node\File or throws NodeNotFound or NodeTypeMismatch (if exists but not a file)
$filesystem->image('some/path.png'); // Zenstruck\Filesystem\Node\File\Image or throws NodeNotFound or NodeTypeMismatch (if exists but not an image)
$filesystem->directory('some/path'); // Zenstruck\Filesystem\Node\Directory or throws NodeNotFound or NodeTypeMismatch (if exists but not a directory)

// write operations (returns Zenstruck\Filesystem\File)
$filesystem->write('some/path.txt', 'string contents'); // write a string
$filesystem->write('some/path.txt', $resource); // write a resource
$filesystem->write('some/path.txt', new \SplFileInfo('path/to/local/file.txt')); // write a local file
$filesystem->write('some/path.txt', $file); // write a Zenstruck\Filesystem\Node\File

$filesystem->copy('from/file.txt', 'dest/file.txt'); // Zenstruck\Filesystem\Node\File (dest/file.txt)

$filesystem->move('from/file.txt', 'dest/file.txt'); // Zenstruck\Filesystem\Node\File (dest/file.txt)

$filesystem->delete('some/file.txt'); // returns self
$filesystem->delete('some/directory'); // returns self

// mkdir operations (returns Zenstruck\Filesystem\Node\Directory)
$filesystem->mkdir('some/directory'); // create an empty directory
$filesystem->mkdir('some/prefix', $directory); // create directory with files from Zenstruck\Filesystem\Node\Directory
$filesystem->mkdir('some/prefix', new \SplFileInfo('path/to/local/directory')); // create directory with files from local directory

$filesystem->chmod('some/file.txt', 'private'); // Zenstruck\Filesystem\Node (some/file.txt)

// utility methods
$filesystem->name(); // string - human-readable name for the filesystem

节点

接口:Zenstruck\Filesystem\Node

/** @var \Zenstruck\Filesystem\Node $node */

$node->path(); // Zenstruck\Filesystem\Node\Path
$node->path()->toString(); // string - the full path
(string) $node->path(); // same as above
$node->path()->name(); // string - filename with extension
$node->path()->basename(); // string - filename without extension
$node->path()->extension(); // string|null - file extension
$node->path()->dirname(); // string - the parent directory

$node->dsn(); // Zenstruck\Filesystem\Node\Dsn
$node->dsn()->toString(); // string - <filesystem-name>://<full-path>
(string) $node->dsn(); // same as above
$node->dsn()->path(); // Zenstruck\Filesystem\Node\Path
$node->dsn()->filesystem(); // string - name of the filesystem this node belongs to

$node->directory(); // Zenstruck\Filesystem\Node\Directory|null - parent directory object

$node->visibility(); // string - ie "public" or "private"
$node->lastModified(); // \DateTimeImmutable (in currently configured timezone)

$node->isDirectory(); // bool
$node->isFile(); // bool
$node->isImage(); // bool

$node->exists(); // bool
$node->ensureExists(); // static or throws NodeNotFound

$node->refresh(); // static and clears any cached metadata

$node->ensureDirectory(); // Zenstruck\Filesystem\Node\Directory or throws NodeTypeMismatch (if not a directory)
$node->ensureFile(); // Zenstruck\Filesystem\Node\File or throws NodeTypeMismatch (if not a file)
$node->ensureImage(); // Zenstruck\Filesystem\Node\Image or throws NodeTypeMismatch (if not an image)

文件

接口:Zenstruck\Filesystem\Node\File(继承自 Node)。

/** @var \Zenstruck\Filesystem\Node\File $file */

$file->contents(); // string - the file's contents

$file->stream(); // \Zenstruck\Stream - wrapper for a resource

$file->read(); // "raw" resource

$file->size(); // int

$file->guessExtension(); // string|null - returns extension if available or attempts to guess from mime-type

$file->checksum(); // string - using FilesystemAdapter's default algorithm
$file->checksum('md5'); // string - specify the algorithm

$file->publicUrl(); // string (needs to be configured)
$file->temporaryUrl(new \DateTimeImmutable('+30 minutes')); // string - expiring url (needs to be configured)
$file->temporaryUrl('+30 minutes'); // equivalent to above

$file->tempFile(); // \SplFileInfo - temporary local file that's deleted at the end of the script

注意

有关 File::tempFile() 的更多详细信息,请参阅 zenstruck/temp-file

注意

有关 File::stream() 的更多详细信息,请参阅 zenstruck/stream

PendingFile

类:Zenstruck\Filesystem\Node\File\PendingFile(继承自 \SplFileInfo 并实现 File)。

use Zenstruck\Filesystem\Node\File\PendingFile;

$file = new PendingFile('/path/to/local/file.txt');
$file->path()->toString(); // "/path/to/local/file.txt"

/** @var \Symfony\Component\HttpFoundation\File\UploadedFile $uploadedFile */

$file = new PendingFile($uploadedFile);
$file->path()->toString(); // $uploadedFile->getClientOriginalName()

图像

接口:Zenstruck\Filesystem\Node\File\Image(继承自 File)。

/** @var \Zenstruck\Filesystem\Node\File\Image $image */

$image->dimensions(); // Zenstruck\Image\Dimensions
$image->dimensions()->height(); // int
$image->dimensions()->width(); // int
$image->dimensions()->pixels(); // int
$image->dimensions()->aspectRatio(); // float
$image->dimensions()->isSquare(); // bool
$image->dimensions()->isPortrait(); // bool
$image->dimensions()->isLandscape(); // bool

$image->exif(); // array - image exif data if available
$image->iptc(); // array - image iptc data if available

$thumbHash = $image->thumbHash(); // Zenstruck\Image\Hash\ThumbHash (requires srwiez/thumbhash)
$thumbHash->dataUri(); // string - data uri for the thumb
$thumbHash->key(); // string - hash for the thumb (to store in database/cache)

$image->transformUrl('filter-name'); // string (needs to be configured)
$image->transformUrl(['w' => 100, 'h' => 50]); // string (needs to be configured)

$image->transform(
    function(ManipulationObject $image) {
        // make manipulations

        return $image;
    }
); // PendingImage

注意

有关 Image::transform()Image::thumbHash() 的更多详细信息,请参阅 zenstruck/image

PendingImage

类:Zenstruck\Filesystem\Node\File\Image\PendingImage(继承自 PendingFile 并实现 Image)。

use Zenstruck\Filesystem\Node\File\Image\PendingImage;

$image = new PendingImage('/path/to/local/file.txt');
$image = new PendingImage($symfonyUploadedFile);

// transform and overwrite
$image->transformInPlace(
    function(ManipulationObject $image) {
        // make manipulations

        return $image;
    }
); // self

目录

接口:Zenstruck\Filesystem\Node\Directory(继承自 Node)。

/** @var Zenstruck\Filesystem\Node\Directory $directory */

// iterate over nodes (non-recursive)
foreach ($directory as $node) {
    /** @var Zenstruck\Filesystem\Node $node */
}

// iterate over only files (non-recursive)
foreach ($directory->files() as $file) {
    /** @var Zenstruck\Filesystem\Node\File $file */
}

// iterate over only directories (non-recursive)
foreach ($directory->directories() as $dir) {
    /** @var Zenstruck\Filesystem\Node\Directory $dir */
}

// recursively iterate
foreach ($directory->recursive() as $node) {
    /** @var Zenstruck\Filesystem\Node $node */
}

// advanced filter
$directories = $directory
    ->recursive()
    ->files()
    ->largerThan('10M')
    ->smallerThan('1G')
    ->olderThan('30 days ago')
    ->newerThan('20 days ago')
    ->matchingFilename('*.twig')
    ->notMatchingFilename('*.txt.twig')
    ->matchingPath('/files/')
    ->notMatchingPath('/exclude/')
    ->filter(function(File $file) { // custom filter
        if ($someCondition) {
            return false; // exclude
        }

        return true; // include
    })
;

// get first matching node
$directories->first(); // null|\Zenstruck\Filesystem\Node

注意

大多数高级过滤器需要 symfony/findercomposer require symfony/finder)。

文件系统

FlysystemFilesystem

use Zenstruck\Filesystem\FlysystemFilesystem;

/** @var \League\Flysystem\FilesystemOperator $operator */
/** @var \League\Flysystem\FilesystemAdapter $adapter */

// create from an already configured Flysystem Filesystem Operator
$filesystem = new FlysystemFilesystem($operator);

// create from an already configured Flysystem Filesystem Adapter
$filesystem = new FlysystemFilesystem($operator);

// create for local directory
$filesystem = new FlysystemFilesystem('/path/to/local/dir');

// create for dsn (see available DSNs below)
$filesystem = new FlysystemFilesystem('flysystem+ftp://user:pass@host.com:21/root');

文件系统 DSN

ScopedFilesystem

use Zenstruck\Filesystem\ScopedFilesystem;

/** @var \Zenstruck\Filesystem $primaryFilesystem */

$scopedFilesystem = new ScopedFilesystem($primaryFilesystem, 'some/prefix');

// paths are prefixed
$scopedFilesystem
    ->write('file.txt', 'content')
    ->path()->toString(); // "some/prefix/file.txt"
;

// prefix is stripped from path
$scopedFilesystem
    ->write('some/prefix/file.txt', 'content')
    ->path()->toString(); // "some/prefix/file.txt"
;

MultiFilesystem

use Zenstruck\Filesystem\MultiFilesystem;

/** @var \Zenstruck\Filesystem $filesystem1 */
/** @var \Zenstruck\Filesystem $filesystem2 */

$filesystem = new MultiFilesystem([
    'filesystem1' => $filesystem1,
    'filesystem2' => $filesystem2,
]);

// prefix paths with a "scheme" as the filesystem's name
$filesystem->file('filesystem1://some/file.txt'); // File from "filesystem1"
$filesystem->file('filesystem2://another/file.txt'); // File from "filesystem2"

// can copy and move across filesystems
$filesystem->copy('filesystem1://file.txt', 'filesystem2://file.txt');
$filesystem->move('filesystem1://file.txt', 'filesystem2://file.txt');

// set a default filesystem for when no scheme is set
$filesystem = new MultiFilesystem(
    [
        'filesystem1' => $filesystem1,
        'filesystem2' => $filesystem2,
    ],
    default: 'filesystem2'
);

$filesystem->file('another/file.txt'); // File from "filesystem2"

LoggableFilesystem

注意

需要 psr/log-implementation

use Zenstruck\Filesystem\LoggableFilesystem;
use Zenstruck\Filesystem\Operation;
use Psr\Log\LogLevel;

/** @var \Zenstruck\Filesystem $inner */
/** @var \Psr\Log\LoggerInterface $logger */

$filesystem = new LoggableFilesystem($inner, $logger);

// operations are logged
$filesystem->write('file.txt', 'content'); // logged as '[info] Writing "string" to "file.txt" on filesystem "<filesystem-name>"'

// customize the log levels for each operation
$filesystem = new LoggableFilesystem($inner, $logger, [
    Operation::READ => false, // disable logging read operations
    Operation::WRITE => LogLevel::DEBUG,
    Operation::MOVE => LogLevel::ALERT,
    Operation::COPY => LogLevel::CRITICAL,
    Operation::DELETE => LogLevel::EMERGENCY,
    Operation::CHMOD => LogLevel::ERROR,
    Operation::MKDIR => LogLevel::NOTICE,
]);

EventDispatcherFilesystem

注意

需要 psr/event-dispatcher-implementation

use Zenstruck\Filesystem\Event\EventDispatcherFilesystem;
use Zenstruck\Filesystem\Operation;

/** @var \Zenstruck\Filesystem $inner */
/** @var \Psr\EventDispatcher\EventDispatcherInterface $dispatcher */

$filesystem = new EventDispatcherFilesystem($inner, $dispatcher, [
    // set these to false or exclude to disable dispatching operation's event
    Operation::WRITE => true,
    Operation::COPY => true,
    Operation::MOVE => true,
    Operation::DELETE => true,
    Operation::CHMOD => true,
    Operation::MKDIR => true,
]);

$filesystem->write('foo', 'bar'); // PreWriteEvent/PostWriteEvent dispatched
$filesystem->mkdir('bar'); // PreMkdirEvent/PostMkdirEvent dispatched
$filesystem->chmod('foo', 'public'); // PreChmodEvent/PostChmodEvent dispatched
$filesystem->copy('foo', 'file.png'); // PreCopyEvent/PostCopyEvent dispatched
$filesystem->delete('foo'); // PreDeleteEvent/PostDeleteEvent dispatched
$filesystem->move('file.png', 'file2.png'); // PreMoveEvent/PostMoveEvent dispatched
;

注意

查看事件类以了解它们提供了哪些功能。

注意

可以操作 Pre*Event 属性。

ZipFile

注意

需要 league/flysystem-ziparchivecomposer require league/flysystem-ziparchive)。

这是一个特殊文件系统,包装本地 zip 存档。它既作为 Filesystem 对象也作为 \SplFileInfo 对象。

use Zenstruck\Filesystem\Archive\ZipFile;

$archive = new ZipFile('/local/path/to/archive.zip');
$archive->file('some/file.txt');
$archive->write('another/file.txt', 'content');

(string) $archive; // /local/path/to/archive.zip

如果没有指定路径,将创建一个临时存档文件(脚本结束时删除)。

use Zenstruck\Filesystem\Archive\ZipFile;

$archive = new ZipFile();

$archive->write('some/file.txt', 'content');
$archive->write('another/file.txt', 'content');

(string) $archive; // /tmp/...

可以通过 事务 对写操作进行排队和提交。

use Zenstruck\Filesystem\Archive\ZipFile;

$archive = new ZipFile();

$archive->beginTransaction(); // start the transaction
$archive->write('some/file.txt', 'content');
$archive->write('another/file.txt', 'content');
$archive->commit(); // actually writes the above files

// optionally pass a progress callback to commit
$archive->commit(function() use ($progress) { // callback is called at most, 100 times
    $progress->advance();
});

静态辅助函数,用于快速创建 zip 存档。

use Zenstruck\Filesystem\Archive\ZipFile;

// compress a local file
$zipFile = ZipFile::compress(new \SplFileInfo('/some/local/file.txt')); // ZipFile (temp file deleted on script end)

// compress a local directory (all files (recursive) in "some/local/directory" are added to archive)
$zipFile = ZipFile::compress(new \SplFileInfo('some/local/directory'));

/** @var \Zenstruck\Filesystem\Node\File $file */

// compress a filesystem file
$zipFile = ZipFile::compress($file);

/** @var \Zenstruck\Filesystem\Node\Directory $directory */

// compress a filesystem directory
$zipFile = ZipFile::compress($directory);

// compress several local/filesystem files
$zipFile = ZipFile::compress([
    new \SplFileInfo('/some/local/file.txt'),
    $file,
    'customize/path.txt' => $file, // use a string array key to set the path for the file
]);

// customize the output filename (will not be deleted at end of script)
$zipFile = ZipFile::compress(..., filename: 'path/to/archive.zip');

TarFile

注意

需要 league/flysystem-read-onlycomposer require league/flysystem-read-only)。

这是一个特殊文件系统,包装现有的本地 tar(.gz/bz2) 存档。它既作为 只读Filesystem 对象也作为 \SplFileInfo 对象。

use Zenstruck\Filesystem\Archive\TarFile;

$archive = new TarFile('/local/path/to/archive.tar');
$archive = new TarFile('/local/path/to/archive.tar.gz');
$archive = new TarFile('/local/path/to/archive.tar.bz2');

$archive->file('some/file.txt'); // \Zenstruck\Filesystem\Node\File

TestFilesystem

该文件系统包装另一个文件系统,并为测试提供断言。在使用 PHPUnit 时,这些断言将被转换为 PHPUnit 断言。

注意

使用断言需要 zenstruck/assertcomposer require --dev zenstruck/assert)。

use Zenstruck\Filesystem\Test\TestFilesystem;
use Zenstruck\Filesystem\Test\Node\TestDirectory;
use Zenstruck\Filesystem\Test\Node\TestFile
use Zenstruck\Filesystem\Test\Node\TestImage;

/** @var \Zenstruck\Filesystem $filesystem */

$filesystem = new TestFilesystem($filesystem);

$filesystem
    ->assertExists('foo')
    ->assertNotExists('invalid')
    ->assertFileExists('file1.txt')
    ->assertDirectoryExists('foo')
    ->assertImageExists('symfony.png')
    ->assertSame('symfony.png', 'fixture://symfony.png')
    ->assertNotSame('file1.txt', 'fixture://symfony.png')
    ->assertDirectoryExists('foo', function(TestDirectory $dir) {
        $dir
            ->assertCount(4)
            ->files()->assertCount(2)
        ;

        $dir
            ->recursive()
            ->assertCount(5)
            ->files()->assertCount(3)
        ;
    })
    ->assertFileExists('file1.txt', function(TestFile $file) {
        $file
            ->assertVisibilityIs('public')
            ->assertChecksum($file->checksum()->toString())
            ->assertContentIs('contents1')
            ->assertContentIsNot('foo')
            ->assertContentContains('1')
            ->assertContentDoesNotContain('foo')
            ->assertMimeTypeIs('text/plain')
            ->assertMimeTypeIsNot('foo')
            ->assertLastModified('2023-01-01 08:54')
            ->assertLastModified(function(\DateTimeInterface $actual) {
                // ...
            })
            ->assertSize(9)
        ;
    })
    ->assertImageExists('symfony.png', function(TestImage $image) {
        $image
            ->assertHeight(678)
            ->assertWidth(563)
        ;
    })
;

$file = $filesystem->realFile('symfony.png'); // \SplFileInfo('/tmp/symfony.png') - deleted at the end of the script

InteractsWithFilesystem

在单元测试中使用 InteractsWithFilesystem 特性可以快速提供内存中的文件系统。

注意

默认情况下,需要 league/flysystem-memorycomposer require --dev league/flysystem-memory)。

use PHPUnit\Framework\TestCase;
use Zenstruck\Filesystem\Test\InteractsWithFilesystem;

class MyTest extends TestCase
{
    use InteractsWithFilesystem;

    public function test_1(): void
    {
        $filesystem = $this->filesystem(); // instance of TestFilesystem wrapping an in-memory filesystem
        $filesystem->write('file.txt', 'content');
        $filesystem->assertExists('file.txt');
    }
}

FilesystemProvider

要为测试提供自己的文件系统,让测试(或基测试用例)实现 FilesystemProvider

use PHPUnit\Framework\TestCase;
use Zenstruck\Filesystem;
use Zenstruck\Filesystem\Test\InteractsWithFilesystem;
use Zenstruck\Filesystem\Test\FilesystemProvider;

class MyTest extends TestCase implements FilesystemProvider
{
    use InteractsWithFilesystem;

    public function test_1(): void
    {
        $filesystem = $this->filesystem(); // instance of TestFilesystem wrapping the AdapterFilesystem defined below
        $filesystem->write('file.txt', 'content');
        $filesystem->assertExists('file.txt');
    }

    public function createFilesystem(): Filesystem|FilesystemAdapter|string;
    {
        return '/some/temp/dir';
    }
}

注意

默认情况下,提供的文件系统在每次测试之前不会重置。请参阅 ResetFilesystem 以启用此行为。

FixtureFilesystemProvider

文件系统测试的一个常见需求是有一组已知存根文件,这些文件在测试中使用。让测试的(或基测试用例)实现 FixtureFilesystemProvider 以在测试中提供。

use PHPUnit\Framework\TestCase;
use Zenstruck\Filesystem;
use Zenstruck\Filesystem\Test\InteractsWithFilesystem;
use Zenstruck\Filesystem\Test\FixtureFilesystemProvider;

class MyTest extends TestCase implements FixtureFilesystemProvider
{
    use InteractsWithFilesystem;

    public function test_1(): void
    {
        $filesystem = $this->filesystem(); // instance of TestFilesystem wrapping a MultiFilesystem

        $filesystem->write('file.txt', 'content'); // accesses your test filesystem
        $filesystem->assertExists('file.txt');
        $filesystem->copy('fixture://some/file.txt', 'file.txt'); // copy a fixture to your test filesystem
    }

    public function createFixtureFilesystem(): Filesystem|FilesystemAdapter|string;
    {
        return __DIR__.'/../fixtures';
    }
}

注意

如果可用 league/flysystem-read-only 适配器,它将用于包装存根适配器,以确保您不会意外覆盖/删除存根文件(composer require --dev league/flysystem-read-only)。

ResetFilesystem

如果使用自己的 FilesystemProvider,可以使用 ResetFilesystem 特性在每次测试之前清除您的文件系统。

use PHPUnit\Framework\TestCase;
use Zenstruck\Filesystem;
use Zenstruck\Filesystem\Test\ResetFilesystem
use Zenstruck\Filesystem\Test\InteractsWithFilesystem;
use Zenstruck\Filesystem\Test\FilesystemProvider;

class MyTest extends TestCase implements FilesystemProvider
{
    use InteractsWithFilesystem, ResetFilesystem;

    public function test_1(): void
    {
        $this->filesystem()->write('file.txt', 'content')
        $this->filesystem()->assertExists('file.txt')
    }

    public function test_2(): void
    {
        $this->filesystem()->assertNotExists('file.txt'); // file created in test_1 was deleted before this test
    }

    public function createFilesystem(): Filesystem|FilesystemAdapter|string;
    {
        return '/some/temp/dir';
    }
}

Symfony 集成

响应

提供有用的自定义 Symfony 响应。

FileResponse

将文件系统 File 作为响应发送。

use Zenstruck\Filesystem\Symfony\HttpFoundation\FileResponse;

/** @var \Zenstruck\Filesystem\File $file */

$response = new FileResponse($file); // auto-adds content-type/last-modified headers

// create inline/attachment responses
$response = FileResponse::attachment($file); // auto names by the filename (file.txt)
$response = FileResponse::inline($file); // auto names by the filename (file.txt)

// customize the filename used for the content-disposition header
$response = FileResponse::attachment($file, 'different-name.txt');
$response = FileResponse::inline($file, 'different-name.txt');

ArchiveResponse

将 zip 文件发送作为响应。可以使用本地文件、本地目录、File 的实例或 Directory 的实例创建。

use Zenstruck\Filesystem\Symfony\HttpFoundation\ArchiveResponse;

/** @var \SplFileInfo|\Zenstruck\Filesystem\Node\File|\Zenstruck\Filesystem\Node\Directory $what */

$response = ArchiveResponse::zip($what);
$response = ArchiveResponse::zip($what, 'data.zip'); // customize the content-disposition name (defaults to archive.zip)

验证器

提供了 PendingFilePendingImage 验证器。约束与 Symfony 本地 FileImage 约束的 API 相同。

use Zenstruck\Filesystem\Symfony\Validator\PendingFileConstraint;
use Zenstruck\Filesystem\Symfony\Validator\PendingImageConstraint;

/** @var \Symfony\Component\Validator\Validator\ValidatorInterface $validator */
/** @var \Zenstruck\Filesystem\Node\File $file */
/** @var \Zenstruck\Filesystem\Node\File\Image $image */

$validator->validate($file, new PendingFileConstraint(maxSize: '1M')));

$validator->validate($image, new PendingImageConstraint(maxWidth: 200, maxHeight: 200)));

捆绑包

配置

服务

序列化器

表单类型

PendingFileType
PendingImageType

命令

zenstruck:filesystem:purge

路由

公共URL路由
临时URL路由
转换URL路由
RouteTransformUrlGenerator

Doctrine 集成

功能/集成测试

TODO:关于 Mock::pendingFile()/pendingImage() 的说明。TODO:关于 Foundry\LazyMock::pendingFile()/pendingImage() 的说明。

测试性能

完整默认包配置

zenstruck_filesystem:

    # Filesystem configurations
    filesystems:

        # Prototype
        name:

            # Flysystem adapter DSN or, if prefixed with "@" flysystem adapter service id
            dsn:                  ~ # Required

                # Examples:
                # - '%kernel.project_dir%/public/files'
                # - 'flysystem+ftp://foo:bar@example.com/path'
                # - 'flysystem+s3://accessKeyId:accessKeySecret@bucket/prefix#us-east-1'
                # - 'static-in-memory'
                # - 'scoped:<name>:<prefix>'
                # - '@my_adapter_service'

            # Extra global adapter filesystem config
            config:               []

            # Lazily load the filesystem when the first call occurs
            lazy:                 true

            # Public URL generator for this filesystem
            public_url:

                # URL prefix or multiple prefixes to use for this filesystem (can be an array)
                prefix:

                    # Examples:
                    # - /files
                    # - 'https://cdn1.example.com'
                    # - 'https://cdn2.example.com'

                # Service id for a League\Flysystem\UrlGeneration\PublicUrlGenerator
                service:              null

                # Generate with a route
                route:

                    # Route name
                    name:                 ~ # Required

                    # Route parameters
                    parameters:           []

                    # Sign by default?
                    sign:                 false

                    # Default expiry
                    expires:              null # Example: '+ 30 minutes'

                # Enables cache busting for public urls
                version:
                    enabled:              false

                    # The metadata to use for versioning
                    metadata:             last_modified # One of "last_modified"; "size"; "checksum"

                    # The query parameter to use for versioning
                    parameter:            v

            # Temporary URL generator for this filesystem
            temporary_url:

                # Service id for a League\Flysystem\UrlGeneration\TemporaryUrlGenerator
                service:              null

                # Generate with a route
                route:

                    # Route name
                    name:                 ~ # Required

                    # Route parameters
                    parameters:           []

            # Image Transform URL generator for this filesystem
            image_url:

                # Service id for a League\Flysystem\UrlGeneration\PublicUrlGenerator
                service:              null

                # Generate with a route
                route:

                    # Route name
                    name:                 ~ # Required

                    # Route parameters
                    parameters:           []

                    # Sign by default?
                    sign:                 false

                    # Default expiry
                    expires:              null # Example: '+ 30 minutes'

            # Dispatch filesystem operation events
            events:
                enabled:              false
                write:                true
                delete:               true
                mkdir:                true
                chmod:                true
                copy:                 true
                move:                 true

            # Log filesystem operations
            log:
                enabled:              true
                read:                 debug # One of false; "emergency"; "alert"; "critical"; "error"; "warning"; "notice"; "info"; "debug"
                write:                info # One of false; "emergency"; "alert"; "critical"; "error"; "warning"; "notice"; "info"; "debug"
                move:                 ~ # One of false; "emergency"; "alert"; "critical"; "error"; "warning"; "notice"; "info"; "debug"
                copy:                 ~ # One of false; "emergency"; "alert"; "critical"; "error"; "warning"; "notice"; "info"; "debug"
                delete:               ~ # One of false; "emergency"; "alert"; "critical"; "error"; "warning"; "notice"; "info"; "debug"
                chmod:                ~ # One of false; "emergency"; "alert"; "critical"; "error"; "warning"; "notice"; "info"; "debug"
                mkdir:                ~ # One of false; "emergency"; "alert"; "critical"; "error"; "warning"; "notice"; "info"; "debug"

            # If true, and using the ResetFilesystem trait
            # in your KernelTestCase's, delete this filesystem
            # before each test.
            reset_before_tests:   false

    # Default filesystem name used to autowire Zenstruck\Filesystem
    default_filesystem:   null

    # Doctrine configuration
    doctrine:
        enabled:              true

        # Global lifecycle events (can be disabled on a property-by-property basis)
        lifecycle:

            # Whether to auto load file type columns during object load
            autoload:             true

            # Whether to delete files on object removal
            delete_on_remove:     true

向后兼容承诺

此库遵循 Symfony的BC承诺,以下为例外情况

  1. Zenstruck/Filesystem 及其任何实现被视为 内部 用于 实现/扩展
  2. Zenstruck/Filesystem/Node 及其任何实现被视为 内部 用于 实现/扩展