cloud-castle / file-system
Безопасная работа с файловой системой для PHP 8.1+: атомарная запись (случайный temp + rename), потоковое и ленивое чтение, символические/жёсткие ссылки, JSON, рекурсивные операции с каталогами, glob, временные пути и лексические операции с путями с защитой от path traversal. Суперсет возможностей S
Requires
- php: >=8.1
- ext-fileinfo: *
Requires (Dev)
- deptrac/deptrac: ^3.0 || ^4.0
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.75
- icanhazstring/composer-unused: ^0.9
- illuminate/filesystem: ^10.0 || ^11.0
- infection/infection: ^0.29 || ^0.33
- league/flysystem: ^3.0
- nette/utils: ^4.0
- php-parallel-lint/php-parallel-lint: ^1.4
- phpmd/phpmd: ^2.15
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^1.12 || ^2.1
- phpstan/phpstan-deprecation-rules: ^1.2 || ^2.0
- phpstan/phpstan-phpunit: ^1.4 || ^2.0
- phpstan/phpstan-strict-rules: ^1.6 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.5
- psalm/plugin-phpunit: ^0.19 || ^0.20
- rector/rector: ^1.2 || ^2.0
- roave/security-advisories: dev-latest
- spatie/temporary-directory: ^2.2
- squizlabs/php_codesniffer: ^3.12 || ^4.0
- symfony/filesystem: ^6.4 || ^7.0
- vimeo/psalm: ^6.0
- webmozart/assert: ^1.11
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-04 18:23:21 UTC
README
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano
CloudCastle FileSystem
Безопасная работа с файловой системой для PHP 8.1+: атомарная запись, потоковое и построчное чтение, символические/жёсткие ссылки, рекурсивные операции с каталогами, JSON, поиск по glob и лексические операции с путями с выявлением path traversal. Зависимости — только
phpиext-fileinfo.
Установка
composer require cloud-castle/file-system
Требуется PHP 8.1+ и расширение fileinfo.
Быстрый старт
<?php
use CloudCastle\FileSystem\FileSystem;
use CloudCastle\FileSystem\Path;
// Атомарная запись (временный файл + rename) — без частично записанных файлов.
FileSystem::write('/var/app/config/app.php', '<?php return ["debug" => false];');
$content = FileSystem::read('/var/app/config/app.php');
$sha256 = FileSystem::hash('/var/app/config/app.php');
$mime = FileSystem::mimeType('/var/app/config/app.php');
// Каталоги.
FileSystem::makeDirectory('/var/app/cache');
$files = FileSystem::allFiles('/var/app'); // рекурсивно, отсортировано
FileSystem::deleteDirectory('/var/app/cache'); // рекурсивно
// JSON, ленивое чтение и замена.
FileSystem::writeJson('/var/app/state.json', ['ready' => true]);
$state = FileSystem::readJson('/var/app/state.json');
foreach (FileSystem::lines('/var/log/app.log') as $line) { /* без загрузки в память */ }
// Ссылки, рекурсивное копирование, glob.
FileSystem::symlink('/var/app/current', '/var/app/releases/42');
FileSystem::copyDirectory('/var/app/skel', '/var/app/new');
$logs = FileSystem::glob('/var/log/*.log');
// Пути (без обращения к диску).
$path = Path::join('/var', 'app', 'config'); // «/var/app/config»
$safe = Path::normalize('/var/data/../../etc'); // «/etc» — виден выход за пределы
$inside = Path::isWithin('/var/app', '/var/app/x'); // true — защита от path traversal
$rel = Path::relative('/var/log', '/var/app'); // «../log»
$base = Path::commonBasePath('/var/www/a', '/var/www/b'); // «/var/www»
<?php
use CloudCastle\FileSystem\Directory;
use CloudCastle\FileSystem\Finder;
use CloudCastle\FileSystem\TemporaryDirectory;
// Текучий поиск: PHP-файлы глубже первого уровня, крупнее 1 КБ, отсортированные.
$found = Finder::create()
->files()
->name('*.php')
->notName('*.blade.php')
->depth(1)
->filter(static fn (\SplFileInfo $f): bool => $f->getSize() > 1024)
->sortByName()
->from('/var/app/src')
->paths();
// Зеркалирование и сравнение деревьев.
Directory::mirror('/var/app/dist', '/var/www/public'); // назначение = источник
$same = Directory::identical('/var/backup', '/var/app'); // структура + хеши
// Временный каталог с гарантированной автоочисткой (даже при исключении).
$tmp = TemporaryDirectory::make()->deleteWhenDestroyed();
FileSystem::write($tmp->path('report/data.json'), '{}'); // подкаталоги создаются
// ...по выходу из области видимости $tmp каталог удаляется автоматически.
// Рекурсивная смена прав по всему дереву.
FileSystem::chmod('/var/app/cache', 0o750, recursive: true);
Возможности
- Атомарная запись через временный файл со случайным именем +
rename— исключает частично записанные файлы при сбое или гонке. - Файлы: чтение, построчное и ленивое (генератор) чтение, запись, запись
массива строк (
writeLines), атомарное преобразование (atomicUpdate), дозапись в начало/конец, замена подстрок, копирование, перемещение, удаление,touch,chmod; предикатыmissing,isReadable,isWritable,isExecutable,type. - Потоки:
readStream/writeStream— обработка больших файлов без загрузки в память; запись из потока атомарна. - JSON:
readJson/writeJsonс контролем ошибок кодирования. - Ссылки: символические и жёсткие ссылки (
symlink,hardlink,readlink). - Метаданные и права: размер,
directorySize, время модификации, хеш (sha256 и др.),checksumEquals(timing-safe), MIME-тип,guessExtension,humanSize, видимостьvisibility/setVisibility; праваchmod/chown/chgrpс рекурсивным применением по дереву (recursive: true); разрешение реального пути черезreadlink(canonicalize: true)(аналогrealpath). - Каталоги: создание (рекурсивно), гарантированное существование файла и
каталога (
ensureFileExists/ensureDirectoryExists), рекурсивные копирование/перемещение/удаление/очистка, листинг файлов и подкаталогов (в т.ч. рекурсивный), поиск поglobи рекурсивныйfind,isDirectoryEmpty,deleteDirectories. - Текучий поиск (
Finder): декларативный обход дерева с фильтрами по типу, маскам имени (name/notName), глубине (depth), произвольным предикатам (filter) и сортировкой; результаты —SplFileInfoсо всеми метаданными. - Операции над деревом (
Directory): зеркалирование каталога (mirror— приведение назначения к источнику с удалением лишнего) и сравнение двух деревьев на идентичность структуры и содержимого (identical). - Реестр MIME ↔ расширение (
MimeTypes): разрешение расширения по MIME-типу и обратно из встроенной таблицы распространённых типов, без внешних зависимостей. - Временные ресурсы с автоочисткой (
TemporaryDirectory,TemporaryFile): RAII-объекты, гарантированно удаляющие ресурс при разрушении (deleteWhenDestroyed) — даже при исключении; поддержка явного имени и пересоздания (force). - Пути:
join,normalize,canonicalize,resolve,relative,isWithin(защита от path traversal),makeAbsolute,getRoot,commonBasePath,getHomeDirectory,expandUser(раскрытие~),segments,unixSlashes,extension,filename,removeExtension,changeExtension,hasExtension,isAbsolute. - Безопасность: отклонение байта NUL в путях, случайное имя temp-файла, атомарность, лексическое выявление выхода за каталог, контрактные исключения вместо предупреждений PHP.
Сравнение с аналогами
Все таблицы ниже сгенерированы автоматически из честных сравнительных
тестов (benchmarks/compare.php) на ОДИНАКОВОЙ операции для всех аналогов,
PHP 8.3.33, без Xdebug.
1. Функциональность
| Возможность | 🏆 CloudCastle | symfony | league | illuminate | nette | spatie | native¹ |
|---|---|---|---|---|---|---|---|
| Атомарная запись (temp + rename) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Дозапись в начало и конец файла | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Хеш и MIME-тип из коробки | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| Ленивое построчное чтение (генератор) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Потоковая обработка (readStream/writeStream) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| JSON: чтение и запись | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Символические и жёсткие ссылки | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |
| Рекурсивное копирование каталога | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ |
| Поиск по glob-шаблону | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ |
| Лексические операции с путями (relative, isWithin) | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Временные файлы и каталоги | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Рекурсивный листинг файлов | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| Управление видимостью (visibility) | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Нулевые внешние зависимости (кроме ext-fileinfo) | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ |
| Текучий поиск с фильтрами (Finder) | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Зеркалирование каталога (mirror) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Сравнение деревьев на идентичность | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| RAII-объекты временных ресурсов (автоочистка) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Рекурсивные права (chmod/chown/chgrp по дереву) | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
| Реестр MIME ↔ расширение из коробки | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Разрешение реального пути (realpath) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Утилиты путей: корень, общий базовый, домашний каталог | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Всего | 🏆 22 | 10 | 4 | 8 | 5 | 3 | 3 |
2. Безопасность и корректность
| Свойство | 🏆 CloudCastle | symfony | league | illuminate | nette | spatie | native¹ |
|---|---|---|---|---|---|---|---|
| Атомарная запись (нет частичных файлов при сбое) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Случайное имя временного файла (защита от symlink-атаки) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Отклонение байта NUL в пути | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Лексическое выявление path traversal (isWithin) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Дозапись под блокировкой (LOCK_EX) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Timing-safe проверка целостности (checksumEquals) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Контрактные исключения (маркер-интерфейс) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ |
| Гарантированная очистка временных ресурсов (RAII) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Всего | 🏆 8 | 2 | 1 | 0 | 1 | 2 | 0 |
3. Производительность
Рекурсивный листинг 80 файлов дерева, 3 000 раз (минимум из 4).
| Решение | Время (мс) | Итог |
|---|---|---|
| native¹ | 1 073,3 | базовый уровень (не библиотека) |
| 🏆 CloudCastle | 1 092,4 | быстрейшее среди библиотек |
| league | 1 552,3 | аналог |
| nette | 1 684,1 | аналог |
| symfony | 1 713,5 | аналог |
| illuminate | 2 392,9 | аналог |
4. Потребление памяти
_Рабочая память операции: пик рабочего набора на 3 000 листингов после прогрева классов (memory_reset_peakusage, PHP 8.2+) — только аллокации операции, не вес загруженного кода.
| Решение | Пиковая память (KB) | Итог |
|---|---|---|
| 🏆 league | 14 | легчайшее среди библиотек |
| CloudCastle | 18 | аналог |
| symfony | 18 | аналог |
| native¹ | 19 | базовый уровень (не библиотека) |
| nette | 26 | аналог |
| illuminate | 83 | аналог |
¹ Базовый уровень (нативные вызовы/примитивы без полноты решения) показан для контекста и не претендует на победу среди библиотек-аналогов.
Производительность и память измеряются на рекурсивном листинге дерева (частая
операция обхода проекта). Все аналоги приведены к ЭКВИВАЛЕНТНОЙ работе — материализации
одного и того же отсортированного списка путей, который allFiles даёт из коробки.
Каждая библиотека замеряется в отдельном процессе; память — это пик рабочего набора
самой операции после прогрева классов (не вес загруженного кода). league легче по
рабочей памяти за счёт ленивого генератора, уступая по функционалу (4 из 22) —
осознанный компромисс: детерминированный материализованный список против ленивого потока.
Вывод: CloudCastle FileSystem объединяет возможности пяти популярных
пакетов (суперсет по функционалу — 22 из 22), лидирует по безопасности, качеству кода
и по скорости обхода дерева среди библиотек, при нулевых внешних зависимостях
(кроме ext-fileinfo).
Плюсы и минусы
Плюсы:
- Самый широкий набор операций из коробки — суперсет (22 из 22 возможностей)
Symfony Filesystem, Flysystem, Laravel, Nette и Spatie вместе взятых: атомарная
запись, ссылки, JSON, glob, рекурсивные операции и права, ленивое чтение, потоки,
текучий поиск (
Finder), зеркалирование и сравнение деревьев (Directory), временные ресурсы с автоочисткой (TemporaryDirectory/TemporaryFile), реестр MIME (MimeTypes), богатые лексические операции с путями. - Безопасность по умолчанию: атомарность, случайное имя temp-файла, отклонение
байта NUL, лексическое выявление path traversal (
isWithin), timing-safe проверка целостности, RAII-очистка временных ресурсов, контрактные исключения с маркер-интерфейсом. - Нулевые внешние зависимости (только
phpиext-fileinfo) — без транзитивного веса и конфликтов версий. - 100 % покрытие строк на каждый файл и 100 % Infection MSI; строгий статанализ (PHPStan max, Psalm errorLevel 1, PHPMD, Deptrac, Rector, PSR-12).
- Быстрее аналогов на обходе дерева за счёт прямого итератора без оверхеда абстракций.
Минусы:
- Пакет моложе и менее распространён, чем Symfony/Laravel/Flysystem — меньше сторонних материалов и время на нём в проде.
- Только локальная файловая система: нет адаптеров к облачным хранилищам (S3/FTP/…), как у Flysystem — для мульти-бэкенда используйте Flysystem, а CloudCastle для локальных операций рядом с ним.
- Статический фасад: удобно и быстро, но при необходимости мокать ввод-вывод в тестах абстрагируйте вызовы за собственный интерфейс.
Когда применять
- Конфигурация, кэш, сессии, генерация файлов — там, где критична атомарная запись без частично записанных файлов (деплой, сборка артефактов).
- Финтех и обработка ПД — благодаря защите от path traversal (
isWithin), отклонению NUL и случайным именам temp-файлов при загрузке/выгрузке файлов. - CLI-инструменты и скрипты сборки — быстрый рекурсивный обход дерева, хеширование, glob, операции с путями без тяжёлых зависимостей.
- Библиотеки и пакеты — когда нужен богатый файловый API без навязывания пользователю транзитивных зависимостей.
- Когда лучше взять другое: для работы с облачными хранилищами или единого API поверх нескольких бэкендов — Flysystem; если проект уже целиком на Symfony или Laravel и хватает их файловых компонентов — их и достаточно.
Разработка
composer install
composer check # линтеры + статический анализ + тесты
composer fix # автоисправления (Rector, PHP CS Fixer, PHPCBF)
composer ci # полный CI-пайплайн локально
Полный список команд с описаниями: composer run-script --list.
Документация
- Репозиторий: https://gitverse.ru/cloud-castle/file-system
- История изменений: CHANGELOG.md
- Как внести вклад: CONTRIBUTING.md
- Кодекс поведения: CODE_OF_CONDUCT.md
- Политика безопасности: SECURITY.md
Лицензия
MIT © CloudCastle (alex-4-17@yandex.ru)
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano