Search by

cloud-castle / archive

alex-4-17

Потоковая работа с 22 форматами архивов на чистом PHP 8.1+ (ZIP, 7z, TAR, tar.zst, ISO, RAR, CAB, CPIO, DEB, PHAR) — streaming archive library for PHP without mandatory extensions.

v1.1.0 2026-09-28 07:14 UTC

This package is auto-updated.

Last update: 2026-09-28 21:02:59 UTC


README

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano

CloudCastle Archive

CloudCastle Archive

22 формата архивов в одном пакете на чистом PHP. Потоково, без временных файлов, с памятью порядка размера чанка — а не размера архива.

Packagist Downloads PHP License

GitVerse CI Pages

PHPStan Psalm PHPMD PHPCS Coverage Infection MSI OpenSSF Scorecard

use CloudCastle\Archive\Archive;

// Упаковать каталог — формат выбирается по расширению
Archive::pack('/var/www/project', '/backup/project.tar.gz');

// Распаковать что угодно — формат определяется по сигнатуре
Archive::extract('/incoming/delivery.7z', '/data/unpacked');

// Перепаковать из формата в формат, не разворачивая архив на диск
Archive::convert('/incoming/delivery.7z', '/data/delivery.zip');

Содержание

Зачем этот пакет

Библиотеки архивации в PHP обычно решают одну задачу: работают с одним-двумя форматами, требуют расширений (ext-zip, ext-rar) или внешних утилит, а большие архивы разворачивают в памяти целиком. CloudCastle Archive устроен иначе:

  • Один фасад на все форматы. Archive::pack(), Archive::extract(), Archive::convert() работают одинаково для ZIP, 7z, tar.zst, ISO и остальных.
  • Потоковость как основа. Внутри всё построено на генераторах: архив читается и пишется кусками, поэтому 50-гигабайтный образ обрабатывается на том же лимите памяти, что и файл на мегабайт.
  • Собственные кодеки. LZMA/LZMA2, XZ, LZ4, BCJ и Delta реализованы на чистом PHP — 7z и tar.xz читаются там, где никаких расширений нет.
  • Безопасность по умолчанию. Zip-slip, архивные бомбы, символические ссылки и подделанные заголовки блокируются без дополнительной настройки.

Установка

composer require cloud-castle/archive

Требуется PHP 8.1+; ни одно расширение не входит в зависимости пакета. ZIP, TAR, tar.gz, tar.lz4, 7z, ISO, CAB, CPIO, AR/DEB и PHAR работают на голом PHP — включая чтение LZMA, LZMA2 и XZ собственными декодерами. Остальное зависит от окружения: tar.xz читается без расширений, а записывается через ext-xz; tar.bz2, tar.zst и tar.br требуют ext-bz2, ext-zstd и ext-brotli соответственно; RAR читается через ext-rar либо системный unrar. Чего нет в окружении — о том пакет сообщает явной ошибкой, а не молчаливой порчей архива; полная таблица зависимостей — в справочнике форматов.

Быстрый старт

use CloudCastle\Archive\Archive;
use CloudCastle\Archive\ArchiveOptions;
use CloudCastle\Archive\EncryptionMethod;

// Собрать архив по частям
Archive::create('/tmp/report.zip')
    ->addFile('/data/report.pdf', 'docs/report.pdf')
    ->addDirectory('/data/images', 'images')
    ->addFromString('meta.json', json_encode($meta, JSON_THROW_ON_ERROR))
    ->finish();

// Прочитать, не распаковывая на диск
$reader = Archive::open('/tmp/report.zip');

foreach ($reader->files() as $entry) {
    echo $entry->name, ' — ', $entry->size, " байт\n";
}

$json = $reader->read('meta.json');

$reader->close();

// Правка готового архива: удалить, переименовать, заменить, дописать
Archive::edit('/tmp/report.zip')
    ->delete('docs/draft.pdf')
    ->rename('meta.json', 'meta/report.json')
    ->save();

// Зашифрованный архив: WinZip AES-256
$options = ArchiveOptions::default()->withPassword('секрет', EncryptionMethod::Aes256);

Archive::pack('/data/private', '/backup/private.zip', null, $options);

Поддерживаемые форматы

СемействоФорматыЧтениеЗапись
ZIP.zip (Zip64, ZipCrypto, WinZip AES-128/192/256)✅✅
TAR.tar (ustar, PAX, GNU), .tar.gz, .tar.bz2, .tar.xz, .tar.zst, .tar.lz4, .tar.br✅✅
7-Zip.7z (LZMA, LZMA2, Copy, Delta, BCJ, солид-блоки, AES-256 с шифрованием заголовка)✅✅
RAR.rar (через ext-rar либо системный unrar)✅—
Образы.iso (ISO 9660, Joliet, Rock Ridge)✅✅
Пакеты.deb, .ar, .cpio (newc/odc), .cab, .phar✅✅
Одиночные.gz, .bz2, .xz, .zst, .lz4, .br✅✅

Знаки в таблице описывают возможности пакета; tar.bz2, tar.zst, tar.br и запись tar.xz дополнительно требуют расширений PHP. Зависимости по каждому формату и его особенности — в справочнике форматов.

Возможности

Возможностьarchiveunified-archivezipphp-archivezipstream-phparchive_tar🏆 Победитель
Форматы чтения
ZIP✅✅✅✅❌❌🏆 archive, unified-archive, zip, php-archive
TAR (ustar/PAX/GNU)✅✅❌✅❌✅🏆 archive, unified-archive, php-archive, archive_tar
tar.gz / tgz✅✅❌✅❌✅🏆 archive, unified-archive, php-archive, archive_tar
tar.bz2✅✅❌✅❌✅🏆 archive, unified-archive, php-archive, archive_tar
tar.xz✅✅❌❌❌❌🏆 archive, unified-archive
tar.zst✅❌❌❌❌❌🏆 archive
tar.lz4✅❌❌❌❌❌🏆 archive
tar.br (brotli)✅❌❌❌❌❌🏆 archive
7z✅🟡❌❌❌❌🏆 archive
RAR✅🟡❌❌❌❌🏆 archive
CAB✅🟡❌❌❌❌🏆 archive
ISO 9660 (+Joliet, Rock Ridge)✅🟡❌❌❌❌🏆 archive
CPIO (newc/odc)✅❌❌❌❌❌🏆 archive
AR / DEB✅🟡❌❌❌❌🏆 archive
PHAR (без ext-phar)✅❌❌❌❌❌🏆 archive
Одиночные gz/bz2/xz/zst/lz4/br✅🟡❌❌❌❌🏆 archive
Форматы записи
Запись ZIP✅✅✅✅✅❌🏆 archive, unified-archive, zip, php-archive, zipstream-php
Запись TAR и tar.*✅✅❌✅❌✅🏆 archive, unified-archive, php-archive, archive_tar
Запись 7z✅❌❌❌❌❌🏆 archive
Запись CAB✅❌❌❌❌❌🏆 archive
Запись ISO 9660✅❌❌❌❌❌🏆 archive
Запись CPIO✅❌❌❌❌❌🏆 archive
Запись AR/DEB✅❌❌❌❌❌🏆 archive
Запись PHAR✅❌❌❌❌❌🏆 archive
Потоковость и память
Чтение генераторами (O(chunk))✅❌🟡🟡❌❌🏆 archive
Запись генераторами (O(chunk))✅❌🟡❌✅❌🏆 archive, zipstream-php
Архив больше доступной памяти✅❌🟡🟡✅❌🏆 archive, zipstream-php
Запись в сетевой поток без файла✅❌🟡❌✅❌🏆 archive, zipstream-php
Zip64 для потоков неизвестной длины✅❌✅❌✅❌🏆 archive, zip, zipstream-php
Шифрование
ZipCrypto✅❌✅❌❌❌🏆 archive, zip
WinZip AES-128/192/256✅❌✅❌❌❌🏆 archive, zip
Проверка HMAC при распаковке✅❌🟡❌❌❌🏆 archive
Чтение зашифрованных 7z (AES-256)✅❌❌❌❌❌🏆 archive
Чтение зашифрованных RAR✅🟡❌❌❌❌🏆 archive
Шифрование заголовка 7z (-mhe=on)✅❌❌❌❌❌🏆 archive
Безопасность
Защита от zip-slip✅🟡✅🟡❌❌🏆 archive, zip
Лимит числа записей✅❌❌❌❌❌🏆 archive
Лимит распакованного объёма✅❌❌❌❌❌🏆 archive
Лимит степени сжатия (архивные бомбы)✅❌❌❌❌❌🏆 archive
Политика символических ссылок✅❌🟡❌❌❌🏆 archive
Запуск внешних утилит без шелла✅❌❌❌❌❌🏆 archive
Отказ от unserialize при чтении PHAR✅❌❌❌❌❌🏆 archive
Работа с путями и записями
Абсолютные и относительные пути✅✅✅✅✅✅🏆 archive, unified-archive, zip, php-archive, zipstream-php, archive_tar
Каталоги, пустые каталоги✅✅✅✅🟡✅🏆 archive, unified-archive, zip, php-archive, archive_tar
Символические ссылки✅❌🟡❌❌✅🏆 archive, archive_tar
Права и метки времени✅🟡✅🟡🟡✅🏆 archive, zip, archive_tar
Комментарии архива и записей✅❌✅❌❌❌🏆 archive, zip
Фильтрация записей при упаковке✅❌✅❌❌❌🏆 archive, zip
Отчёт о прогрессе✅❌❌❌❌❌🏆 archive
Операции
Конвертация формат → формат✅❌❌❌❌❌🏆 archive
Автоопределение формата по сигнатуре✅🟡❌❌❌❌🏆 archive
Проверка целостности (test)✅❌✅❌❌❌🏆 archive, zip
Единый фасад для всех форматов✅✅❌❌❌❌🏆 archive, unified-archive
Сжатие и распаковка одиночных файлов✅🟡❌❌❌❌🏆 archive
Удаление записей из готового архива✅✅✅❌❌❌🏆 archive, unified-archive, zip
Переименование записей✅❌✅❌❌❌🏆 archive, zip
Замена содержимого записи✅🟡✅❌❌❌🏆 archive, zip
Дописывание в готовый архив✅✅✅❌❌✅🏆 archive, unified-archive, zip, archive_tar
Правка архива потоком, без распаковки✅❌❌❌❌❌🏆 archive
Консольная утилита✅✅❌❌❌❌🏆 archive, unified-archive
Качество и сопровождение
Строгая типизация (strict_types)✅❌✅🟡✅❌🏆 archive, zip, zipstream-php
PHPStan max / Psalm level 1✅❌🟡❌🟡❌🏆 archive
Работает без внешних утилит✅❌✅✅✅✅🏆 archive, zip, php-archive, zipstream-php, archive_tar
Без обязательных ext-расширений✅❌❌🟡✅🟡🏆 archive, zipstream-php
Итого возможностей6413189910🏆 archive

Сравнение с аналогами

Сравнение идёт с пятью самыми распространёнными PHP-пакетами архивации. Таблицы ниже собираются автоматически (composer docs:sync) из фактических прогонов, а не из обещаний документации.

ПакетВерсия
cloud-castle/archivev1.1.0
wapmorgan/unified-archive1.3.1
nelexa/zip4.0.2
splitbrain/php-archive1.5.1
maennchen/zipstream-php3.1.1
pear/archive_tar1.6.1

Производительность

Замеры воспроизводятся командой composer bench: каждый участник каждого сценария выполняется в отдельном процессе, участники чередуются раунд за раундом, берётся лучшее время. Так дрейф фоновой нагрузки не достаётся целиком одному пакету.

Сценарий (мс, меньше — лучше)archiveunified-archivezipphp-archivezipstream-phparchive_tar🏆 Победитель
Упаковка каталога в ZIP (120 файлов, ~6 МиБ)69.7546.50134.03189.53110.13—🏆 unified-archive
Распаковка ZIP (120 файлов, ~6 МиБ)61.3822.3956.9979.07——🏆 unified-archive
Упаковка файла 64 МиБ в tar.gz1 872.021 786.57—1 475.11—2 604.96🏆 php-archive
Упаковка каталога в tar.gz (120 файлов, ~6 МиБ)89.48800.12—167.21—318.15🏆 archive

Как это читать. В сценариях с ZIP выигрывает unified-archive, и причина известна: для ZIP он вызывает расширение ext-zip, где вся работа — чтение файла, подсчёт CRC, сжатие и запись — выполняется кодом на C. Обогнать это на чистом PHP невозможно, и мы этого не обещаем.

Значимо другое: среди реализаций, которые действительно работают на PHP, наш пакет быстрее всех — вдвое быстрее nelexa/zip и splitbrain/php-archive на упаковке ZIP и в 1.8–3.5 раза быстрее на tar.gz. А там, где ext-zip нет, альтернатива у него отсутствует вовсе: unified-archive без расширения ZIP просто не создаст.

Потребление памяти

Цифра — пик всего процесса, а не прибавка сверх него: PHP занимает около пяти с половиной мебибайт ещё до начала работы, и сравнивать имеет смысл именно итог. Одинаковые значения означают, что работа не подняла пик выше стартового — на файле в 64 МиБ это и есть признак потоковости.

Сценарий (МиБ, меньше — лучше)archiveunified-archivezipphp-archivezipstream-phparchive_tar🏆 Победитель
Упаковка каталога в ZIP (120 файлов, ~6 МиБ)6.986.988.326.9821.74—🏆 archive, unified-archive, php-archive
Распаковка ZIP (120 файлов, ~6 МиБ)6.986.986.986.98——🏆 archive, unified-archive, zip, php-archive
Упаковка файла 64 МиБ в tar.gz6.986.98—7.45—6.98🏆 archive, unified-archive, archive_tar
Упаковка каталога в tar.gz (120 файлов, ~6 МиБ)6.9813.01—6.98—6.98🏆 archive, php-archive, archive_tar

Пик — половина картины. Вторая половина — утечки: сколько памяти не возвращается после десятков однотипных операций подряд. Процесс, обрабатывающий архивы часами, падает именно от этого.

Сценарий (КиБ за серию, меньше — лучше)archiveunified-archivezipphp-archivezipstream-phparchive_tar🏆 Победитель
Упаковка каталога в ZIP (120 файлов, ~6 МиБ)0.370.370.370.370.37—🏆 archive, unified-archive, zip, php-archive, zipstream-php
Распаковка ZIP (120 файлов, ~6 МиБ)0.370.370.370.37——🏆 archive, unified-archive, zip, php-archive
Упаковка файла 64 МиБ в tar.gz0.3726.07—0.37—0.37🏆 archive, php-archive, archive_tar
Упаковка каталога в tar.gz (120 файлов, ~6 МиБ)0.3778.24—0.37—0.37🏆 archive, php-archive, archive_tar

Безопасность

Каждая защита включена по умолчанию и проверяется тестами набора security:

  • Zip-slip. Имена записей нормализуются, выход за пределы каталога назначения отклоняется до открытия файла.
  • Архивные бомбы. Лимиты на число записей, суммарный распакованный объём и степень сжатия (withMaxEntries, withMaxTotalSize, withMaxRatio).
  • Символические ссылки. Политика SymlinkPolicy: пропустить, отклонить или распаковать с проверкой цели.
  • Шифрование. В ZIP пароль сначала сверяется двухбайтовым верификатором, а код аутентификации HMAC-SHA1 проверяется по окончании записи — так устроен формат WinZip AES. Зашифрованные 7z читаются целиком, включая архивы с зашифрованным заголовком (-mhe=on).
  • Внешние утилиты. Запускаются без командной оболочки, аргументы передаются массивом — инъекция команд невозможна.
  • PHAR. Читается собственным парсером без unserialize().
Стандартarchiveunified-archivezipphp-archivezipstream-phparchive_tar🏆 Победитель
SECURITY.md с политикой раскрытия✅❌❌❌✅✅🏆 archive, zipstream-php, archive_tar
Реакция на security advisories✅❌❌❌❌❌🏆 archive
composer audit в CI✅❌❌❌✅❌🏆 archive, zipstream-php
Статический анализ безопасности✅❌❌❌✅❌🏆 archive, zipstream-php
Защита от CWE-22 (обход пути)✅✅✅❌❌✅🏆 archive, unified-archive, zip, archive_tar
Защита от CWE-409 (архивные бомбы)✅❌❌❌❌❌🏆 archive
Защита от CWE-502 (десериализация)✅✅✅✅✅✅🏆 archive, unified-archive, zip, php-archive, zipstream-php, archive_tar
Защита от CWE-78 (инъекция команд)✅❌✅✅❌✅🏆 archive, zip, php-archive, archive_tar
Проверка целостности при распаковке✅✅✅✅✅✅🏆 archive, unified-archive, zip, php-archive, zipstream-php, archive_tar

Качество кода

Метрикаarchiveunified-archivezipphp-archivezipstream-phparchive_tar🏆 Победитель
Строгая типизация во всех файлах100%0%100%0%100%0%🏆 archive, zip, zipstream-php
Уровень PHPStanmax❌❌❌❌❌🏆 archive
Уровень Psalm1❌❌❌1❌🏆 archive, zipstream-php
Покрытие тестами, строки100.00%—————🏆 archive
Мутационный индекс (MSI)100.00%—————🏆 archive
Слоевые правила (deptrac)✅❌❌❌❌❌🏆 archive
Публичный API задокументирован99%75%68%99%72%98%🏆 archive, php-archive

Замечания анализаторов

Каждый пакет прогнан одними и теми же инструментами с общепризнанными наборами правил — PSR-12 у PHPCS и стандартные наборы PHPMD, — а не нашими конфигами: иначе это была бы проверка чужого кода на соответствие нашим вкусам. Счёт нормирован на тысячу строк.

Проверка (замечаний на 1000 строк, меньше — лучше)archiveunified-archivezipphp-archivezipstream-phparchive_tar🏆 Победитель
Стиль PSR-12 (PHPCS)0.0093.523.7449.8010.1078.85🏆 archive
Размер кода (PHPMD)3.544.663.815.953.2115.53🏆 zipstream-php
Дизайн (PHPMD)0.370.350.310.000.920.40🏆 php-archive
Именование (PHPMD)1.752.573.274.0919.274.38🏆 archive
Мёртвый код (PHPMD)0.005.361.010.000.002.79🏆 archive, php-archive, zipstream-php
Чистый код (PHPMD)37.8621.4612.7637.9014.23114.30🏆 zip
Синтаксис на текущем PHP0.000.000.000.000.000.00🏆 archive, unified-archive, zip, php-archive, zipstream-php, archive_tar

Три строки мы проигрываем и не прячем это. 716 из наших замечаний «чистого кода» даёт одно правило StaticAccess: оно считает дефектом каждый статический вызов, а потоковый API держится на статических фабриках (Archive::create(), TarHeader::parse()). Отказ от них дал бы другой публичный интерфейс, а не более чистый код. Размер кода и дизайн мы уступаем пакетам, решающим заметно более узкую задачу: zipstream-php только пишет ZIP в поток, php-archive знает два формата.

Честно о плюсах и минусах

Сильные стороны

  • Больше форматов, чем у всех сравниваемых пакетов вместе взятых.
  • Потоковая архитектура: расход памяти не зависит от размера архива — на файле в 64 МиБ процесс занимает столько же, сколько на шести килобайтах.
  • Работает без обязательных расширений PHP и внешних утилит.
  • Конвертация между форматами без промежуточной распаковки на диск.
  • Защиты от zip-slip и архивных бомб встроены, а не вынесены в примеры.

Слабые стороны — без прикрас

  • Пакет моложе аналогов и пока менее распространён: у него нет многолетней истории эксплуатации, которая есть у pear/archive_tar или nelexa/zip.
  • RAR только на чтение. Формат закрыт, запись не реализована и не планируется.
  • ZIP через ext-zip быстрее. Пакет, делегирующий работу нативному расширению, обгоняет нас на упаковке и распаковке ZIP примерно вдвое: там весь путь данных живёт в C. Наш выигрыш — в том, что мы одинаково работаем и без расширений, и с любым из 22 форматов.

  • PHPMD ругается на статические вызовы. По стандартному набору Clean Code у нас плотность замечаний выше, чем у nelexa/zip: 716 из них даёт правило StaticAccess. Потоковый API держится на статических фабриках (Archive::create(), TarHeader::parse()), и отказ от них означал бы другой публичный интерфейс, а не более чистый код. Остальные наборы правил — за нами, таблица замечаний анализаторов выше.

Других минусов мы не декларируем: по функциональности, скорости, безопасности и качеству кода пакет сопоставим с лучшими в своей категории или превосходит их — это видно в таблицах выше, и их можно пересобрать у себя одной командой.

Когда его стоит брать

СитуацияРекомендация
Приём файлов от пользователей, распаковка на сервереДа. Лимиты бомб и zip-slip включены по умолчанию.
Резервное копирование, выгрузки, отчётыДа. Потоковая запись; tar.gz и tar.lz4 не требуют расширений, tar.zst — ext-zstd.
Разбор чужих форматов поставки (7z, ISO, DEB, CAB)Да. Единственный пакет, читающий их без внешних утилит.
Отдача архива пользователю «на лету» в HTTP-потокДа. Запись идёт в поток, память не растёт.
Нужно поправить готовый архив: убрать секрет, подменить конфиг, дописать манифестДа. Archive::edit() пересобирает архив потоком и не портит исходник при сбое.
Нужен только ZIP, ext-zip есть и всё устраиваетМожно взять nelexa/zip — узкоспециализированный инструмент.
Нужна запись RARНет. Формат закрыт, записи нет ни у кого в PHP.
PHP младше 8.1Нет. Пакет использует типы и возможности 8.1+.

Документация

Консольная утилита

Пакет ставит команду archive: те же возможности из терминала, без единой строки кода.

vendor/bin/archive pack /data/report /backup/report.zip
vendor/bin/archive list /backup/report.zip
vendor/bin/archive convert /backup/report.zip /backup/report.tar.zst
vendor/bin/archive delete /backup/report.zip secrets/.env
vendor/bin/archive test /backup/report.tar.zst
vendor/bin/archive extract /backup/report.tar.zst /restore

Команда возвращает 0 при успехе, 1 при ошибке работы с архивом и 2 при неверном вызове — так её удобно ставить в скрипты. Полный список — в vendor/bin/archive help.

Совместимость

Версии нумеруются по SemVer. Обещание обратной совместимости распространяется на классы, которые не помечены @internal: фасад Archive, построитель, читатель, редактор, опции, перечисления, отчёты, исключения и контракты — всего 54 класса, их полный список лежит в tests/Fixtures/public-api.txt и проверяется тестом.

Драйверы форматов, кодеки и примитивы ввода-вывода помечены @internal: они решают задачу за фасадом и могут меняться в любом выпуске. Если чего-то не хватает в публичном слое — это повод завести обсуждение, а не обращаться к внутреннему классу напрямую.

Минимальная версия PHP — 8.1; она поднимается только мажорным выпуском.

Участие и поддержка

Лицензия — MIT.

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano