Квест → Как хакнуть форму
Прошли: 77
Для тех, кто пропустил: 24 ноября вышла новая мажорная версия Flysystem. Новая версия позволяет порвать с прошлым ради будущего, что я и сделал.
Для создания второй версии Flysystem я начал с чистого листа. Многие основные элементы дизайна библиотеки были почищены и улучшены. API уменьшен, сохраняя при этом свой прежний функционал. Обработка ошибок теперь основана только на исключениях, а листинг директорий теперь используют генераторами. Несмотря на то, что изменений много, эта версия Flysystem верна своим корням. Давайте погрузимся в детали!
В версии V1 ошибки моделировались максимально схоже с собственными функциями файловой системы PHP. Такие функции используют false
в качестве индикатора неудачной операции. И, хотя такой подход работал, он вызывал сложности в Flysystem и коде, который использовал его. Иногда это также приводило к потере информации при работе с ошибками. Для непредвиденных ошибок всё-таки использовались исключения. Данная конструкция образовывала два разных пути борьбы с ошибками, что приводило к подобным конструкциям:
try { $success = $filesystem->write($path, $contents); if ($success === false) { // handle error } } catch (Throwable $exception) { // handle an exception }
Хоть это и не является огромной проблемой, но когда вы используете Flysystem, подобный код дублируется на каждом этапе интеграции. Внутренние части библиотеки страдали от такого же рода дублирования, что приводило к лишней запутанности.
В версии V2 все ошибки представлены в качестве исключений. Каждая операция с файловой системой имеет соответствующий класс-исключение. Каждый такой класс объясняет, что было исходной операцией и что пошло не так. Если основной SDK или клиентская библиотека генерируют исключение, Flysystem обязательно обернет его в определенное исключение Flysystem, сохраняя соответствующий стек-трейс. Это позволяет обрабатывать исключения единообразно, сохраняя при этом всю информацию, необходимую для отладки любых проблем.
try { $filesystem->write($path, $contents); } catch (UnableToWriteFile $exception) { // handle the error }
В версии V1 было два способа записи файла. Функции write
и writeStream
позволяли записывать новые файлы. Для обновления файлов использовались функции update
и updateStream
. Эти методы выполняли проверку существования файла, чтобы предотвратить перезапись файлов или попытку обновления несуществующего файла. Хотя в то время это казалось полезным, это стало большим разочарованием для меня, так как это привело к большому количеству условных выражений в использующемся коде. Это также вызывает ненужные проверки, часто приводящие к дорогостоящим вызовам по сети.
if ($filesystem->has($file)) { $filesystem->update($file, $contents); } else { $filesystem->write($file, $contents); }
Если бы в приведенном выше примере использовался AWS S3, то этот блок кода всегда вызывал бы 3 HTTP-запроса.
В V2 все операции записи и удаления являются детерминированными. Для записи это означает, что файл с предоставленным содержимым всегда будет записан. Для удаления это означает, что операция удаления выполнена успешно, даже если файл не существовал. Результат всегда таков: "теперь файла больше нет". Это устраняет множество шансов возникновения "состояний гонки", вызванных самой библиотекой, и упрощает использование кода. Такое поведение устранило необходимость использовать функцию update
, поэтому update
и updateStream
теперь удалены.
$filesystem->write($path, $contents); // если вам действительно необходимо проверить существование файла $fileExists = $filesystem->fileExists($pathToFile);
По сравнению с V1, для записи или обновления файла на S3 потребуется всего 1 HTTP-вызов.
В версии V1 листинг директорий возвращал массив ассоциативных массивов с относительно хорошо стандартизированными свойствами/ключами. Данное решение было очень практичным, но имело свои ограничения. Во-первых, использующийся код должен был знать, какие ключи доступны в массивах, и в IDE не было никаких хинтов. Кроме того, весь список был полностью получен (fetch) перед его возвратом. Для больших списков директорий это могло приводить (и часто приводило) к тому, что у процессов заканчивалась память.
В версии V2 листинг директорий используется генераторами. Это позволяет гораздо более эффективно использовать память для листинга содержимого директории. Кроме того, Filesystem
возвращает объект DirectoryListing
, который добавляет некоторые удобные методы, такие как filter
и map
.
Давайте сравним кейс, когда мы фильтруем и сопоставляем списки директорий. Вот как это работало в V1:
$listing = $filesystem->listContents('path/to/dir'); $files = array_filter( $listing, fn ($i) => $i['type'] === 'file', ); $paths = array_map( fn ($i) => $i['path'] $files, );
И вот как это выглядит в V2:
$paths = $filesystem->listContents('path/to/dir') ->filter(fn (StorageAttributes $i) => $i->isFile()) ->map(fn (StorageAttributes $i) => $i->path()) ->toArray();
Я считаю, что библиотека Flysystem готова к предстоящей необходимости в абстрагировании файловой системы, и надеюсь, что вы будете рады начать её использовать. Для получения дополнительной информации ознакомьтесь с документацией.