jardisadapter/filesystem
Composer 安装命令:
composer require jardisadapter/filesystem
包简介
Filesystem abstraction for PHP covering local and S3-compatible storage — unified read, write, stream, and visibility API without Flysystem or the AWS SDK; a building block of the open-source foundation that Jardis-generated DDD code runs on
关键字:
README 文档
README
Part of Jardis — the Domain-Driven Design platform for PHP. You model your domain; Jardis generates the production-ready hexagonal code (DTOs, Command/Query handlers, repositories, persistence). This package is part of the open-source foundation that generated code runs on.
File operations without the framework. A lean filesystem abstraction for PHP covering local and S3-compatible storage — designed for applications that store uploads, manage assets, or sync backups. No Flysystem, no AWS SDK, no dependency bloat. Just cURL and PHP builtins.
Why This Filesystem?
- Two classes are enough —
FilesystemService+ a config object, nothing else - Multiple instances — local for uploads, S3 for backups, both in the same project
- Atomic handler pipeline — each operation is its own invokable, orchestrated by closures
- Stream support — read and write large files without memory overhead
- S3 without the SDK — AWS Signature v4 via cURL, works with MinIO, DigitalOcean Spaces, etc.
- Security hardened — path traversal protection, symlink containment, XXE prevention, secret masking
- 79% test coverage — integration tests against real MinIO, not mocks
Installation
composer require jardisadapter/filesystem
Quick Start
Local Filesystem
use JardisAdapter\Filesystem\FilesystemService; $service = new FilesystemService(); $fs = $service->local('/var/app/storage'); $fs->write('uploads/photo.jpg', $imageData); $content = $fs->read('uploads/photo.jpg');
S3-Compatible Storage
$fs = $service->s3( bucket: 'my-bucket', region: 'eu-central-1', key: 'AKIAEXAMPLE', secret: 'wJalrXUtnFEMI/K7MDENG...', ); $fs->write('backups/dump.sql', $sqlDump);
Multiple Backends
$uploads = $service->local('/storage/uploads'); $backups = $service->s3('company-backups', 'eu-central-1', $env('AWS_KEY'), $env('AWS_SECRET')); // Upload lokal speichern $uploads->write('invoice-2026.pdf', $pdf); // Backup auf S3 sichern $backups->write('daily/invoice-2026.pdf', $pdf);
Advanced Configuration
For custom permissions, symlink settings, or other advanced options — use create() with a config object:
use JardisAdapter\Filesystem\Config\LocalConfig; $fs = $service->create(new LocalConfig( root: '/storage/uploads', filePermissions: 0600, dirPermissions: 0700, followSymlinks: false, ));
File Operations
$fs->write('file.txt', 'content'); $fs->read('file.txt'); // string $fs->exists('file.txt'); // bool $fs->delete('file.txt'); $fs->copy('source.txt', 'target.txt'); $fs->move('old.txt', 'new.txt'); $fs->size('file.txt'); // int (bytes) $fs->lastModified('file.txt'); // int (unix timestamp) $fs->mimeType('file.txt'); // string
Stream Support
For large files — no memory overhead:
// Write from stream $stream = fopen('/tmp/video.mp4', 'rb'); $fs->writeStream('videos/intro.mp4', $stream); fclose($stream); // Read as stream $stream = $fs->readStream('videos/intro.mp4'); while (!feof($stream)) { $chunk = fread($stream, 8192); // process chunk... } fclose($stream);
Directory Operations
$fs->createDirectory('uploads/2026'); $fs->deleteDirectory('uploads/2025'); // recursive foreach ($fs->listContents('uploads', recursive: true) as $item) { echo $item->path(); // 'uploads/photo.jpg' echo $item->size(); // 1048576 echo $item->lastModified(); // 1711929600 echo $item->isFile(); // true echo $item->isDirectory(); // false }
Visibility
Control file permissions (local: Unix chmod, S3: ACL):
$fs->setVisibility('public/logo.png', 'public'); $fs->setVisibility('private/secret.pdf', 'private'); $fs->getVisibility('public/logo.png'); // 'public'
Configuration
LocalConfig
new LocalConfig( root: '/var/app/storage', // required — validated via realpath() filePermissions: 0644, // new files (default: 0644) dirPermissions: 0755, // new directories (default: 0755) followSymlinks: true, // follow symlinks (default: true) publicFilePerms: 0644, // visibility 'public' files privateFilePerms: 0600, // visibility 'private' files publicDirPerms: 0755, // visibility 'public' directories privateDirPerms: 0700, // visibility 'private' directories )
S3Config
new S3Config( bucket: 'my-bucket', // required region: 'eu-central-1', // required key: 'AKIAEXAMPLE', // required secret: 'wJalrXUtnFEMI...', // required, masked in debug output endpoint: 'https://s3.amazonaws.com', // default: AWS (use custom for MinIO etc.) prefix: 'uploads/', // path prefix in bucket (default: '') )
The secret is protected with #[\SensitiveParameter] and masked in var_dump() / debug output.
Error Handling
All exceptions implement FilesystemExceptionInterface — catch one, catch all:
| Exception | When |
|---|---|
FileNotFoundException |
File or directory does not exist |
UnableToReadException |
Read failure (permissions, I/O, S3 auth) |
UnableToWriteException |
Write failure (permissions, disk full, S3) |
UnableToDeleteException |
Delete failure |
FilesystemException |
Base — path traversal, null byte, invalid config |
use JardisAdapter\Filesystem\Exception\FileNotFoundException; use JardisSupport\Contract\Filesystem\FilesystemExceptionInterface; try { $content = $fs->read('missing.txt'); } catch (FileNotFoundException $e) { // file does not exist } catch (FilesystemExceptionInterface $e) { // any other filesystem error }
Architecture
The user only sees FilesystemService + config objects. Internally, the orchestrator builds a pipeline of atomic invokable handlers — one __invoke per operation:
FilesystemService (implements FilesystemServiceInterface)
├── local(root): FilesystemInterface
├── s3(bucket, region, key, secret): FilesystemInterface
└── create(LocalConfig|S3Config): FilesystemInterface ← advanced
Filesystem (Orchestrator)
│
│ PathNormalizer — traversal + null byte protection
│
├── Local:
│ LocalFullPath (symlink containment via realpath)
│ + 16 atomic handlers: LocalRead, LocalWrite, LocalExists, ...
│
└── S3:
S3Signer (AWS Signature v4)
S3Request (shared cURL helper)
+ 16 atomic handlers: S3Read, S3Write, S3Exists, ...
Each handler is an invokable object (__invoke) — independently testable, replaceable, composable. The orchestrator extracts closures via ->__invoke(...) and stores only the closures. No handler object survives as a property.
Security
- Path traversal —
..segments and null bytes rejected before any I/O - Symlink containment —
realpath()check ensures resolved paths stay inside root - Root validation —
LocalConfigresolves root viarealpath()at construction time - XXE prevention —
LIBXML_NONETon all XML parsing (S3 responses) - Secret masking —
S3Config::$secretuses#[\SensitiveParameter]+__debugInfo() - Bucket wipe guard —
deleteDirectory('')with empty prefix is rejected
Contracts
The package implements interfaces from jardissupport/contract:
| Interface | Purpose |
|---|---|
FilesystemServiceInterface |
Factory: local(), s3() |
FilesystemInterface |
Full API (extends Reader + Writer) |
FilesystemReaderInterface |
Read-only subset — inject this for read-only contexts |
FilesystemWriterInterface |
Write-only subset |
// Inject read-only access public function __construct( private readonly FilesystemReaderInterface $storage, ) {}
Jardis Foundation Integration
In a Jardis DDD project, the filesystem is available via the resource chain:
$uploads = $this->getResource()->filesystem()->local('/storage/uploads'); $backups = $this->getResource()->filesystem()->s3( bucket: $env('FS_BACKUPS_BUCKET'), region: $env('FS_BACKUPS_REGION'), key: $env('FS_BACKUPS_KEY'), secret: $env('FS_BACKUPS_SECRET'), );
No singleton, no handler — the developer decides how many filesystem instances exist and how they are configured. The resource chain returns FilesystemServiceInterface.
Development
cp .env.example .env # One-time setup make install # Install dependencies make start # Start MinIO (S3 integration tests) make phpunit # Run tests (118 tests, starts MinIO automatically) make phpstan # Static analysis (Level 8) make phpcs # Coding standards (PSR-12)
Documentation
Full documentation, guides, and API reference:
docs.jardis.io/en/adapter/filesystem
License
MIT License — free for any use, including commercial.
AI-Assisted Development
This package ships with a skill for Claude Code, Cursor, Continue, and Aider. Install it in your consuming project:
composer require --dev jardis/dev-skills
More details: https://docs.jardis.io/en/skills
jardisadapter/filesystem 适用场景与选型建议
jardisadapter/filesystem 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 107 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 02 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「file」 「filesystem」 「stream」 「storage」 「s3」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 jardisadapter/filesystem 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 jardisadapter/filesystem 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 jardisadapter/filesystem 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A SDK for working with B2 cloud storage.
A PHP class providing static methods for reading, writing, copying, moving, and deleting files and directories, MIME type detection, image size detection, and file permission management
The file manager intended for using Laravel with CKEditor / TinyMCE / Colorbox
The flysystem adapter for yandex disk rest api.
A sleek PHP wrapper around rclone with Laravel-style fluent API syntax
Laravel Media Popup to upload/view the files
统计信息
- 总下载量: 107
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 43
- 依赖项目数: 1
- 推荐数: 1
其他信息
- 授权协议: MIT
- 更新时间: 2026-04-02