Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 106 · С нуля → полный справочник

ZIP: документы в одном архиве и полный справочник

Создание, чтение, безопасное извлечение, кодеки, метаданные, откат и полный переход со старого API; все официальные узлы book.zip.

Первый полезный результат: чек в одном ZIP

Наглядная модель

Чек и памятка становятся одним ZIP-файлом

ZIP хранит несколько именованных записей. Подготовка записи и успешное сохранение готового архива — разные шаги.

  1. receipt.txt и guide.txt

    Два исходных документа должны попасть в посылку клиенту.

  2. ZipArchive::open

    Именно true подтверждает открытие: числовой код ошибки не считается успехом.

  3. Добавить записи

    addFromString/addFile формируют состав; файл-источник может понадобиться до close.

  4. Проверить close

    Только успешное завершение подтверждает сохранение, затем получатель открывает записи по именам.

Нулевой файл от tempnam не является готовым пустым ZIP. setPassword само по себе не включает шифрование, а извлечение чужого архива требует ограничений пути и ресурсов.

Магазину нужно передать клиенту чек и памятку одним файлом. ZIP — контейнер записей: у каждой есть имя, байты и метаданные. Он может сжимать данные, но не обязан. Один ZIP-файл на диске и файлы внутри него — разные сущности. Начнём с одного текстового чека, затем научимся выбирать документы, читать без распаковки и обрабатывать отказ сохранения.

Если переменные и вызовы пока незнакомы: переменная и вывод, вызов функции, объект и метод, локальные файлы. Здесь каждый новый ZIP-шаг объясняется на месте; ссылки нужны для общего синтаксиса.

Контейнер заказа 42
  1. Строка paid:42 — содержимое чека
  2. Внутреннее имя order.txt — подпись записи
  3. Объект $zip — управление открытым контейнером
  4. close() → lesson.zip на диске
  5. Повторное open() → чтение order.txt
СинтаксисЗачем он в первом примере
$path = __DIR__ . "/lesson.zip"Переменная хранит путь. __DIR__ — папка PHP-файла; . соединяет строки. Пример запускают в отдельной своей пустой папке.
new ZipArchive()$zip становится объектом; это ещё не открытый архив.
$zip->open(...)-> вызывает метод именно этого объекта. Имена в скобках — аргументы в указанном порядке.
ZipArchive::CREATE | ZipArchive::EXCL:: берёт именованную константу класса; | сочетает создание с запретом заменять существующий файл.
!== trueНеудача open может быть числовым кодом, поэтому требуется строгое сравнение со значением true.
if (...) { throw new RuntimeException(...); }При отказе прекращаем пример понятным исключением, вместо работы с неоткрытым архивом.
echo ..., " ";Вывод результата и перевод строки. Точка с запятой завершает инструкцию.
unlink($path)Удаляет только созданный этим примером ZIP после проверки. Для сохранения результата уберите заключительную уборку.
Создать, сохранить и проверить один чекСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Чек не добавлен');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$receipt = $zip->getFromName('order.txt');
if ($receipt === false) throw new RuntimeException('Чек не прочитан');
echo $receipt, "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Новый файл создаётся без перезаписи существующего. Сначала проверяется сохранение, потом читается сохранённая версия. Папка процесса имеет значение для относительных имён; __DIR__ привязывает ZIP к файлу урока.

Аналогия с коробкой объясняет подписи и содержимое, но не обещает мгновенную запись: пока архив открыт, часть действий только подготовлена. Закрытие может потребовать временного файла и свободного места рядом с архивом. Даже при успешном добавлении итоговый close() ещё может отказать.

Установка, версии и проверка возможностей

ZipArchive относится к расширению zip, которое использует libzip. PHP CLI и PHP веб-сервера могут загружать разные php.ini; проверяйте тот исполнитель, где будет работать приложение. Уроки исполнялись в PHP 8.5.11 x64 с ext-zip 1.22.8 и libzip 1.11.4; это проверенное окружение, а не обязательные значения на любом компьютере.

Проверка установки
  1. Выбрать нужный php.exe
  2. php --ini показывает его конфигурацию
  3. Проверить extension_loaded("zip")
  4. Проверить нужный метод и поддержку кодека
Проверка перед первым архивомСамостоятельный пример
<?php
declare(strict_types=1);

if (!extension_loaded('zip')) throw new RuntimeException('Нужно расширение zip');
echo class_exists(ZipArchive::class) ? "ZipArchive доступен\n" : "Нет класса\n";
echo is_string(ZipArchive::LIBZIP_VERSION) ? "Версия libzip доступна\n" : "Нет версии\n";
Ожидаемый результат
ZipArchive доступен
Версия libzip доступна

Номер версии не зашит в ожидаемый результат. ::class даёт имя класса; class_exists проверяет доступность. Этот пример требует zip, как и остальные исполняемые примеры главы.

ОкружениеЧто именно требуется
Windows, PHP 8.2+Нужно подключить соответствующую сборке php_zip.dll через extension=zip в нужном php.ini; в старых Windows PHP ZIP был встроен. DLL должна совпадать с PHP по архитектуре, версии/ABI и TS/NTS. Не скачивайте случайную DLL.
Unix: сборка PHPВ текущем исходном PHP применяется --with-zip и системная libzip. Для пакетов ОС используйте пакет расширения под вашу версию PHP. Это настройки сборки, не команды в PHP-файле.
История установкиДо PHP 7.4 использовалось --enable-zip; внешняя libzip выбиралась --with-libzip=DIR; отключение встроенной — --without-libzip. В PHP 7.3 встроенная libzip объявлена deprecated; встроенная версия 1.1.2 поставлялась до PHP 7.3.
БиблиотекаСтраница требований указывает libzip не ниже 0.11 и рекомендует новую. Шифрование требует libzip 1.2+, progress callback 1.3+, cancel callback 1.6+. Конкретные новые методы/константы имеют собственные минимумы.
НастройкиВ официальном дереве ZIP нет страницы специальных runtime INI-директив; загрузка расширения и общие ограничения PHP/файловой системы действуют отдельно.
Обновление расширенияPECL zip — отдельный выпуск расширения; версия PECL/ext-zip не равна PHP и не равна libzip. Проверяйте совместимость конкретной сборки.
Диагностика только отдельного процессаФрагмент или команда
php --ini
php -d extension=zip --ri zip

Команды PowerShell/терминала, mode=fragment: php уже должен находиться в PATH либо замените его полным путём к нужному php.exe. extension_dir должен указывать на совместимую DLL/so. Не добавляйте extension=zip повторно, если расширение уже загружено. Это не изменение PATH или службы.

Объект ZipArchive, индексы и readonly-свойства

Объект хранит состояние открытого архива. Один и тот же номер записи может значить другое после сохранения и повторного открытия. Для устойчивой бизнес-связи обычно используют проверенное имя; индекс полезен для обхода текущего снимка. Класс реализует Countable: count($zip) вызывает count(). Читать readonly-свойство можно, присваивать ему новое значение нельзя.

Жизненный цикл
  1. new → объект ещё без архива
  2. open===true → разрешены операции
  3. Изменения подготовлены в памяти/источниках
  4. close → успех либо отказ; архив закрыт
  5. Новый open → новый набор индексов
Readonly-свойствоДоговор
lastId:intИндекс последнего добавленного файла/каталога; PHP 8.0 / PECL 1.18.
status:intКод libzip ER_*. С PHP 8.0 / PECL 1.18 доступен и после close().
statusSys:intСистемный код соответствующего отказа; не всегда errno одного вида, трактуется вместе с ZIP-ошибкой. После close() также PHP 8.0 / PECL 1.18.
numFiles:intЧисло записей открытого архива, включая каталоги.
filename:stringПуть открытого ZIP, не имя текущей записи.
comment:stringКомментарий всего архива. Для изменения есть setArchiveComment().
Узнать, что сейчас открытоСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->numFiles, " запись\n";
echo basename($zip->filename), "\n";
$zip->addFromString("note.txt", "ok");
echo $zip->lastId, "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
1 запись
lesson.zip
1

Свойства читают состояние объекта; basename выводит только известное имя нашего архива. lastId относится к последнему добавлению.

Методы, кроме статических проверок возможностей и специальных методов диагностики, требуют действующий архив; PHP 8.5.11 бросает ValueError для неинициализированного/закрытого состояния. Не рассчитывайте сериализовать/клонировать открытый дескриптор для передачи другому процессу: передавайте путь с проверенной политикой доступа и открывайте там заново.

Имена, байты, Unicode и переносимость

ZIP не перекодирует текст документов. В UTF-8 строке размер измеряется байтами; strlen не считает видимые буквы. Имя записи — отдельная строка со своей кодировкой. FL_ENC_UTF_8 сообщает библиотеке кодировку имени, но не меняет Windows-кодировку пути на диске и не исправляет уже повреждённые байты.

Две независимые строки
  1. Имя: чек.txt (UTF-8)
  2. Содержимое: оплачен (UTF-8)
  3. FL_ENC_UTF_8 относится к имени
  4. getFromName возвращает исходное содержимое
Русское имя и текст без преобразованияСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString("чек.txt", "оплачен", ZipArchive::FL_ENC_UTF_8);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getNameIndex(0), ":", $zip->getFromName("чек.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
чек.txt:оплачен

Файл урока сохранён в UTF-8. В другом терминале вывод может отображаться неверно даже при корректных байтах: это отдельная настройка отображения.

При интеграции с чужим архиватором сначала договоритесь о UTF-8. Не заменяйте русские имена транслитерацией автоматически: это может создавать коллизии. Содержимое CSV/JSON декодируется своим соответствующим инструментом после чтения ZIP.

От одного чека к пакету и обёртке zip://

Официальные примеры показывают создание ZIP, просмотр записей, чтение отдельных файлов через zip:// и старый ресурсный обход. Ниже — такой же путь на собственном маленьком пакете: собираем два документа внутри функции, затем считаем только содержимое выбранной записи. zip:// — локальная обёртка потока; часть после # обозначает запись архива, а не URL-фрагмент сайта.

Готовая поставка
  1. buildOrderBundle(path,42) получает место и заказ
  2. Записывает order.txt и README.txt
  3. Проверяет close и возвращает управление
  4. zip://...#order.txt читает один документ
Два документа как одна прикладная операцияСамостоятельный пример
<?php
declare(strict_types=1);

function buildOrderBundle(string $path, int $orderId): void {
    $zip = new ZipArchive();
    if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
        throw new RuntimeException('Не создан пакет');
    }
    if (!$zip->addFromString('order.txt', 'paid:' . $orderId)
        || !$zip->addFromString('README.txt', 'Сохраните чек.')) {
        $zip->unchangeAll();
        $zip->close();
        throw new RuntimeException('Документы не добавлены');
    }
    if (!$zip->close()) throw new RuntimeException('Пакет не сохранён');
}
$path = __DIR__ . '/bundle.zip';
buildOrderBundle($path, 42);
$body = file_get_contents('zip://' . $path . '#order.txt');
if ($body === false) throw new RuntimeException('Нет чека');
echo $body, "\n";
unlink($path);
Ожидаемый результат
paid:42

Параметр $path получает конкретный путь, $orderId — 42; тело добавляет оба документа, void возвращает управление без значения. || останавливает проверку после первого отказа. Ошибка добавления отменяет подготовленное перед закрытием.

Прочитать XML-файл внутри своего контейнераСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString("meta.xml", '<order id="42"/>');
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$stream = fopen('zip://' . $path . '#meta.xml', 'rb');
if ($stream === false) throw new RuntimeException('Нет метаданных');
$xml = stream_get_contents($stream, 128);
fclose($stream);
if ($xml === false) throw new RuntimeException('Не прочитано');
echo $xml, "\n";
unlink($path);
Ожидаемый результат
<order id="42"/>

Это чтение XML-байтов без парсинга, не проверка XML-схемы. Так устроен доступ к частям OpenDocument (например ODT с meta.xml) и OOXML-контейнера: для интерпретации XML используйте отдельный XML-парсер с собственной политикой внешних сущностей. Не include/require содержимое недоверенного ZIP.

Следующая ступень — разобрать XML, а не только получить строку. XMLReader читает узлы последовательно: read() передвигается к следующему, nodeType отличает элемент, localName и namespaceURI выбирают нужное поле, readString() получает его текст. Это отдельное расширение xmlreader, уже загруженное в проверенном окружении. Для своего мини-примера создадим ZIP с meta.xml в стиле OpenDocument; это не полноценный ODT-документ.

Метаданные контейнера
  1. meta.xml содержит dc:title
  2. XMLReader открывает zip://...#meta.xml
  3. read() идёт по узлам
  4. Элемент title в Dublin Core → readString()
  5. Получаем «Заказ 42»
Извлечь название из XML-метаданныхСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$xml = '<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '
    . 'xmlns:dc="http://purl.org/dc/elements/1.1/">'
    . '<office:meta><dc:title>Заказ 42</dc:title></office:meta></office:document-meta>';
$zip->addFromString('meta.xml', $xml);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$reader = new XMLReader();
if (!$reader->open('zip://' . $path . '#meta.xml', null, LIBXML_NONET)) {
    throw new RuntimeException('Не открыты метаданные');
}
$reader->setParserProperty(XMLReader::LOADDTD, false);
$reader->setParserProperty(XMLReader::SUBST_ENTITIES, false);
$title = null;
try {
    while ($reader->read()) {
        if ($reader->nodeType === XMLReader::ELEMENT
            && $reader->localName === 'title'
            && $reader->namespaceURI === 'http://purl.org/dc/elements/1.1/') {
            $title = $reader->readString();
            break;
        }
    }
} finally { $reader->close(); }
if ($title === null) throw new RuntimeException('Нет названия');
echo $title, "\n";
unlink($path);
Ожидаемый результат
Заказ 42

Namespace URI здесь идентификатор словаря, а не сетевой запрос. Не включены загрузка DTD и подстановка сущностей; LIBXML_NONET запрещает сетевую загрузку libxml. Для внешних документов дополнительно ограничивают ZIP/XML-размеры и обрабатывают ошибки XML. Код читает только собственные маленькие метаданные.

Безопасный импорт: имена, лимиты, перезаписи

Импортёр не должен угадывать, какие пути хотел создать автор архива. Для конкретного обмена проще принимать только точные ожидаемые имена и читать их потоком в память/свой выбранный файл, не распаковывая всё дерево. Так имена из ZIP вообще не становятся путями назначения. PHP 8.5.11 нормализует пути extractTo, но политика приложения всё равно должна предотвращать перезаписи, коллизии и неожиданную структуру.

Узкий импорт заказа
  1. Разрешено только order.txt
  2. Проверить число и размер записей
  3. Прочитать не более лимита+1 байтов
  4. Отказать при превышении
  5. Использовать текст как данные, не исполнять
Импорт только известного чека без распаковкиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
function readOrderReceipt(ZipArchive $zip): string {
    if ($zip->count() !== 1 || $zip->getNameIndex(0) !== 'order.txt') {
        throw new RuntimeException('Неожиданный состав');
    }
    $info = $zip->statIndex(0);
    if ($info === false || $info['size'] > 1024) throw new RuntimeException('Размер');
    $stream = $zip->getStreamIndex(0);
    if ($stream === false) throw new RuntimeException('Не прочитать');
    try { $body = stream_get_contents($stream, 1025); }
    finally { fclose($stream); }
    if ($body === false || strlen($body) > 1024) throw new RuntimeException('Лимит');
    return $body;
}
echo readOrderReceipt($zip), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Имя не используется как путь назначения. Даже если метаданные размера недостоверны, само чтение ограничено. Квоты времени/CPU, размер загруженного ZIP и параллелизм настраиваются дополнительно; этот маленький пример не является общим антивирусом.

Риск при распаковке дереваПолитика приложения
Zip Slip / path traversalОтклонять абсолютные пути, буквы дисков/UNC, NUL, сегменты .., обратные слеши и неожиданные компоненты. Не «лечить» плохой путь basename: разные имена могут превратиться в одно.
Коллизии и перезаписьНовая приватная пустая директория вне web root; разрешённый список имён; учёт регистра/Unicode-нормализации/конечных точек и Windows reserved names. Не извлекать поверх пользовательских файлов.
Символьные ссылки и внешние атрибутыПроверять тип записи и разрешать только ожидаемые обычные файлы/каталоги. Не восстанавливать присланные права и не следовать существующим ссылкам в каталоге назначения.
Архивная бомбаОграничить входной размер, число записей, заявленный и реально прочитанный объём, время/CPU и глубину; не распаковывать вложенные архивы рекурсивно без отдельных лимитов. Один коэффициент сжатия недостаточен.
Частичный отказextractTo не транзакция. Собирать в отдельной staging-папке, проверять все результаты; только после успеха предоставлять пакет. Убирать только собственные созданные файлы.
Недоверенные данныеНе исполнять PHP из ZIP, не публиковать активный HTML без политики; антивирус/проверка формата/доступ применяются по назначению системы. CRC не подтверждает автора.

Старые ресурсы, современные объекты и миграция

У старого API два разных ресурса: zip_open возвращает ресурс каталога архива, zip_read — ресурс отдельной записи. Обычный поток от getStream — третий вид ресурса, который закрывают fclose. Эти значения не взаимозаменяемы. Все 10 процедурных ZIP-функций deprecated с PHP 8.0; ниже они сохранены полностью для понимания старых проектов и миграции.

Переход к современному API
  1. zip_open → new ZipArchive + open
  2. zip_read → обход индексов / locateName
  3. zip_entry_read → getFromName или getStream
  4. zip_entry_close → fclose для современного потока
  5. zip_close → проверенный ZipArchive::close
Старая задачаСовременный путь
Имя, размер, метод сжатияgetNameIndex + statIndex/statName
Небольшой документgetFromName с проверкой === false
Крупный документgetStream + ограниченное чтение + fclose
Извлечь выбранные документыПолитика имён/размеров → extractTo или управляемая потоковая запись

Все константы: флаги, алгоритмы, ошибки и системы

Именованная константа заменяет непонятное число и показывает намерение: ZipArchive::CREATE. Но разные семейства нельзя смешивать. Флаги open(), flags конкретного метода, алгоритм CM_* и ошибка ER_* решают разные задачи. Наличие константы в классе не обещает поддержку кодека или любого сочетания флагов.

Три разных выбора
  1. CREATE|EXCL → как открыть lesson.zip
  2. CM_DEFLATE → как сжать order.txt
  3. ER_NOENT → почему поиск missing.txt не удался

Режимы открытия

КонстантаЗначение для читателя
ZipArchive::CREATEСоздать при отсутствии, не очищая существующий ZIP.
ZipArchive::OVERWRITEСчитать прежнее содержимое пустым; уничтожение старых записей при сохранении.
ZipArchive::EXCLОтказать, если файл уже существует.
ZipArchive::RDONLYОткрыть для чтения. PHP 7.4.3 / PECL 1.17.1; libzip 1.0+.
ZipArchive::CHECKCONSДополнительно проверить согласованность структуры при открытии.

Общие признаки (PHP 8.3 / PECL 1.22, libzip 1.10+)

КонстантаЗначение для читателя
ZipArchive::AFL_RDONLYАрхив только для чтения. Режим от open(RDONLY) снять нельзя; вручную установленный флаг libzip позволяет снять обратно.
ZipArchive::AFL_IS_TORRENTZIPНаблюдаемый признак уже существующего формата TorrentZip.
ZipArchive::AFL_WANT_TORRENTZIPЗапросить запись в формате TorrentZip.
ZipArchive::AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVEСохранить файл ZIP даже при отсутствии записей.

Флаги операций — только там, где разрешены сигнатурой/договором

КонстантаЗначение для читателя
ZipArchive::FL_NOCASEИгнорировать регистр имени при поиске.
ZipArchive::FL_NODIRНе учитывать каталог при поиске имени.
ZipArchive::FL_COMPRESSEDПолучить сжатые байты вместо распаковки.
ZipArchive::FL_UNCHANGEDОбратиться к исходному состоянию при open(), не отменяя подготовленное.
ZipArchive::FL_RECOMPRESSПринудительное повторное сжатие; PHP 8.0 / PECL 1.18, deprecated PHP 8.3 / PECL 1.22.1; будущая libzip может убрать.
ZipArchive::FL_ENCRYPTEDПолучить шифрованные байты; подразумевает FL_COMPRESSED. PHP 8.0 / PECL 1.18.
ZipArchive::FL_OVERWRITEРазрешить замену одноимённой записи. PHP 8.0 / PECL 1.18.
ZipArchive::FL_LOCALОбращение к локальному заголовку, только для поддерживающей это операции. PHP 8.0 / PECL 1.18.
ZipArchive::FL_CENTRALОбращение к центральному каталогу, только для поддерживающей это операции. PHP 8.0 / PECL 1.18.
ZipArchive::FL_ENC_GUESSОпределить кодировку строк (0, стандартно); PHP 7.0.8.
ZipArchive::FL_ENC_RAWВернуть исходные байты строки без преобразования; PHP 7.0.8.
ZipArchive::FL_ENC_STRICTСледовать правилам формата для кодировки строго; PHP 7.0.8.
ZipArchive::FL_ENC_UTF_8Строка имени задана в UTF-8; PHP 7.0.8.
ZipArchive::FL_ENC_CP437Строка имени задана в CP437; PHP 7.0.8.
ZipArchive::FL_OPEN_FILE_NOWОткрыть добавляемый файл сразу, расходуя дескриптор; PHP 8.3 / PECL 1.22.1.

Способы сжатия

КонстантаЗначение для читателя
ZipArchive::CM_DEFAULTВыбор libzip: PHP описывает лучший из deflate/store; текущая документация libzip — deflate с игнорированием flags. Для точного метода выбирайте его явно.
ZipArchive::CM_STOREБез сжатия.
ZipArchive::CM_SHRINKИсторический Shrink.
ZipArchive::CM_REDUCE_1Исторический Reduce, коэффициент 1.
ZipArchive::CM_REDUCE_2Reduce, коэффициент 2.
ZipArchive::CM_REDUCE_3Reduce, коэффициент 3.
ZipArchive::CM_REDUCE_4Reduce, коэффициент 4.
ZipArchive::CM_IMPLODEИсторический Implode.
ZipArchive::CM_DEFLATEРаспространённый Deflate.
ZipArchive::CM_DEFLATE64Deflate64, отдельный алгоритм; не заменяет Deflate автоматически.
ZipArchive::CM_PKWARE_IMPLODEPKWARE DCL Implode.
ZipArchive::CM_BZIP2Bzip2.
ZipArchive::CM_LZMALZMA.
ZipArchive::CM_LZMA2LZMA2; PHP 7.4.3 / PECL 1.16.0; libzip 1.6+.
ZipArchive::CM_ZSTDZstandard; PHP 8.0 / PECL 1.19.1; libzip 1.8+.
ZipArchive::CM_XZXZ; PHP 7.4.3 / PECL 1.16.1; libzip 1.6+.
ZipArchive::CM_TERSEИдентификатор TERSE (существование не обещает поддержку).
ZipArchive::CM_LZ77Идентификатор LZ77.
ZipArchive::CM_WAVPACKИдентификатор WavPack.
ZipArchive::CM_PPMDИдентификатор PPMd.

Коды ошибок

КонстантаЗначение для читателя
ZipArchive::ER_OKОтказа нет.
ZipArchive::ER_MULTIDISKМноготомный ZIP не поддерживается.
ZipArchive::ER_RENAMEНе получилось переименовать временный файл.
ZipArchive::ER_CLOSEНе получилось закрыть ZIP.
ZipArchive::ER_SEEKОшибка перемещения позиции чтения.
ZipArchive::ER_READОшибка чтения.
ZipArchive::ER_WRITEОшибка записи.
ZipArchive::ER_CRCНе совпала контрольная сумма CRC.
ZipArchive::ER_ZIPCLOSEDСодержащий запись архив уже закрыт.
ZipArchive::ER_NOENTФайл/запись не найдены.
ZipArchive::ER_EXISTSИмя уже существует.
ZipArchive::ER_OPENНе удалось открыть файл.
ZipArchive::ER_TMPOPENНе удалось создать временный файл для записи ZIP.
ZipArchive::ER_ZLIBОшибка библиотеки zlib.
ZipArchive::ER_MEMORYНе удалось выделить память.
ZipArchive::ER_CHANGEDТребуются данные записи, которая изменена.
ZipArchive::ER_COMPNOTSUPPСпособ сжатия не поддерживается.
ZipArchive::ER_EOFПреждевременный конец данных.
ZipArchive::ER_INVALНекорректный аргумент libzip.
ZipArchive::ER_NOZIPДанные не являются ZIP.
ZipArchive::ER_INTERNALВнутренняя ошибка.
ZipArchive::ER_INCONSСтруктура ZIP несогласованна.
ZipArchive::ER_REMOVEНе удалось удалить файл.
ZipArchive::ER_DELETEDЗапись помечена удалённой.
ZipArchive::ER_ENCRNOTSUPPШифрование не поддерживается.
ZipArchive::ER_RDONLYПопытка изменения read-only архива.
ZipArchive::ER_NOPASSWDДля чтения не предоставлен пароль.
ZipArchive::ER_WRONGPASSWDПароль не подошёл.
ZipArchive::ER_OPNOTSUPPОперация не поддерживается.
ZipArchive::ER_INUSEРесурс всё ещё используется.
ZipArchive::ER_TELLНе удалось узнать позицию.
ZipArchive::ER_COMPRESSED_DATAНекорректные сжатые данные.
ZipArchive::ER_CANCELLEDОперация отменена callback.
ZipArchive::ER_DATA_LENGTHРазмер данных изменился вопреки договору.
ZipArchive::ER_NOT_ALLOWEDОперация несовместима с TorrentZip.
ZipArchive::ER_TRUNCATED_ZIPZIP возможно обрезан или повреждён.

Способы шифрования

КонстантаЗначение для читателя
ZipArchive::EM_NONEБез шифрования; PHP 7.2 / PECL 1.14.
ZipArchive::EM_TRAD_PKWAREТрадиционное слабое PKWARE; PHP 8.0 / PECL 1.19.
ZipArchive::EM_AES_128AES-128; PHP 7.2 / PECL 1.14; libzip 1.2+.
ZipArchive::EM_AES_192AES-192; PHP 7.2 / PECL 1.14; libzip 1.2+.
ZipArchive::EM_AES_256AES-256; PHP 7.2 / PECL 1.14; libzip 1.2+.
ZipArchive::EM_UNKNOWNНеизвестный алгоритм, не выбор для нового шифрования; PHP 8.0 / PECL 1.19.

Длина и версия библиотеки

КонстантаЗначение для читателя
ZipArchive::LENGTH_TO_END0: фиксировать доступный размер источника; рост игнорируется, уменьшение — ER_DATA_LENGTH. PHP 8.3 / PECL 1.22.2.
ZipArchive::LENGTH_UNCHECKEDПрочитать все доступные данные источника с указанного start. PHP 8.3 / PECL 1.22.2; libzip 1.10.1+.
ZipArchive::LIBZIP_VERSIONСтроковая версия связанной libzip; PHP 7.4.3 / PECL 1.16.0. Остальные перечисленные константы имеют тип int.

Происхождение внешних атрибутов (PECL 1.12.4+)

КонстантаЗначение для читателя
ZipArchive::OPSYS_DOSDOS
ZipArchive::OPSYS_AMIGAAmiga
ZipArchive::OPSYS_OPENVMSOpenVMS
ZipArchive::OPSYS_UNIXUNIX
ZipArchive::OPSYS_VM_CMSVM/CMS
ZipArchive::OPSYS_ATARI_STAtari ST
ZipArchive::OPSYS_OS_2OS/2
ZipArchive::OPSYS_MACINTOSHклассическая Macintosh
ZipArchive::OPSYS_Z_SYSTEMZ-System
ZipArchive::OPSYS_CPMCP/M
ZipArchive::OPSYS_WINDOWS_NTFSWindows NTFS
ZipArchive::OPSYS_MVSMVS
ZipArchive::OPSYS_VSEVSE
ZipArchive::OPSYS_ACORN_RISCAcorn RISC
ZipArchive::OPSYS_VFATVFAT
ZipArchive::OPSYS_ALTERNATE_MVSальтернативный MVS
ZipArchive::OPSYS_BEOSBeOS
ZipArchive::OPSYS_TANDEMTandem
ZipArchive::OPSYS_OS_400OS/400
ZipArchive::OPSYS_OS_XOS X
ZipArchive::OPSYS_DEFAULTсистема по умолчанию в сборке

Версии новых ошибок: ER_ENCRNOTSUPP, ER_RDONLY, ER_NOPASSWD, ER_WRONGPASSWD — PHP 7.4.3 / PECL 1.16.1. ER_OPNOTSUPP, ER_INUSE, ER_TELL дополнительно libzip 1.0+. ER_COMPRESSED_DATA и ER_CANCELLED — libzip 1.6+. ER_DATA_LENGTH и ER_NOT_ALLOWED — PHP 8.3 / PECL 1.22, libzip 1.10+. ER_TRUNCATED_ZIP — PHP 8.4 / PECL 1.22.4, libzip 1.11.1+. Старые ошибки перечислены без искусственного обещания точной версии появления.

PHP-страница кратко говорит, что AFL_RDONLY нельзя снять. Уточнение libzip: нельзя снять read-only от open(RDONLY); вручную установленный setArchiveFlag флаг можно снять. AFL_IS_TORRENTZIP доступен только для наблюдения. OPSYS_* — формат трактовки сохранённых атрибутов, не команда поменять ОС и не проверка текущей ОС.

Проверить независимые семьиСамостоятельный пример
<?php
declare(strict_types=1);

$flags = ZipArchive::CREATE | ZipArchive::EXCL;
echo ($flags & ZipArchive::CREATE) !== 0 ? "Создание разрешено\n" : "Нет\n";
echo ZipArchive::ER_OK === 0 ? "Код успеха 0\n" : "Другой код\n";
Ожидаемый результат
Создание разрешено
Код успеха 0

| объединяет биты флагов, & проверяет их наличие. ER_OK — значение статуса; успешный open всё равно сравнивают с true, не с ER_OK.

Практика, стоимость и следующий шаг

Упражнение: получатель требует только order.txt, но в вашем текущем пакете ещё есть notes.txt. До сохранения удалите notes.txt, смените комментарий архива на «Выпуск 42», закройте с проверкой и убедитесь, что остался один документ. Не меняйте исходные файлы на диске.

Решение упражнения
  1. Добавлены order.txt и notes.txt
  2. Удалена запись notes.txt
  3. Комментарий стал «Выпуск 42»
  4. close/open → один документ и комментарий
Решение: подготовить минимальную поставкуСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->addFromString('notes.txt', 'Внутренняя заметка');
if (!$zip->deleteName('notes.txt')) throw new RuntimeException('Не удалено');
if (!$zip->setArchiveComment('Выпуск 42')) throw new RuntimeException('Нет комментария');
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->count(), ":", $zip->getNameIndex(0), "\n", $zip->getArchiveComment(), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
1:order.txt
Выпуск 42

Удаление относится к записи, не к файлу notes.txt на компьютере. Для усложнения: при отказе добавления второго документа отмените всю партию через unchangeAll().

Для больших поставок addFile уменьшает необходимость держать всё содержимое строками; потоки ограничивают память чтения. При изменении существующего архива может потребоваться переписать его в новый временный файл. Измеряйте время, свободное место и дескрипторы на реальном наборе данных; «ZIP всегда меньше/быстрее» неверно.

Дальше: выбор файлов, шифрование отдельных записей, отмена долгой записи.

Полный каталог методов и исторических функций

Карточки ниже сохраняют всё официальное дерево ZIP. Name-методы выбирают по имени, Index-методы — по индексу текущего открытого архива. У каждого вызова есть договор, модель, два самостоятельных примера и точная проверка результата. Устаревшие функции выделены версией deprecated; они не предлагаются для нового кода.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

ZipArchive::openСобрать чек и памятку в один файл или открыть готовую поставку.

PHP 5.2+, PECL zip 1.1+; RDONLY: PHP 7.4.3 / PECL 1.17.1, libzip 1.0+ · zip

open(string $filename, int $flags = 0): bool|int

Собрать чек и памятку в один файл или открыть готовую поставку.

Синтаксис: open(string $filename, int $flags = 0): bool|int. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
filenameПуть ZIP в файловой системе; это не имя одного файла внутри ZIP. Непустой локальный путь.
flags0 открывает существующий ZIP; CREATE создаёт отсутствующий, сохраняя существующий; OVERWRITE отбрасывает старое содержимое; CREATE|OVERWRITE допускает новый или существующий. CREATE|EXCL запрещает заменить существующий. RDONLY — чтение, CHECKCONS — дополнительные проверки структуры.

Результат: Именно true означает успех. Целочисленный ER_* (в том числе ненулевой и потому truthy) или false означает отказ.

Отказ: Пустое имя — ValueError; доступ/путь могут дать false и warning; ошибки libzip — код ER_EXISTS, ER_INCONS, ER_INVAL, ER_MEMORY, ER_NOENT, ER_NOZIP, ER_OPEN, ER_READ, ER_SEEK. Проверяйте результат, try/catch один не заменяет проверку.

Как получить первый результат
  1. lesson.zip ещё нет
  2. CREATE|EXCL открывает новый архив без разрешения замены
  3. order.txt добавлен
  4. close() записывает архив
Не затереть чужой архивСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
echo "Подготовлен чек\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Подготовлен чек

new создаёт объект управления, -> вызывает его метод; :: обращается к именованному флагу класса. Проверка !== true нужна из-за целочисленных ошибок.

Понятная ветка для отсутствующей поставкиСамостоятельный пример
<?php
declare(strict_types=1);

$zip = new ZipArchive();
$result = $zip->open(__DIR__ . '/missing.zip', ZipArchive::RDONLY);
echo $result === ZipArchive::ER_NOENT ? "Нет поставки\n" : "Другой результат\n";
Ожидаемый результат
Нет поставки

Проверяется конкретный код; отсутствующий ZIP не создаётся.

Нулевой файл от tempnam() не является пустым корректным ZIP: libzip 1.6+ его отвергает; PHP 8.5.11 сохраняет deprecated-совместимость для открытия на запись без OVERWRITE. Для собственного уже выделенного пустого файла применяют OVERWRITE, понимая уничтожение его содержимого. Новый архив создаётся при close(), а не обязательно при open(). Повторный open() того же объекта сначала закрывает предыдущий архив: предпочтительнее явный проверенный close().

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::close, ZipArchive::getStatusString.

ZipArchive::closeПодтвердить, что подготовленная выгрузка действительно сохранена.

PHP 5.2+; PECL zip · zip

close(): bool

Подтвердить, что подготовленная выгрузка действительно сохранена.

Синтаксис: close(): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
Нет аргументовСкобки () обязательны.

Результат: true — закрытие успешно; false — сохранить не удалось. Объект после попытки close() больше не держит открытый архив.

Отказ: Ошибка записи, чтения добавленного источника, временного файла, переименования или отмена callback: warning и false; диагностический status доступен после закрытия (PHP 8.0+).

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Создать lesson.zip
  2. Добавить order.txt
  3. Проверить close()
  4. Повторно открыть и прочитать paid:42
Проверить сохранение чтениемСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

В подготовке close() уже проверен, затем открыт сохранённый файл. После второго close() удаляется только учебный ZIP.

Пустой архив по умолчанию не остаётся на дискеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
echo file_exists($path) ? "есть\n" : "нет записей — нет файла\n";
Ожидаемый результат
нет записей — нет файла

Не добавили ни одной записи: обычная политика libzip удаляет пустой архив. Для намеренно пустого ZIP нужен AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE.

Завершение скрипта также закрывает объект, но тогда пропускается удобная проверка результата. В PHP 8.5.11 неудачный close() отбрасывает подготовленные изменения и освобождает архив; повторять close() на нём нельзя. Источники addFile()/replaceFile() должны жить до завершения записи. Не удаляйте их после одного true от addFile().

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::open, ZipArchive::setArchiveFlag, ZipArchive::registerCancelCallback.

ZipArchive::addEmptyDirПередать получателю пустую папку для будущих актов.

PHP 5.2+, PECL 1.8+; flags PHP 8.0 / PECL 1.18+ · zip

addEmptyDir(string $dirname, int $flags = 0): bool

Передать получателю пустую папку для будущих актов.

Синтаксис: addEmptyDir(string $dirname, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
dirnameИмя папки внутри ZIP, например acts; метод создаёт запись каталога с конечным /.
flagsFL_ENC_GUESS (0: определить кодировку), FL_ENC_UTF_8 или FL_ENC_CP437 задают кодировку имени записи. Это не перекодировка содержимого файла.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при конфликте/отказе libzip или пустом имени (PHP 8.5.11).

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. lesson.zip открыт
  2. acts становится acts/
  3. Это запись каталога, не mkdir на компьютере
Папка для актовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addEmptyDir("acts");
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
acts/

Внешняя папка acts не создаётся: меняется только структура архива.

Русская папка получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addEmptyDir("Акты", ZipArchive::FL_ENC_UTF_8);
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Акты/

Явно указан UTF-8 для имени; содержимого у пустой папки нет.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::addFile, ZipArchive::getNameIndex.

ZipArchive::addFromStringДобавить сформированный в PHP чек без промежуточного файла.

PHP 5.2+, PECL 1.1+; flags PHP 8.0 / PECL 1.18+ · zip

addFromString(string $name, string $content, int $flags = ZipArchive::FL_OVERWRITE): bool

Добавить сформированный в PHP чек без промежуточного файла.

Синтаксис: addFromString(string $name, string $content, int $flags = ZipArchive::FL_OVERWRITE): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИмя записи внутри ZIP.
contentГотовые байты, включая NUL; функция не кодирует JSON/текст сама.
flagsFL_OVERWRITE разрешает замену одноимённой записи; 0 запрещает. FL_ENC_GUESS (0: определить кодировку), FL_ENC_UTF_8 или FL_ENC_CP437 задают кодировку имени записи. Это не перекодировка содержимого файла.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при конфликте без FL_OVERWRITE или ошибке libzip. PHP 8.5.11 / libzip 1.11.4 допускает даже пустое имя здесь: приложение должно само требовать непустое безопасное имя, не рассчитывая на ValueError.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. paid:42 — строка в памяти
  2. order.txt — имя в ZIP
  3. addFromString связывает имя и байты
  4. После close()/open() строка читается
Сохранить готовый чекСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Перед чтением повторно открываем сохранённую версию.

Отменённый заказ заменяет прежний статусСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->addFromString("order.txt", "cancelled:42");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
cancelled:42

Значение flags по умолчанию допускает замену. Для запрета дубликата передайте 0 и проверьте false.

Вся строка уже находится в памяти; большой диск-файл выгоднее передать addFile(), а не читать его целиком ради этой функции. Наличие записи после добавления не обещает чтение её новых байтов до close()/open(). Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::addFile, ZipArchive::getFromName.

ZipArchive::addFileВключить готовый прайс-лист с диска в поставку.

PHP 5.2 / PECL 1.1+; flags PHP 8.0 / PECL 1.18+ · zip

addFile(string $filepath, string $entryname = "", int $start = 0, int $length = ZipArchive::LENGTH_TO_END, int $flags = ZipArchive::FL_OVERWRITE): bool

Включить готовый прайс-лист с диска в поставку.

Синтаксис: addFile(string $filepath, string $entryname = "", int $start = 0, int $length = ZipArchive::LENGTH_TO_END, int $flags = ZipArchive::FL_OVERWRITE): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
filepathСуществующий читаемый файл на диске; сохраните его до close().
entrynameИмя в ZIP; пустое использует filepath, поэтому лучше задать короткое переносимое имя.
startСмещение в байтах от начала файла.
length0 / LENGTH_TO_END — размер на момент добавления: последующий рост игнорируется, уменьшение вызывает ошибку при записи. LENGTH_UNCHECKED — все доступные байты с start (PHP 8.3 / PECL 1.22.2, libzip 1.10.1).
flagsFL_OVERWRITE разрешает замену; FL_ENC_GUESS (0: определить кодировку), FL_ENC_UTF_8 или FL_ENC_CP437 задают кодировку имени записи. Это не перекодировка содержимого файла. FL_OPEN_FILE_NOW открывает источник сразу (8.3 / 1.22.1).

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при недоступном файле/индексе/ошибке; пустой filepath — ValueError, отрицательный index replaceFile — ValueError. Поздняя ошибка чтения может возникнуть только на close().

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. source.csv содержит SKU;42
  2. Файл остаётся на диске до close()
  3. Байты попадают в price.csv
  4. Повторное открытие проверяет результат
Дисковый прайс-листСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFile(__DIR__ . "/source.csv", "price.csv");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("price.csv"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
SKU;42

Файл-источник и имя записи — разные вещи. Входной source.csv дан рядом с примером.

Передать только полезный фрагментСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFile(__DIR__ . "/slice.txt", "number.txt", 7, 2);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
42

Первые 7 ASCII-байтов prefix: пропускаются; длина 2 оставляет 42. Для UTF-8 байтовый разрез может повредить символ.

PHP удерживает источник до закрытия архива. FL_OPEN_FILE_NOW позволяет раньше обнаружить отказ открытия, но при множестве файлов расходует дескрипторы; он не делает файл неизменным. Не меняйте источник параллельно. Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::close, ZipArchive::addFromString.

ZipArchive::replaceFileЗаменить байты прайс-листа, сохранив его имя в ZIP.

PHP 8.0 / PECL 1.18+ · zip

replaceFile(string $filepath, int $index, int $start = 0, int $length = ZipArchive::LENGTH_TO_END, int $flags = 0): bool

Заменить байты прайс-листа, сохранив его имя в ZIP.

Синтаксис: replaceFile(string $filepath, int $index, int $start = 0, int $length = ZipArchive::LENGTH_TO_END, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
filepathСуществующий читаемый файл на диске; сохраните его до close().
indexИндекс существующей записи; имя не меняется.
startСмещение в байтах от начала файла.
length0 / LENGTH_TO_END — размер на момент добавления: последующий рост игнорируется, уменьшение вызывает ошибку при записи. LENGTH_UNCHECKED — все доступные байты с start (PHP 8.3 / PECL 1.22.2, libzip 1.10.1).
flagsДопустимы FL_ENC_GUESS, FL_ENC_UTF_8, FL_ENC_CP437 и FL_OPEN_FILE_NOW. Последний открывает источник сразу (PHP 8.3 / PECL 1.22.1); замена не меняет имя записи.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при недоступном файле/индексе/ошибке; пустой filepath — ValueError, отрицательный index replaceFile — ValueError. Поздняя ошибка чтения может возникнуть только на close().

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. source.csv содержит SKU;42
  2. Файл остаётся на диске до close()
  3. Байты попадают в order.txt
  4. Повторное открытие проверяет результат
Дисковый прайс-листСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->replaceFile(__DIR__ . "/source.csv", 0);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
SKU;42

Файл-источник и имя записи — разные вещи. Входной source.csv дан рядом с примером.

Передать только полезный фрагментСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->replaceFile(__DIR__ . "/slice.txt", 0, 7, 2);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
42

Первые 7 ASCII-байтов prefix: пропускаются; длина 2 оставляет 42. Для UTF-8 байтовый разрез может повредить символ.

PHP удерживает источник до закрытия архива. FL_OPEN_FILE_NOW позволяет раньше обнаружить отказ открытия, но при множестве файлов расходует дескрипторы; он не делает файл неизменным. Не меняйте источник параллельно. Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::close, ZipArchive::addFromString.

ZipArchive::addGlobСобрать выбранные файлы каталога в одну поставку без ручного addFile для каждого.

PHP 5.3 / PECL zip 1.9+ · zip

addGlob(string $pattern, int $flags = 0, array $options = []): array|false

Собрать выбранные файлы каталога в одну поставку без ручного addFile для каждого.

Синтаксис: addGlob(string $pattern, int $flags = 0, array $options = []): array|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
patternМаска glob для пути, например input/*.csv; это не регулярное выражение.
flagsФлаги glob(), например GLOB_NOSORT; это НЕ FL_* ZipArchive.
options: путиremove_path удаляет заданный префикс; remove_all_path=true оставляет basename; add_path добавляется после удаления, например prices/.
options: записьflags — ZIP-флаги замены/кодировки/раннего открытия (PHP 8.0 / PECL 1.18).
options: кодекиcomp_method, comp_flags задают сжатие; enc_method, enc_password — шифрование (PHP 8.0 / PECL 1.18.1). Пароль не передавайте в логи.

Результат: Массив имён добавленных исходных файлов или false; [] означает отсутствие совпадений, а не true.

Отказ: false при отказе поиска/добавления; ошибки PCRE/файлов могут сопровождаться warning. Пустой pattern — ValueError; неправильный тип опций — TypeError/ValueError. Частичное добавление до ошибки возможно: используйте unchangeAll() для отказа от партии.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. input/a.csv подходит маске
  2. input/a.txt не подходит
  3. Исходный путь сокращается
  4. В ZIP получается prices/a.csv
Выбрать прайс-листыСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
chdir(__DIR__);
$added = $zip->addGlob("input/*.csv", 0, ["remove_path" => "input/", "add_path" => "prices/"]);
if ($added === false) throw new RuntimeException("Пакет не собран");
echo count($added), ":", $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
1:prices/a.csv

remove_path/remove_all_path меняют имя в ZIP, не перемещают исходные файлы. Папки и файлы fixtures должны существовать перед запуском.

Нет документов для выбранной группыСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
chdir(__DIR__);
$added = $zip->addGlob("input/*.txt", 0, ["remove_all_path" => true]);
echo $added === [] ? "Нет документов\n" : "Другая ветка\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
Ожидаемый результат
Нет документов

Пустая выборка обработана отдельно от false. Пустой ZIP после close() не остаётся.

Эти вызовы не являются рекурсивным архиватором дерева. Каталог проекта и путь архивного имени разделяйте; не начинайте add_path с /. При одинаковых basename можно перезаписать запись: определите политику дубликатов. Для отдельного выбранного файла проще addFile().

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::addFile, ZipArchive::unchangeAll.

ZipArchive::addPatternСобрать выбранные файлы каталога в одну поставку без ручного addFile для каждого.

PHP 5.3 / PECL zip 1.9+ · zip

addPattern(string $pattern, string $path = ".", array $options = []): array|false

Собрать выбранные файлы каталога в одну поставку без ручного addFile для каждого.

Синтаксис: addPattern(string $pattern, string $path = ".", array $options = []): array|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
patternPCRE с разделителями, например /\.csv$/; проверяет только имя файла, не полный путь.
pathКаталог поиска, по умолчанию текущий рабочий каталог. Не рекурсивно.
options: путиremove_path удаляет заданный префикс; remove_all_path=true оставляет basename; add_path добавляется после удаления, например prices/.
options: записьflags — ZIP-флаги замены/кодировки/раннего открытия (PHP 8.0 / PECL 1.18).
options: кодекиcomp_method, comp_flags задают сжатие; enc_method, enc_password — шифрование (PHP 8.0 / PECL 1.18.1). Пароль не передавайте в логи.

Результат: Массив имён добавленных исходных файлов или false; [] означает отсутствие совпадений, а не true.

Отказ: false при отказе поиска/добавления; ошибки PCRE/файлов могут сопровождаться warning. Пустой pattern — ValueError; неправильный тип опций — TypeError/ValueError. Частичное добавление до ошибки возможно: используйте unchangeAll() для отказа от партии.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. input/a.csv подходит маске
  2. input/a.txt не подходит
  3. Исходный путь сокращается
  4. В ZIP получается prices/a.csv
Выбрать прайс-листыСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
chdir(__DIR__);
$added = $zip->addPattern("/\.csv$/", "input", ["remove_all_path" => true, "add_path" => "prices/"]);
if ($added === false) throw new RuntimeException("Пакет не собран");
echo count($added), ":", $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
1:prices/a.csv

remove_path/remove_all_path меняют имя в ZIP, не перемещают исходные файлы. Папки и файлы fixtures должны существовать перед запуском.

Нет документов для выбранной группыСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
chdir(__DIR__);
$added = $zip->addPattern("/^invoice-.*\.txt$/", "input", ["remove_all_path" => true]);
echo $added === [] ? "Нет документов\n" : "Другая ветка\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
Ожидаемый результат
Нет документов

Пустая выборка обработана отдельно от false. Пустой ZIP после close() не остаётся.

Эти вызовы не являются рекурсивным архиватором дерева. Каталог проекта и путь архивного имени разделяйте; не начинайте add_path с /. При одинаковых basename можно перезаписать запись: определите политику дубликатов. Для отдельного выбранного файла проще addFile().

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::addFile, ZipArchive::unchangeAll.

ZipArchive::countПроверить, что поставка содержит ожидаемое число записей.

PHP 7.2 / PECL 1.15+ · zip

count(): int

Проверить, что поставка содержит ожидаемое число записей.

Синтаксис: count(): int. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
Нет аргументовСкобки () обязательны.

Результат: Число записей в открытом архиве, включая записи каталогов; метод также вызывается count($zip), поскольку класс реализует Countable.

Отказ: Неоткрытый объект — ValueError. Это не число обычных файлов и не объём распакованных байтов.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt — единственная запись
  2. count() спрашивает число записей
  3. Результат 1 используется для проверки поставки
Проверка количестваСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->count(), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
1

В подготовленном архиве один чек.

Каталоги тоже считаютсяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->addEmptyDir("acts");
echo count($zip), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
2

Countable позволяет общую функцию count(). После удаления индексы могут иметь пропуски: количество не доказывает доступность каждого номера.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::statIndex, ZipArchive::getNameIndex.

ZipArchive::locateNameНайти номер записи по известному имени перед точечным изменением.

PHP 5.2 / PECL 1.5+ · zip

locateName(string $name, int $flags = 0): int|false

Найти номер записи по известному имени перед точечным изменением.

Синтаксис: locateName(string $name, int $flags = 0): int|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИмя записи в ZIP.
flags0 — точное имя; FL_NOCASE — без учёта регистра; FL_NODIR — сравнивать без каталогов. Флаги можно объединить |.

Результат: Индекс найденного элемента, начиная с 0; false — совпадения нет.

Отказ: Пустое name в PHP 8.5.11 даёт false; false также при отказе поиска. Не используйте if ($index): нулевой индекс корректен.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Ищем order.txt
  2. Первый элемент имеет индекс 0
  3. Проверяем !== false
Первый файл не теряетсяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$index = $zip->locateName("order.txt");
echo $index !== false ? "Индекс: $index\n" : "Нет\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Индекс: 0

Строгое сравнение различает false и 0.

Получатель прислал имя другим регистромСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->locateName("ORDER.TXT", ZipArchive::FL_NOCASE), "\n";
var_export($zip->locateName("missing.txt")); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
0
false

FL_NOCASE удобен для поиска, но не решает конфликты нескольких подходящих имён. В импорте лучше требовать однозначное точное имя.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getNameIndex, ZipArchive::renameIndex.

ZipArchive::getNameIndexПоказать содержимое поставки без распаковки.

PHP 5.2 / PECL 1.5+ · zip

getNameIndex(int $index, int $flags = 0): string|false

Показать содержимое поставки без распаковки.

Синтаксис: getNameIndex(int $index, int $flags = 0): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexНомер записи, начиная с 0.
flagsFL_UNCHANGED возвращает исходное имя до подготовленных изменений.

Результат: Строка имени записи или false для отсутствующего/удалённого индекса.

Отказ: Неверный индекс возвращает false; прочитайте статус при диагностике.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Архив содержит order.txt
  2. Номер 0 → getNameIndex(0)
  3. Получаем имя без чтения содержимого
Одно имя в манифестеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt

Имя записи не является безопасным HTML: при выводе в веб-страницу нужно htmlspecialchars().

Сравнить имя до и после переименованияСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->renameIndex(0, "receipt.txt");
echo $zip->getNameIndex(0), "\n", $zip->getNameIndex(0, ZipArchive::FL_UNCHANGED), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
receipt.txt
order.txt

UNCHANGED обращается к состоянию, открытому с диска, а не отменяет изменение.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::locateName, ZipArchive::statIndex.

ZipArchive::getFromNameПрочитать небольшой чек из ZIP без записи на диск.

PHP 5.2 / PECL 1.1+ · zip

getFromName(string $name, int $len = 0, int $flags = 0): string|false

Прочитать небольшой чек из ZIP без записи на диск.

Синтаксис: getFromName(string $name, int $len = 0, int $flags = 0): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТочное имя записи.
lenМаксимальное число байтов результата; 0 читает запись целиком. Для предсказуемого договора используйте 0 или положительное значение.
flagsFL_UNCHANGED читает прежнюю версию; FL_COMPRESSED выдаёт сжатые байты. FL_NOCASE позволяет поиск без учёта регистра.

Результат: Байтовая строка; пустая запись даёт "", отсутствие/отказ — false.

Отказ: false при отсутствии записи, недоступных новых данных, пароле/декомпрессии; повреждённые данные могут дать warning. name="" — ValueError. Полное чтение выделяет память под данные.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt хранит paid:42
  2. Вызов выбирает запись
  3. В строке PHP оказываются 7 байтов
Прочитать статус заказаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Это байты содержимого, а не имя и не распакованный файл на диске.

Считать только маркер форматаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt", 4), "\n";
var_export($zip->getFromName("missing.txt")); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid
false

Первые 4 байта подходят для ASCII-маркера. Ограниченный префикс не доказывает целостность всего содержимого; UTF-8 можно разрезать посередине символа.

Большой документ читайте потоком; ни расширение имени, ни успешная декомпрессия не делают содержимое доверенным. FL_COMPRESSED не выдаёт готовый самостоятельный ZIP-файл. Новые/заменённые данные сначала сохраните и откройте повторно.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getStream, ZipArchive::statName.

ZipArchive::getFromIndexПрочитать небольшой чек из ZIP без записи на диск.

PHP 5.2 / PECL 1.1+ · zip

getFromIndex(int $index, int $len = 0, int $flags = 0): string|false

Прочитать небольшой чек из ZIP без записи на диск.

Синтаксис: getFromIndex(int $index, int $len = 0, int $flags = 0): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс записи, начиная с 0.
lenМаксимальное число байтов результата; 0 читает запись целиком. Для предсказуемого договора используйте 0 или положительное значение.
flagsFL_UNCHANGED читает прежнюю версию; FL_COMPRESSED выдаёт сжатые байты.

Результат: Байтовая строка; пустая запись даёт "", отсутствие/отказ — false.

Отказ: false при отсутствии записи, недоступных новых данных, пароле/декомпрессии; повреждённые данные могут дать warning. name="" — ValueError. Полное чтение выделяет память под данные.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt хранит paid:42
  2. Вызов выбирает запись
  3. В строке PHP оказываются 7 байтов
Прочитать статус заказаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Это байты содержимого, а не имя и не распакованный файл на диске.

Считать только маркер форматаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromIndex(0, 4), "\n";
var_export($zip->getFromIndex(9)); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid
false

Первые 4 байта подходят для ASCII-маркера. Ограниченный префикс не доказывает целостность всего содержимого; UTF-8 можно разрезать посередине символа.

Большой документ читайте потоком; ни расширение имени, ни успешная декомпрессия не делают содержимое доверенным. FL_COMPRESSED не выдаёт готовый самостоятельный ZIP-файл. Новые/заменённые данные сначала сохраните и откройте повторно.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getStream, ZipArchive::statName.

ZipArchive::statNameУзнать размер и способ сжатия документа до выделения памяти.

PHP 5.2 / PECL 1.5+ · zip

statName(string $name, int $flags = 0): array|false

Узнать размер и способ сжатия документа до выделения памяти.

Синтаксис: statName(string $name, int $flags = 0): array|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИмя записи.
flagsFL_UNCHANGED — исходные сведения. FL_NOCASE и FL_NODIR меняют поиск.

Результат: Ассоциативный массив: name, index, crc, size (байты после распаковки), mtime (Unix timestamp), comp_size (сжатые байты), comp_method (CM_*), encryption_method (EM_*). PHP добавляет доступные поля libzip; не полагайтесь на порядок ключей.

Отказ: false при неизвестной записи/отказе; пустое name — ValueError. Для ещё не записанного источника некоторые сведения могут быть недоступны.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt найден
  2. size=7 описывает распакованные байты
  3. Приложение сравнивает размер со своим лимитом
Размер чекаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$info = $zip->statName("order.txt");
if ($info === false) throw new RuntimeException("Нет записи");
echo $info["name"], ":", $info["size"], "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt:7

size и comp_size различаются по смыслу; коэффициент сжатия не является гарантией безопасности.

Лимит импорта перед чтениемСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$info = $zip->statName("order.txt");
if ($info === false) throw new RuntimeException("Нет записи");
echo $info["size"] <= 1024 ? "В пределах 1 KiB\n" : "Слишком большой\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
В пределах 1 KiB

Это только предварительная проверка метаданных; при потоковом чтении дополнительно считайте реально полученные байты и ограничивайте время/число записей.

CRC — контроль случайной порчи, не криптографическое доказательство автора. mtime зависит от точности ZIP/временной зоны; не используйте его как уникальную версию. После извлечения права автоматически не восстанавливаются.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getFromName, ZipArchive::getExternalAttributesName.

ZipArchive::statIndexУзнать размер и способ сжатия документа до выделения памяти.

PHP 5.2 / PECL 1.1+ · zip

statIndex(int $index, int $flags = 0): array|false

Узнать размер и способ сжатия документа до выделения памяти.

Синтаксис: statIndex(int $index, int $flags = 0): array|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс записи.
flagsFL_UNCHANGED — исходные сведения.

Результат: Ассоциативный массив: name, index, crc, size (байты после распаковки), mtime (Unix timestamp), comp_size (сжатые байты), comp_method (CM_*), encryption_method (EM_*). PHP добавляет доступные поля libzip; не полагайтесь на порядок ключей.

Отказ: false при неизвестной записи/отказе; пустое name — ValueError. Для ещё не записанного источника некоторые сведения могут быть недоступны.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt найден
  2. size=7 описывает распакованные байты
  3. Приложение сравнивает размер со своим лимитом
Размер чекаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$info = $zip->statIndex(0);
if ($info === false) throw new RuntimeException("Нет записи");
echo $info["name"], ":", $info["size"], "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt:7

size и comp_size различаются по смыслу; коэффициент сжатия не является гарантией безопасности.

Лимит импорта перед чтениемСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$info = $zip->statIndex(0);
if ($info === false) throw new RuntimeException("Нет записи");
echo $info["size"] <= 1024 ? "В пределах 1 KiB\n" : "Слишком большой\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
В пределах 1 KiB

Это только предварительная проверка метаданных; при потоковом чтении дополнительно считайте реально полученные байты и ограничивайте время/число записей.

CRC — контроль случайной порчи, не криптографическое доказательство автора. mtime зависит от точности ZIP/временной зоны; не используйте его как уникальную версию. После извлечения права автоматически не восстанавливаются.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getFromName, ZipArchive::getExternalAttributesName.

ZipArchive::getStreamЧитать документ небольшими порциями, не помещая его целиком в память.

PHP 5.2 / PECL 1.1+ · zip

getStream(string $name): resource|false

Читать документ небольшими порциями, не помещая его целиком в память.

Синтаксис: getStream(string $name): resource|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИмя записи.

Результат: Открытый поток только для чтения или false. Для потока работают fread(), stream_get_contents(), fclose(); это не объект ZipArchive.

Отказ: false при недоступной записи/данных/пароле; имя не может быть пустым. Обрабатывайте false и от операций чтения. Открытый архив и поток имеют отдельное время жизни.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt → поток
  2. fread(...,4) → paid
  3. Закрываем поток
  4. Затем закрываем архив
Прочитать заголовокСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$stream = $zip->getStream("order.txt");
if ($stream === false) throw new RuntimeException("Нет потока");
echo fread($stream, 4), "\n";
fclose($stream);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid

resource — дескриптор открытого потока; fclose закрывает именно его. Поток создан для чтения, fwrite здесь не подходит.

Ограничить тело 8 байтамиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$stream = $zip->getStream("order.txt");
if ($stream === false) throw new RuntimeException("Нет потока");
try {
    $body = stream_get_contents($stream, 9);
    if ($body === false || strlen($body) > 8) throw new RuntimeException("Лимит");
    echo $body, "\n";
} finally {
    fclose($stream);
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Читаем лимит+1, чтобы обнаружить превышение; finally освобождает поток и при исключении. В большом импорте обрабатывайте порции и суммарный счётчик.

Не обещайте произвольный seek или запись. Закрывайте потоки до архива, не меняйте читаемую запись одновременно. Содержимое новых записей может быть недоступно до close()/open().

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getFromName, ZipArchive::close.

ZipArchive::getStreamNameЧитать документ небольшими порциями, не помещая его целиком в память.

PHP 8.2 / PECL 1.20+ · zip

getStreamName(string $name, int $flags = 0): resource|false

Читать документ небольшими порциями, не помещая его целиком в память.

Синтаксис: getStreamName(string $name, int $flags = 0): resource|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИмя записи.
flagsFL_UNCHANGED — исходная версия записи.

Результат: Открытый поток только для чтения или false. Для потока работают fread(), stream_get_contents(), fclose(); это не объект ZipArchive.

Отказ: false при недоступной записи/данных/пароле; имя не может быть пустым. Обрабатывайте false и от операций чтения. Открытый архив и поток имеют отдельное время жизни.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt → поток
  2. fread(...,4) → paid
  3. Закрываем поток
  4. Затем закрываем архив
Прочитать заголовокСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$stream = $zip->getStreamName("order.txt");
if ($stream === false) throw new RuntimeException("Нет потока");
echo fread($stream, 4), "\n";
fclose($stream);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid

resource — дескриптор открытого потока; fclose закрывает именно его. Поток создан для чтения, fwrite здесь не подходит.

Ограничить тело 8 байтамиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$stream = $zip->getStreamName("order.txt");
if ($stream === false) throw new RuntimeException("Нет потока");
try {
    $body = stream_get_contents($stream, 9);
    if ($body === false || strlen($body) > 8) throw new RuntimeException("Лимит");
    echo $body, "\n";
} finally {
    fclose($stream);
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Читаем лимит+1, чтобы обнаружить превышение; finally освобождает поток и при исключении. В большом импорте обрабатывайте порции и суммарный счётчик.

Не обещайте произвольный seek или запись. Закрывайте потоки до архива, не меняйте читаемую запись одновременно. Содержимое новых записей может быть недоступно до close()/open().

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getFromName, ZipArchive::close.

ZipArchive::getStreamIndexЧитать документ небольшими порциями, не помещая его целиком в память.

PHP 8.2 / PECL 1.20+ · zip

getStreamIndex(int $index, int $flags = 0): resource|false

Читать документ небольшими порциями, не помещая его целиком в память.

Синтаксис: getStreamIndex(int $index, int $flags = 0): resource|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexНомер записи от 0.
flagsFL_UNCHANGED — исходная версия записи.

Результат: Открытый поток только для чтения или false. Для потока работают fread(), stream_get_contents(), fclose(); это не объект ZipArchive.

Отказ: false при недоступной записи/данных/пароле; имя не может быть пустым. Обрабатывайте false и от операций чтения. Открытый архив и поток имеют отдельное время жизни.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt → поток
  2. fread(...,4) → paid
  3. Закрываем поток
  4. Затем закрываем архив
Прочитать заголовокСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$stream = $zip->getStreamIndex(0);
if ($stream === false) throw new RuntimeException("Нет потока");
echo fread($stream, 4), "\n";
fclose($stream);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid

resource — дескриптор открытого потока; fclose закрывает именно его. Поток создан для чтения, fwrite здесь не подходит.

Ограничить тело 8 байтамиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$stream = $zip->getStreamIndex(0);
if ($stream === false) throw new RuntimeException("Нет потока");
try {
    $body = stream_get_contents($stream, 9);
    if ($body === false || strlen($body) > 8) throw new RuntimeException("Лимит");
    echo $body, "\n";
} finally {
    fclose($stream);
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Читаем лимит+1, чтобы обнаружить превышение; finally освобождает поток и при исключении. В большом импорте обрабатывайте порции и суммарный счётчик.

Не обещайте произвольный seek или запись. Закрывайте потоки до архива, не меняйте читаемую запись одновременно. Содержимое новых записей может быть недоступно до close()/open().

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getFromName, ZipArchive::close.

ZipArchive::getStatusStringПолучить понятную диагностику внутреннего отказа ZIP.

PHP 5.2.7+; после close() PHP 8.0 / PECL 1.18+ · zip

getStatusString(): string

Получить понятную диагностику внутреннего отказа ZIP.

Синтаксис: getStatusString(): string. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
Нет аргументовСкобки () обязательны.

Результат: Текст текущей ошибки ZIP/системы; с PHP 8.0 возвращает только string и доступен после close().

Отказ: Текст зависит от libzip/ОС: не сравнивайте его целиком для бизнес-ветвления; используйте status и ER_*.

Как получить первый результат
  1. Не найден missing.txt
  2. Статус фиксирует отказ
  3. Текст подходит для закрытого технического журнала
Диагностика без привязки к английскому текстуСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->locateName("missing.txt");
echo $zip->status === ZipArchive::ER_NOENT ? "Нет записи\n" : "Иной код\n";
echo $zip->getStatusString() !== "" ? "Есть описание\n" : "Пусто\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Нет записи
Есть описание

Журнал может содержать чувствительные пути: не отдавайте его клиенту целиком.

Сбросить старую диагностикуСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->locateName("missing.txt");
$zip->clearError();
echo $zip->status === ZipArchive::ER_OK ? "OK\n" : "Ошибка\n";
echo $zip->getStatusString() !== "" ? "Есть описание состояния\n" : "Пусто\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
OK
Есть описание состояния

Текст существует и для нормального состояния, поэтому непустая строка сама по себе не признак ошибки.

open() возвращает собственный код отказа: сохраняйте его сразу. Не рассчитывайте, что всякий успешный следующий вызов обязательно очистил прошлую ошибку.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::clearError, ZipArchive::open.

ZipArchive::clearErrorОтделить уже обработанный отказ от новой операции.

PHP 8.2 / PECL 1.20+ · zip

clearError(): void

Отделить уже обработанный отказ от новой операции.

Синтаксис: clearError(): void. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
Нет аргументовСкобки () обязательны.

Результат: Ничего не возвращает; очищает ZIP- и системную часть ошибки. Это не отмена изменений и не исправление файла.

Отказ: Доступен и после close(); сброс не доказывает успешность предыдущей операции.

Как получить первый результат
  1. locateName не нашёл missing.txt
  2. Приложение обработало отсутствие
  3. clearError сбрасывает status в ER_OK
Завершить обработку отсутствующего файлаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->locateName("missing.txt");
$zip->clearError();
echo $zip->status === ZipArchive::ER_OK ? "Сброшено\n" : "Ошибка\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Сброшено

Не скрывайте необработанный отказ простым сбросом.

Новый цикл диагностики после закрытияСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$zip->clearError();
echo $zip->status, ":", $zip->statusSys, "\n";
unlink($path);
Ожидаемый результат
0:0

Метод работает с сохранёнными кодами закрытого объекта; другие операции над записями всё ещё требуют open().

Для отката изменений есть unchangeAll(). Сброс ошибки не аналог отката и не повтор операции.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getStatusString, ZipArchive::unchangeAll.

ZipArchive::extractToВыдать проверенные документы из архива в отдельную рабочую папку.

PHP 5.2+; PECL zip · zip

extractTo(string $pathto, array|string|null $files = null): bool

Выдать проверенные документы из архива в отдельную рабочую папку.

Синтаксис: extractTo(string $pathto, array|string|null $files = null): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
pathtoКаталог назначения; отсутствующий каталог может быть создан рекурсивно.
filesnull — все записи; string — одно имя; список строк с индексами 0..n-1 — выбранные имена. [] в PHP 8.5.11 возвращает false; это не «ничего успешно».

Результат: true при успешной выбранной распаковке; false при отказе. Уже извлечённые до ошибки файлы могут остаться.

Отказ: Права, недопустимые имена, повреждённые данные, пароль и нехватка места могут дать warning/false. Это не транзакция.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. В ZIP один проверенный order.txt
  2. Создаём отдельную пустую папку unpack
  3. Извлекаем только order.txt
  4. Читаем paid:42
Извлечь один известный чекСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$dest = __DIR__ . "/unpack";
mkdir($dest, 0700);
if (!$zip->extractTo($dest, "order.txt")) throw new RuntimeException("Не извлечён");
echo file_get_contents($dest . "/order.txt"), "\n";
unlink($dest . "/order.txt");
rmdir($dest);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Своя новая папка исключает случайную перезапись существующего пользовательского документа. Пример получает собственный доверенный ZIP.

Проверить состав перед выборочной распаковкойСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$allowed = ["order.txt"];
foreach ($allowed as $name) {
    if ($zip->locateName($name) === false) throw new RuntimeException("Неполный пакет");
}
$dest = __DIR__ . "/unpack";
mkdir($dest, 0700);
if (!$zip->extractTo($dest, $allowed)) throw new RuntimeException("Ошибка распаковки");
echo count($allowed), " документ\n";
unlink($dest . "/order.txt");
rmdir($dest);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
1 документ

Список имён — политика импорта; он не разрешает остальные записи архива.

Для недоверенных архивов сначала ограничьте число/суммарный размер/имена, запрещайте абсолютные пути, .., коллизии и нежелательные типы; подробнее в отдельном уроке. PHP 8.5.11 сам нормализует пути при extractTo(), поэтому не утверждаем, что любой ../ обязательно выйдет наружу, но молчаливая нормализация также может создать коллизию. Извлечение может перезаписывать файлы. Исходные UNIX-права не восстанавливаются: используются максимально разрешённые права с umask. В Windows <|>*?": и конечные точки могут вызвать отказ, без автоматического безопасного переименования.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::statIndex, ZipArchive::getStream, ZipArchive::getExternalAttributesIndex.

ZipArchive::setArchiveCommentСнабдить всю поставку пояснением.

PHP 5.2 / PECL 1.4+ · zip

setArchiveComment(string $comment): bool

Снабдить всю поставку пояснением.

Синтаксис: setArchiveComment(string $comment): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
commentСтрока комментария; "" удаляет комментарий. Максимум 65535 байтов, не Unicode-символов.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: Комментарий длиннее 65535 байтов — ValueError; отсутствующая запись/отказ libzip — false.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt уже сохранён
  2. Комментарий «Поставка 42» записан архиву
  3. Получатель читает пояснение
Пояснение для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setArchiveComment("Поставка 42");
echo $zip->getArchiveComment(), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Поставка 42

Комментарий — метаданные ZIP, не дополнительный текстовый файл; он не шифруется вместе с содержимым записи.

Удалить устаревшее пояснениеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setArchiveComment("Поставка 42");
$zip->setArchiveComment("");
var_export($zip->getArchiveComment() === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
true

Пустой комментарий — успешный результат; false имеет другой смысл. FL_UNCHANGED не делает откат.

Не храните секреты в комментариях и не выводите их в HTML без экранирования. Сохранение требует close(). Общий комментарий виден также в readonly-свойстве comment; комментарии отдельных записей читаются отдельными методами.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getArchiveComment, ZipArchive::close.

ZipArchive::getArchiveCommentПрочитать пояснение ко всей поставке.

PHP 5.2 / PECL 1.1+ · zip

getArchiveComment(int $flags = 0): string|false

Прочитать пояснение ко всей поставке.

Синтаксис: getArchiveComment(int $flags = 0): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
flagsFL_UNCHANGED читает первоначальный комментарий до текущих изменений.

Результат: Строка, включая "" при отсутствии комментария, либо false при отказе.

Отказ: false при недоступной записи/ошибке; пустое имя записи — ValueError.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt уже сохранён
  2. Комментарий «Поставка 42» записан архиву
  3. Получатель читает пояснение
Пояснение для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setArchiveComment("Поставка 42");
echo $zip->getArchiveComment(), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Поставка 42

Комментарий — метаданные ZIP, не дополнительный текстовый файл; он не шифруется вместе с содержимым записи.

Сравнить черновик с исходной версиейСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setArchiveComment("Черновик");
echo $zip->getArchiveComment(), "\n";
var_export($zip->getArchiveComment(ZipArchive::FL_UNCHANGED) === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Черновик
true

Пустой комментарий — успешный результат; false имеет другой смысл. FL_UNCHANGED не делает откат.

Не храните секреты в комментариях и не выводите их в HTML без экранирования. Сохранение требует close(). Общий комментарий виден также в readonly-свойстве comment; комментарии отдельных записей читаются отдельными методами.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setArchiveComment, ZipArchive::close.

ZipArchive::setCommentIndexСнабдить отдельный документ пояснением.

PHP 5.2 / PECL 1.4+ · zip

setCommentIndex(int $index, string $comment): bool

Снабдить отдельный документ пояснением.

Синтаксис: setCommentIndex(int $index, string $comment): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс записи от 0.
commentСтрока комментария; "" удаляет комментарий. Максимум 65535 байтов, не Unicode-символов.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: Комментарий длиннее 65535 байтов — ValueError; отсутствующая запись/отказ libzip — false.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt уже сохранён
  2. Комментарий «Поставка 42» записан записи
  3. Получатель читает пояснение
Пояснение для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentIndex(0, "Поставка 42");
echo $zip->getCommentIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Поставка 42

Комментарий — метаданные ZIP, не дополнительный текстовый файл; он не шифруется вместе с содержимым записи.

Удалить устаревшее пояснениеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentIndex(0, "Поставка 42");
$zip->setCommentIndex(0, "");
var_export($zip->getCommentIndex(0) === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
true

Пустой комментарий — успешный результат; false имеет другой смысл. FL_UNCHANGED не делает откат.

Не храните секреты в комментариях и не выводите их в HTML без экранирования. Сохранение требует close(). Общий комментарий виден также в readonly-свойстве comment; комментарии отдельных записей читаются отдельными методами.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getCommentIndex, ZipArchive::close.

ZipArchive::getCommentIndexПрочитать пояснение к конкретному документу.

PHP 5.2 / PECL 1.4+ · zip

getCommentIndex(int $index, int $flags = 0): string|false

Прочитать пояснение к конкретному документу.

Синтаксис: getCommentIndex(int $index, int $flags = 0): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс записи от 0.
flagsFL_UNCHANGED читает первоначальный комментарий до текущих изменений.

Результат: Строка, включая "" при отсутствии комментария, либо false при отказе.

Отказ: false при недоступной записи/ошибке; пустое имя записи — ValueError.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt уже сохранён
  2. Комментарий «Поставка 42» записан записи
  3. Получатель читает пояснение
Пояснение для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentIndex(0, "Поставка 42");
echo $zip->getCommentIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Поставка 42

Комментарий — метаданные ZIP, не дополнительный текстовый файл; он не шифруется вместе с содержимым записи.

Сравнить черновик с исходной версиейСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentIndex(0, "Черновик");
echo $zip->getCommentIndex(0), "\n";
var_export($zip->getCommentIndex(0, ZipArchive::FL_UNCHANGED) === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Черновик
true

Пустой комментарий — успешный результат; false имеет другой смысл. FL_UNCHANGED не делает откат.

Не храните секреты в комментариях и не выводите их в HTML без экранирования. Сохранение требует close(). Общий комментарий виден также в readonly-свойстве comment; комментарии отдельных записей читаются отдельными методами.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setCommentIndex, ZipArchive::close.

ZipArchive::setCommentNameСнабдить отдельный документ пояснением.

PHP 5.2 / PECL 1.4+ · zip

setCommentName(string $name, string $comment): bool

Снабдить отдельный документ пояснением.

Синтаксис: setCommentName(string $name, string $comment): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТочное имя записи.
commentСтрока комментария; "" удаляет комментарий. Максимум 65535 байтов, не Unicode-символов.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: Комментарий длиннее 65535 байтов — ValueError; отсутствующая запись/отказ libzip — false.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt уже сохранён
  2. Комментарий «Поставка 42» записан записи
  3. Получатель читает пояснение
Пояснение для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentName("order.txt", "Поставка 42");
echo $zip->getCommentName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Поставка 42

Комментарий — метаданные ZIP, не дополнительный текстовый файл; он не шифруется вместе с содержимым записи.

Удалить устаревшее пояснениеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentName("order.txt", "Поставка 42");
$zip->setCommentName("order.txt", "");
var_export($zip->getCommentName("order.txt") === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
true

Пустой комментарий — успешный результат; false имеет другой смысл. FL_UNCHANGED не делает откат.

Не храните секреты в комментариях и не выводите их в HTML без экранирования. Сохранение требует close(). Общий комментарий виден также в readonly-свойстве comment; комментарии отдельных записей читаются отдельными методами.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getCommentName, ZipArchive::close.

ZipArchive::getCommentNameПрочитать пояснение к конкретному документу.

PHP 5.2 / PECL 1.4+ · zip

getCommentName(string $name, int $flags = 0): string|false

Прочитать пояснение к конкретному документу.

Синтаксис: getCommentName(string $name, int $flags = 0): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТочное имя записи.
flagsFL_UNCHANGED читает первоначальный комментарий до текущих изменений.

Результат: Строка, включая "" при отсутствии комментария, либо false при отказе.

Отказ: false при недоступной записи/ошибке; пустое имя записи — ValueError.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt уже сохранён
  2. Комментарий «Поставка 42» записан записи
  3. Получатель читает пояснение
Пояснение для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentName("order.txt", "Поставка 42");
echo $zip->getCommentName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Поставка 42

Комментарий — метаданные ZIP, не дополнительный текстовый файл; он не шифруется вместе с содержимым записи.

Сравнить черновик с исходной версиейСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setCommentName("order.txt", "Черновик");
echo $zip->getCommentName("order.txt"), "\n";
var_export($zip->getCommentName("order.txt", ZipArchive::FL_UNCHANGED) === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Черновик
true

Пустой комментарий — успешный результат; false имеет другой смысл. FL_UNCHANGED не делает откат.

Не храните секреты в комментариях и не выводите их в HTML без экранирования. Сохранение требует close(). Общий комментарий виден также в readonly-свойстве comment; комментарии отдельных записей читаются отдельными методами.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setCommentName, ZipArchive::close.

ZipArchive::setExternalAttributesIndexПометить документ переносимыми UNIX-атрибутами для архиватора получателя.

PHP 5.6 / PECL 1.12.4+ · zip

setExternalAttributesIndex(int $index, int $opsys, int $attr, int $flags = 0): bool

Пометить документ переносимыми UNIX-атрибутами для архиватора получателя.

Синтаксис: setExternalAttributesIndex(int $index, int $opsys, int $attr, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс записи от 0.
opsysOPSYS_* обозначает систему, чьим правилам соответствуют атрибуты.
attrБитовое поле внешних атрибутов; смысл зависит от opsys. Для UNIX верхние 16 бит содержат тип и права.
flagsВ setter не используется; передавайте 0.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неизвестной записи/ошибке libzip; пустое имя — ValueError. PHP-тип/ссылочность аргументов также проверяются.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt — обычный файл
  2. 0100644: тип regular + права rw-r--r--
  3. Сдвиг <<16 помещает поле в ZIP
  4. Чтение возвращает 100644 в восьмеричном виде
Описать обычный файлСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesIndex(0, ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesIndex(0, $system, $attributes)) throw new RuntimeException("Нет атрибутов");
echo $system === ZipArchive::OPSYS_UNIX ? "UNIX\n" : "Другое\n";
echo decoct(($attributes >> 16) & 0xffff), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
UNIX
100644

Начальный 0 в 0100644 — восьмеричная запись; << и >> сдвигают биты, & оставляет нужное поле. Это меняет метаданные ZIP, а не chmod локального файла.

Отделить обычный документ от иных типовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesIndex(0, ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesIndex(0, $system, $attributes)) throw new RuntimeException("Нет атрибутов");
$isRegular = $system === ZipArchive::OPSYS_UNIX && (($attributes >> 16) & 0170000) === 0100000;
echo $isRegular ? "Обычный файл\n" : "Нужна другая политика\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Обычный файл

Маска 0170000 выделяет тип. Если система не UNIX, эти биты нельзя трактовать по UNIX-правилам; консервативный импорт должен выбрать явную политику.

Атрибуты прислал автор архива; они не делают содержимое безопасным. extractTo() не восстанавливает исходные UNIX-права автоматически. Не применяйте сохранённые права вслепую, особенно исполняемые/setuid-биты.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::extractTo, ZipArchive::statIndex.

ZipArchive::getExternalAttributesIndexПроверить тип/права записи по метаданным создавшей её системы.

PHP 5.6 / PECL 1.12.4+ · zip

getExternalAttributesIndex(int $index, int &$opsys, int &$attr, int $flags = 0): bool

Проверить тип/права записи по метаданным создавшей её системы.

Синтаксис: getExternalAttributesIndex(int $index, int &$opsys, int &$attr, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс записи от 0.
opsysOPSYS_* обозначает систему, чьим правилам соответствуют атрибуты. Выходной параметр по ссылке &, записывается при успехе.
attrБитовое поле внешних атрибутов; смысл зависит от opsys. Для UNIX верхние 16 бит содержат тип и права. Выходной параметр по ссылке &.
flagsFL_UNCHANGED — исходные атрибуты.

Результат: true и два выходных значения при успехе; false при отказе. Не используйте выходные переменные после false.

Отказ: false при неизвестной записи/ошибке libzip; пустое имя — ValueError. PHP-тип/ссылочность аргументов также проверяются.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt — обычный файл
  2. 0100644: тип regular + права rw-r--r--
  3. Сдвиг <<16 помещает поле в ZIP
  4. Чтение возвращает 100644 в восьмеричном виде
Описать обычный файлСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesIndex(0, ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesIndex(0, $system, $attributes)) throw new RuntimeException("Нет атрибутов");
echo $system === ZipArchive::OPSYS_UNIX ? "UNIX\n" : "Другое\n";
echo decoct(($attributes >> 16) & 0xffff), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
UNIX
100644

Начальный 0 в 0100644 — восьмеричная запись; << и >> сдвигают биты, & оставляет нужное поле. Это меняет метаданные ZIP, а не chmod локального файла.

Отделить обычный документ от иных типовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesIndex(0, ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesIndex(0, $system, $attributes)) throw new RuntimeException("Нет атрибутов");
$isRegular = $system === ZipArchive::OPSYS_UNIX && (($attributes >> 16) & 0170000) === 0100000;
echo $isRegular ? "Обычный файл\n" : "Нужна другая политика\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Обычный файл

Маска 0170000 выделяет тип. Если система не UNIX, эти биты нельзя трактовать по UNIX-правилам; консервативный импорт должен выбрать явную политику.

Атрибуты прислал автор архива; они не делают содержимое безопасным. extractTo() не восстанавливает исходные UNIX-права автоматически. Не применяйте сохранённые права вслепую, особенно исполняемые/setuid-биты.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::extractTo, ZipArchive::statIndex.

ZipArchive::setMtimeIndexСохранить время изменения документа при формировании архивной поставки.

PHP 8.0 / PECL 1.16+, libzip 1.0+ · zip

setMtimeIndex(int $index, int $timestamp, int $flags = 0): bool

Сохранить время изменения документа при формировании архивной поставки.

Синтаксис: setMtimeIndex(int $index, int $timestamp, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс либо точное имя существующей записи.
timestampUnix timestamp в секундах; это не миллисекунды JavaScript.
flagsПока не используется, передавайте 0.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неизвестной записи/ошибке libzip; пустое имя — ValueError. Доступность метода требует подходящей libzip.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. 2024-01-01 00:00:00 UTC → 1704067200
  2. Устанавливаем секунды для order.txt
  3. Сохраняем и открываем ZIP
  4. stat показывает выбранное время
Время исходной выгрузкиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
date_default_timezone_set("UTC");
$zip->setMtimeIndex(0, 1704067200);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo gmdate("Y-m-d H:i:s", $zip->statIndex(0)["mtime"]), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
2024-01-01 00:00:00

Время фиксировано и зона UTC задана явно; архивный формат может округлять точность, поэтому здесь выбрана чётная секунда.

Одинаковая дата двух документовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
date_default_timezone_set("UTC");
$zip->addFromString("note.txt", "ok");
$zip->setMtimeIndex(0, 1704067200);
$zip->setMtimeIndex(1, 1704067200);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->statIndex(0)["mtime"] === $zip->statIndex(1)["mtime"] ? "Один выпуск\n" : "Разные даты\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Один выпуск

Единая метка полезна для выпуска, но сама по себе не делает ZIP побайтово воспроизводимым: есть порядок, кодеки, прочие метаданные.

Базовые DOS-поля ZIP имеют ограниченный диапазон и двухсекундную точность; дополнительные поля и поведение libzip могут отличаться. Не используйте mtime для доказательства авторства или идентичности файла.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::statIndex, ZipArchive::addFile.

ZipArchive::setExternalAttributesNameПометить документ переносимыми UNIX-атрибутами для архиватора получателя.

PHP 5.6 / PECL 1.12.4+ · zip

setExternalAttributesName(string $name, int $opsys, int $attr, int $flags = 0): bool

Пометить документ переносимыми UNIX-атрибутами для архиватора получателя.

Синтаксис: setExternalAttributesName(string $name, int $opsys, int $attr, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТочное имя записи.
opsysOPSYS_* обозначает систему, чьим правилам соответствуют атрибуты.
attrБитовое поле внешних атрибутов; смысл зависит от opsys. Для UNIX верхние 16 бит содержат тип и права.
flagsВ setter не используется; передавайте 0.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неизвестной записи/ошибке libzip; пустое имя — ValueError. PHP-тип/ссылочность аргументов также проверяются.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt — обычный файл
  2. 0100644: тип regular + права rw-r--r--
  3. Сдвиг <<16 помещает поле в ZIP
  4. Чтение возвращает 100644 в восьмеричном виде
Описать обычный файлСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesName("order.txt", ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesName("order.txt", $system, $attributes)) throw new RuntimeException("Нет атрибутов");
echo $system === ZipArchive::OPSYS_UNIX ? "UNIX\n" : "Другое\n";
echo decoct(($attributes >> 16) & 0xffff), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
UNIX
100644

Начальный 0 в 0100644 — восьмеричная запись; << и >> сдвигают биты, & оставляет нужное поле. Это меняет метаданные ZIP, а не chmod локального файла.

Отделить обычный документ от иных типовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesName("order.txt", ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesName("order.txt", $system, $attributes)) throw new RuntimeException("Нет атрибутов");
$isRegular = $system === ZipArchive::OPSYS_UNIX && (($attributes >> 16) & 0170000) === 0100000;
echo $isRegular ? "Обычный файл\n" : "Нужна другая политика\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Обычный файл

Маска 0170000 выделяет тип. Если система не UNIX, эти биты нельзя трактовать по UNIX-правилам; консервативный импорт должен выбрать явную политику.

Атрибуты прислал автор архива; они не делают содержимое безопасным. extractTo() не восстанавливает исходные UNIX-права автоматически. Не применяйте сохранённые права вслепую, особенно исполняемые/setuid-биты.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::extractTo, ZipArchive::statIndex.

ZipArchive::getExternalAttributesNameПроверить тип/права записи по метаданным создавшей её системы.

PHP 5.6 / PECL 1.12.4+ · zip

getExternalAttributesName(string $name, int &$opsys, int &$attr, int $flags = 0): bool

Проверить тип/права записи по метаданным создавшей её системы.

Синтаксис: getExternalAttributesName(string $name, int &$opsys, int &$attr, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТочное имя записи.
opsysOPSYS_* обозначает систему, чьим правилам соответствуют атрибуты. Выходной параметр по ссылке &, записывается при успехе.
attrБитовое поле внешних атрибутов; смысл зависит от opsys. Для UNIX верхние 16 бит содержат тип и права. Выходной параметр по ссылке &.
flagsFL_UNCHANGED — исходные атрибуты.

Результат: true и два выходных значения при успехе; false при отказе. Не используйте выходные переменные после false.

Отказ: false при неизвестной записи/ошибке libzip; пустое имя — ValueError. PHP-тип/ссылочность аргументов также проверяются.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt — обычный файл
  2. 0100644: тип regular + права rw-r--r--
  3. Сдвиг <<16 помещает поле в ZIP
  4. Чтение возвращает 100644 в восьмеричном виде
Описать обычный файлСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesName("order.txt", ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesName("order.txt", $system, $attributes)) throw new RuntimeException("Нет атрибутов");
echo $system === ZipArchive::OPSYS_UNIX ? "UNIX\n" : "Другое\n";
echo decoct(($attributes >> 16) & 0xffff), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
UNIX
100644

Начальный 0 в 0100644 — восьмеричная запись; << и >> сдвигают биты, & оставляет нужное поле. Это меняет метаданные ZIP, а не chmod локального файла.

Отделить обычный документ от иных типовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setExternalAttributesName("order.txt", ZipArchive::OPSYS_UNIX, 0100644 << 16);
$system = 0; $attributes = 0;
if (!$zip->getExternalAttributesName("order.txt", $system, $attributes)) throw new RuntimeException("Нет атрибутов");
$isRegular = $system === ZipArchive::OPSYS_UNIX && (($attributes >> 16) & 0170000) === 0100000;
echo $isRegular ? "Обычный файл\n" : "Нужна другая политика\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Обычный файл

Маска 0170000 выделяет тип. Если система не UNIX, эти биты нельзя трактовать по UNIX-правилам; консервативный импорт должен выбрать явную политику.

Атрибуты прислал автор архива; они не делают содержимое безопасным. extractTo() не восстанавливает исходные UNIX-права автоматически. Не применяйте сохранённые права вслепую, особенно исполняемые/setuid-биты.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::extractTo, ZipArchive::statIndex.

ZipArchive::setMtimeNameСохранить время изменения документа при формировании архивной поставки.

PHP 8.0 / PECL 1.16+, libzip 1.0+ · zip

setMtimeName(string $name, int $timestamp, int $flags = 0): bool

Сохранить время изменения документа при формировании архивной поставки.

Синтаксис: setMtimeName(string $name, int $timestamp, int $flags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИндекс либо точное имя существующей записи.
timestampUnix timestamp в секундах; это не миллисекунды JavaScript.
flagsПока не используется, передавайте 0.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неизвестной записи/ошибке libzip; пустое имя — ValueError. Доступность метода требует подходящей libzip.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. 2024-01-01 00:00:00 UTC → 1704067200
  2. Устанавливаем секунды для order.txt
  3. Сохраняем и открываем ZIP
  4. stat показывает выбранное время
Время исходной выгрузкиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
date_default_timezone_set("UTC");
$zip->setMtimeName("order.txt", 1704067200);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo gmdate("Y-m-d H:i:s", $zip->statIndex(0)["mtime"]), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
2024-01-01 00:00:00

Время фиксировано и зона UTC задана явно; архивный формат может округлять точность, поэтому здесь выбрана чётная секунда.

Одинаковая дата двух документовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
date_default_timezone_set("UTC");
$zip->addFromString("note.txt", "ok");
$zip->setMtimeName("order.txt", 1704067200);
$zip->setMtimeName("note.txt", 1704067200);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->statIndex(0)["mtime"] === $zip->statIndex(1)["mtime"] ? "Один выпуск\n" : "Разные даты\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Один выпуск

Единая метка полезна для выпуска, но сама по себе не делает ZIP побайтово воспроизводимым: есть порядок, кодеки, прочие метаданные.

Базовые DOS-поля ZIP имеют ограниченный диапазон и двухсекундную точность; дополнительные поля и поведение libzip могут отличаться. Не используйте mtime для доказательства авторства или идентичности файла.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::statIndex, ZipArchive::addFile.

ZipArchive::setArchiveFlagСохранить намеренно пустой архив и управлять общими признаками ZIP.

PHP 8.3 / PECL 1.22+, libzip 1.10+ · zip

setArchiveFlag(int $flag, int $value): bool

Сохранить намеренно пустой архив и управлять общими признаками ZIP.

Синтаксис: setArchiveFlag(int $flag, int $value): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
flagДля изменения: AFL_WANT_TORRENTZIP, AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE, вручную устанавливаемый AFL_RDONLY; AFL_IS_TORRENTZIP только читается.
valueНоль выключает, ненулевое значение включает.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при недопустимом/неизменяемом флаге или ошибке.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Новый ZIP без записей
  2. Включаем KEEP_FILE_FOR_EMPTY_ARCHIVE
  3. getArchiveFlag даёт 1
  4. close сохраняет пустой ZIP
Сохранить пустую поставкуСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->setArchiveFlag(ZipArchive::AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE, 1);
echo $zip->getArchiveFlag(ZipArchive::AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
echo is_file($path) ? "ZIP сохранён\n" : "Нет\n";
unlink($path);
Ожидаемый результат
1
ZIP сохранён

Возможность требует PHP 8.3 / PECL 1.22 и libzip 1.10; это не то же самое, что нулевой файл.

Проверить архив только для чтенияСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setArchiveFlag(ZipArchive::AFL_WANT_TORRENTZIP, 1);
echo $zip->getArchiveFlag(ZipArchive::AFL_WANT_TORRENTZIP) === 1 ? "Запрошен TorrentZip\n" : "Нет\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Запрошен TorrentZip

AFL_RDONLY от open(RDONLY) нельзя снять; вручную установленный через setArchiveFlag можно снять обратно (уточнение libzip к краткому тексту PHP.net). AFL_IS_TORRENTZIP — наблюдаемый признак. WANT_TORRENTZIP запрашивает формат для записи и не доказывает, что несохранённые изменения уже применены.

Эти AFL_* отличаются от flags аргумента open() и от FL_* операций над записями. Не передавайте их взаимозаменяемо. TorrentZip задаёт специальное стандартизованное представление архива и может менять метаданные.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::close, ZipArchive::open.

ZipArchive::getArchiveFlagПроверить общий признак архива, не перепутав ошибку -1 с true.

PHP 8.3 / PECL 1.22+, libzip 1.10+ · zip

getArchiveFlag(int $flag, int $flags = 0): int

Проверить общий признак архива, не перепутав ошибку -1 с true.

Синтаксис: getArchiveFlag(int $flag, int $flags = 0): int. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
flagДля изменения: AFL_WANT_TORRENTZIP, AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE, вручную устанавливаемый AFL_RDONLY; AFL_IS_TORRENTZIP только читается.
flagsFL_UNCHANGED читает значение, с которым архив был открыт.

Результат: 1 — установлен, 0 — не установлен, -1 — ошибка. Используйте === 1, а не приведение к bool.

Отказ: -1 при ошибке флага; это целое truthy-значение.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Новый ZIP без записей
  2. Включаем KEEP_FILE_FOR_EMPTY_ARCHIVE
  3. getArchiveFlag даёт 1
  4. close сохраняет пустой ZIP
Сохранить пустую поставкуСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->setArchiveFlag(ZipArchive::AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE, 1);
echo $zip->getArchiveFlag(ZipArchive::AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
echo is_file($path) ? "ZIP сохранён\n" : "Нет\n";
unlink($path);
Ожидаемый результат
1
ZIP сохранён

Возможность требует PHP 8.3 / PECL 1.22 и libzip 1.10; это не то же самое, что нулевой файл.

Проверить архив только для чтенияСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path, ZipArchive::RDONLY) !== true) throw new RuntimeException("Нет ZIP");
echo $zip->getArchiveFlag(ZipArchive::AFL_RDONLY) === 1 ? "Только чтение\n" : "Другое\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Только чтение

AFL_RDONLY от open(RDONLY) нельзя снять; вручную установленный через setArchiveFlag можно снять обратно (уточнение libzip к краткому тексту PHP.net). AFL_IS_TORRENTZIP — наблюдаемый признак. WANT_TORRENTZIP запрашивает формат для записи и не доказывает, что несохранённые изменения уже применены.

Эти AFL_* отличаются от flags аргумента open() и от FL_* операций над записями. Не передавайте их взаимозаменяемо. TorrentZip задаёт специальное стандартизованное представление архива и может менять метаданные.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::close, ZipArchive::open.

ZipArchive::deleteIndexИсключить устаревший документ из поставки.

PHP 5.2 / PECL 1.5+ · zip

deleteIndex(int $index): bool

Исключить устаревший документ из поставки.

Синтаксис: deleteIndex(int $index): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexТекущий индекс записи от 0.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отсутствии записи/конфликте имени/отказе libzip. deleteName/unchangeName с пустым name возвращают false; renameName с пустым name и rename с пустым new_name бросают ValueError. Откат может отказать, если прежнее имя теперь занято другой записью.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt открыт с диска
  2. Запись помечается удалённой
  3. Изменение видно в открытом объекте; close фиксирует оставшееся
Удалить старый чекСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->deleteIndex(0);
var_export($zip->locateName("order.txt")); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
false

Операция меняет запись ZIP, не удаляет/переименовывает исходный файл на компьютере.

Отменить удаление до закрытияСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->deleteIndex(0);
$zip->unchangeIndex(0);
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt

Для отката по имени после rename используйте новое текущее имя. Удалённую запись удобно восстановить по сохранённому индексу.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться. unchange* возвращает состояние на момент последнего open(), а не произвольную историю версий и не уже сохранённую предыдущим close() редакцию. После удаления последней записи close() по умолчанию удаляет пустой ZIP.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeAll, ZipArchive::close.

ZipArchive::deleteNameИсключить устаревший документ из поставки.

PHP 5.2 / PECL 1.5+ · zip

deleteName(string $name): bool

Исключить устаревший документ из поставки.

Синтаксис: deleteName(string $name): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТекущее имя записи, включая путь внутри ZIP.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отсутствии записи/конфликте имени/отказе libzip. deleteName/unchangeName с пустым name возвращают false; renameName с пустым name и rename с пустым new_name бросают ValueError. Откат может отказать, если прежнее имя теперь занято другой записью.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt открыт с диска
  2. Запись помечается удалённой
  3. Изменение видно в открытом объекте; close фиксирует оставшееся
Удалить старый чекСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->deleteName("order.txt");
var_export($zip->locateName("order.txt")); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
false

Операция меняет запись ZIP, не удаляет/переименовывает исходный файл на компьютере.

Отменить удаление до закрытияСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->deleteName("order.txt");
$zip->unchangeIndex(0);
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt

Для отката по имени после rename используйте новое текущее имя. Удалённую запись удобно восстановить по сохранённому индексу.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться. unchange* возвращает состояние на момент последнего open(), а не произвольную историю версий и не уже сохранённую предыдущим close() редакцию. После удаления последней записи close() по умолчанию удаляет пустой ZIP.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeAll, ZipArchive::close.

ZipArchive::renameIndexПереименовать документ внутри ZIP, сохранив его содержимое.

PHP 5.2 / PECL 1.5+ · zip

renameIndex(int $index, string $new_name): bool

Переименовать документ внутри ZIP, сохранив его содержимое.

Синтаксис: renameIndex(int $index, string $new_name): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexТекущий индекс записи от 0.
new_nameНовое непустое имя внутри ZIP.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отсутствии записи/конфликте имени/отказе libzip. deleteName/unchangeName с пустым name возвращают false; renameName с пустым name и rename с пустым new_name бросают ValueError. Откат может отказать, если прежнее имя теперь занято другой записью.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt открыт с диска
  2. Текущее имя становится receipt.txt
  3. Изменение видно в открытом объекте; close фиксирует оставшееся
Читаемое имя для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->renameIndex(0, "receipt.txt");
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
receipt.txt

Операция меняет запись ZIP, не удаляет/переименовывает исходный файл на компьютере.

Показать первоначальное имяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->renameIndex(0, "receipt.txt");
echo $zip->getNameIndex(0, ZipArchive::FL_UNCHANGED), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt

Для отката по имени после rename используйте новое текущее имя. Удалённую запись удобно восстановить по сохранённому индексу.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться. unchange* возвращает состояние на момент последнего open(), а не произвольную историю версий и не уже сохранённую предыдущим close() редакцию. После удаления последней записи close() по умолчанию удаляет пустой ZIP.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeAll, ZipArchive::close.

ZipArchive::renameNameПереименовать документ внутри ZIP, сохранив его содержимое.

PHP 5.2 / PECL 1.5+ · zip

renameName(string $name, string $new_name): bool

Переименовать документ внутри ZIP, сохранив его содержимое.

Синтаксис: renameName(string $name, string $new_name): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТекущее имя записи, включая путь внутри ZIP.
new_nameНовое непустое имя внутри ZIP.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отсутствии записи/конфликте имени/отказе libzip. deleteName/unchangeName с пустым name возвращают false; renameName с пустым name и rename с пустым new_name бросают ValueError. Откат может отказать, если прежнее имя теперь занято другой записью.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt открыт с диска
  2. Текущее имя становится receipt.txt
  3. Изменение видно в открытом объекте; close фиксирует оставшееся
Читаемое имя для получателяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->renameName("order.txt", "receipt.txt");
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
receipt.txt

Операция меняет запись ZIP, не удаляет/переименовывает исходный файл на компьютере.

Показать первоначальное имяСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->renameName("order.txt", "receipt.txt");
echo $zip->getNameIndex(0, ZipArchive::FL_UNCHANGED), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt

Для отката по имени после rename используйте новое текущее имя. Удалённую запись удобно восстановить по сохранённому индексу.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться. unchange* возвращает состояние на момент последнего open(), а не произвольную историю версий и не уже сохранённую предыдущим close() редакцию. После удаления последней записи close() по умолчанию удаляет пустой ZIP.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeAll, ZipArchive::close.

ZipArchive::unchangeIndexОтменить подготовленное изменение одной записи до сохранения.

PHP 5.2 / PECL 1.1+ · zip

unchangeIndex(int $index): bool

Отменить подготовленное изменение одной записи до сохранения.

Синтаксис: unchangeIndex(int $index): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexТекущий индекс записи от 0.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отсутствии записи/конфликте имени/отказе libzip. deleteName/unchangeName с пустым name возвращают false; renameName с пустым name и rename с пустым new_name бросают ValueError. Откат может отказать, если прежнее имя теперь занято другой записью.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt открыт с диска
  2. Готовится замена paid:42 → cancelled:42, затем отменяется
  3. Изменение видно в открытом объекте; close фиксирует оставшееся
Вернуть прежний статусСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->addFromString("order.txt", "cancelled:42");
$zip->unchangeIndex(0);
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Операция меняет запись ZIP, не удаляет/переименовывает исходный файл на компьютере.

Откатить переименованиеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->renameIndex(0, "receipt.txt");
$zip->unchangeIndex(0);
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt

Для отката по имени после rename используйте новое текущее имя. Удалённую запись удобно восстановить по сохранённому индексу.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться. unchange* возвращает состояние на момент последнего open(), а не произвольную историю версий и не уже сохранённую предыдущим close() редакцию. После удаления последней записи close() по умолчанию удаляет пустой ZIP.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeAll, ZipArchive::close.

ZipArchive::unchangeNameОтменить подготовленное изменение одной записи до сохранения.

PHP 5.2 / PECL 1.5+ · zip

unchangeName(string $name): bool

Отменить подготовленное изменение одной записи до сохранения.

Синтаксис: unchangeName(string $name): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameТекущее имя записи, включая путь внутри ZIP.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отсутствии записи/конфликте имени/отказе libzip. deleteName/unchangeName с пустым name возвращают false; renameName с пустым name и rename с пустым new_name бросают ValueError. Откат может отказать, если прежнее имя теперь занято другой записью.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt открыт с диска
  2. Готовится замена paid:42 → cancelled:42, затем отменяется
  3. Изменение видно в открытом объекте; close фиксирует оставшееся
Вернуть прежний статусСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->addFromString("order.txt", "cancelled:42");
$zip->unchangeName("order.txt");
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Операция меняет запись ZIP, не удаляет/переименовывает исходный файл на компьютере.

Откатить переименованиеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->renameIndex(0, "receipt.txt");
$zip->unchangeName("receipt.txt");
echo $zip->getNameIndex(0), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt

Для отката по имени после rename используйте новое текущее имя. Удалённую запись удобно восстановить по сохранённому индексу.

Имена внутри ZIP используют / даже на Windows. Они не должны автоматически становиться путями на сервере; правила извлечения разобраны в разделе безопасности. Индекс начинается с 0; после удаления в открытом архиве могут оставаться пропуски. После закрытия и открытия индексы могут измениться. unchange* возвращает состояние на момент последнего open(), а не произвольную историю версий и не уже сохранённую предыдущим close() редакцию. После удаления последней записи close() по умолчанию удаляет пустой ZIP.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeAll, ZipArchive::close.

ZipArchive::unchangeAllОтменить всю подготовленную партию изменений перед закрытием.

PHP 5.2 / PECL 1.1+ · zip

unchangeAll(): bool

Отменить всю подготовленную партию изменений перед закрытием.

Синтаксис: unchangeAll(): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
Нет аргументовСкобки () обязательны.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отказе libzip; открытый архив обязателен. Это не откат внешних файлов приложения.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Комментарий «Черновик» и новое имя receipt.txt подготовлены
  2. unchangeAll отменяет все изменения
  3. Приложение проверяет имя и комментарий
Граница откатаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setArchiveComment("Черновик");
$zip->renameIndex(0, "receipt.txt");
$zip->unchangeAll();
echo $zip->getNameIndex(0), "\n";
var_export($zip->getArchiveComment() === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
order.txt
true

Комментарий всего архива относится к общим данным; имя записи — к конкретному документу.

Откат и содержимое документаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->addFromString("order.txt", "cancelled:42");
$zip->unchangeAll();
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Сравнение показывает, почему unchangeArchive не заменяет unchangeAll при отказе импорта. Проверяется уже сохранённое содержимое.

Исходная точка — состояние при open(). Для unchangeArchive официальная страница называет общие изменения, прежде всего комментарий; не рассчитывайте этим вызовом отменить изменения отдельных записей. Нельзя вернуть изменения после успешного close() без отдельной резервной версии.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeIndex, ZipArchive::close.

ZipArchive::unchangeArchiveОтменить только общие изменения архива, оставив изменения документов.

PHP 5.2 / PECL 1.1+ · zip

unchangeArchive(): bool

Отменить только общие изменения архива, оставив изменения документов.

Синтаксис: unchangeArchive(): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
Нет аргументовСкобки () обязательны.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при отказе libzip; открытый архив обязателен. Это не откат внешних файлов приложения.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Комментарий «Черновик» и новое имя receipt.txt подготовлены
  2. unchangeArchive отменяет только общие метаданные
  3. Приложение проверяет имя и комментарий
Граница откатаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setArchiveComment("Черновик");
$zip->renameIndex(0, "receipt.txt");
$zip->unchangeArchive();
echo $zip->getNameIndex(0), "\n";
var_export($zip->getArchiveComment() === ""); echo "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
receipt.txt
true

Комментарий всего архива относится к общим данным; имя записи — к конкретному документу.

Откат и содержимое документаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->addFromString("order.txt", "cancelled:42");
$zip->unchangeArchive();
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
cancelled:42

Сравнение показывает, почему unchangeArchive не заменяет unchangeAll при отказе импорта. Проверяется уже сохранённое содержимое.

Исходная точка — состояние при open(). Для unchangeArchive официальная страница называет общие изменения, прежде всего комментарий; не рассчитывайте этим вызовом отменить изменения отдельных записей. Нельзя вернуть изменения после успешного close() без отдельной резервной версии.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::unchangeIndex, ZipArchive::close.

ZipArchive::isCompressionMethodSupportedПроверить отдельно запись и чтение выбранного способа сжатия.

PHP 8.0 / PECL 1.19+, libzip 1.7+ · zip

isCompressionMethodSupported(int $method, bool $enc = true): bool

Проверить отдельно запись и чтение выбранного способа сжатия.

Синтаксис: isCompressionMethodSupported(int $method, bool $enc = true): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
methodОдна константа CM_* сжатия. Это значение, не набор флагов.
enctrue — создание (шифрование/сжатие); false — чтение (расшифровка/распаковка).

Результат: true если конкретное направление поддерживается данной сборкой libzip; false если нет. Статический метод: экземпляр/открытый архив не нужен.

Отказ: Неизвестный/неподдерживаемый метод даёт false; неверный PHP-тип нарушает договор. Сам метод доступен с libzip 1.7.

Как получить первый результат
  1. CM_DEFLATE — требование приложения
  2. Проверяется enc=true
  3. Только при поддержке включаем соответствующий кодек
Проверить возможность созданияСамостоятельный пример
<?php
declare(strict_types=1);

$method = ZipArchive::CM_DEFLATE;
$available = ZipArchive::isCompressionMethodSupported($method, true);
echo $available ? "Можно создать выбранный формат\n" : "Отказать: нужного кодека нет\n";
Ожидаемый результат
Можно создать выбранный формат

Ожидаемый вывод относится к проверенной сборке главы, где AES-256/DEFLATE поддерживаются. На другой сборке честная ветка отказа запрещает молча выдать документ без требуемой защиты/формата.

Проверить приём и выдачуСамостоятельный пример
<?php
declare(strict_types=1);

$method = ZipArchive::CM_DEFLATE;
$canWrite = ZipArchive::isCompressionMethodSupported($method, true);
$canRead = ZipArchive::isCompressionMethodSupported($method, false);
if ($canWrite && $canRead) {
    echo "Сервер может принять и выдать этот формат\n";
} else {
    echo "Нужна другая сборка или согласованный формат\n";
}
Ожидаемый результат
Сервер может принять и выдать этот формат

Способность прочитать формат не обязана означать способность создать его; обе проверки влияют на допуск сервера к обмену. Вывод указан для проверенной сборки, возможности клиента проверяются отдельно.

Наличие CM_* / EM_* в классе не доказывает реализацию алгоритма. Поддержка здесь относится к серверной сборке, а не к архиватору получателя. Согласуйте формат с получателем и откажите явно, если обязательное шифрование недоступно.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setCompressionName.

ZipArchive::isEncryptionMethodSupportedВыбрать шифрование, которое умеет установленная libzip.

PHP 8.0 / PECL 1.19+, libzip 1.7+ · zip

isEncryptionMethodSupported(int $method, bool $enc = true): bool

Выбрать шифрование, которое умеет установленная libzip.

Синтаксис: isEncryptionMethodSupported(int $method, bool $enc = true): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
methodОдна константа EM_* шифрования.
enctrue — создание (шифрование/сжатие); false — чтение (расшифровка/распаковка).

Результат: true если конкретное направление поддерживается данной сборкой libzip; false если нет. Статический метод: экземпляр/открытый архив не нужен.

Отказ: Неизвестный/неподдерживаемый метод даёт false; неверный PHP-тип нарушает договор. Сам метод доступен с libzip 1.7.

Как получить первый результат
  1. EM_AES_256 — требование приложения
  2. Проверяется enc=true
  3. Только при поддержке включаем соответствующий кодек
Проверить возможность созданияСамостоятельный пример
<?php
declare(strict_types=1);

$method = ZipArchive::EM_AES_256;
$available = ZipArchive::isEncryptionMethodSupported($method, true);
echo $available ? "Можно создать выбранный формат\n" : "Отказать: нужного кодека нет\n";
Ожидаемый результат
Можно создать выбранный формат

Ожидаемый вывод относится к проверенной сборке главы, где AES-256/DEFLATE поддерживаются. На другой сборке честная ветка отказа запрещает молча выдать документ без требуемой защиты/формата.

Проверить приём и выдачуСамостоятельный пример
<?php
declare(strict_types=1);

$method = ZipArchive::EM_AES_256;
$canWrite = ZipArchive::isEncryptionMethodSupported($method, true);
$canRead = ZipArchive::isEncryptionMethodSupported($method, false);
if ($canWrite && $canRead) {
    echo "Сервер может принять и выдать этот формат\n";
} else {
    echo "Нужна другая сборка или согласованный формат\n";
}
Ожидаемый результат
Сервер может принять и выдать этот формат

Способность прочитать формат не обязана означать способность создать его; обе проверки влияют на допуск сервера к обмену. Вывод указан для проверенной сборки, возможности клиента проверяются отдельно.

Наличие CM_* / EM_* в классе не доказывает реализацию алгоритма. Поддержка здесь относится к серверной сборке, а не к архиватору получателя. Согласуйте формат с получателем и откажите явно, если обязательное шифрование недоступно.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setEncryptionName.

ZipArchive::setCompressionIndexВыбрать степень сжатия текстового отчёта либо оставить уже сжатые данные без повторной работы.

PHP 7.0 / PECL 1.13+ · zip

setCompressionIndex(int $index, int $method, int $compflags = 0): bool

Выбрать степень сжатия текстового отчёта либо оставить уже сжатые данные без повторной работы.

Синтаксис: setCompressionIndex(int $index, int $method, int $compflags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс/имя уже добавленной записи.
methodCM_STORE — без сжатия; CM_DEFLATE — deflate; CM_DEFAULT — выбор библиотеки. Прочие методы зависят от libzip.
compflagsПараметры зависят от метода. Для DEFLATE: 1..9 — уровень, 0 — стандартный уровень, НЕ «без сжатия». Для STORE уровень не нужен; CM_DEFAULT игнорирует flags.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неподдерживаемом методе, неверной записи/комбинации, read-only архиве. Пустое имя — ValueError.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt содержит paid:42
  2. CM_STORE выбирает хранение без сжатия
  3. close применяет выбор
  4. stat показывает comp_method=CM_STORE
Управлять форматом записиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!$zip->setCompressionIndex(0, ZipArchive::CM_STORE)) throw new RuntimeException("Нет STORE");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->statIndex(0)["comp_method"] === ZipArchive::CM_STORE ? "Без сжатия\n" : "Другой метод\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Без сжатия

Не печатаем размеры сжатых байтов как константу: они зависят от кодека и его версии.

Сжать текстовый отчётСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!ZipArchive::isCompressionMethodSupported(ZipArchive::CM_DEFLATE, true)) throw new RuntimeException("Нужен DEFLATE");
if (!$zip->setCompressionIndex(0, ZipArchive::CM_DEFLATE, 6)) throw new RuntimeException("Не настроено");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Проверяем восстановление содержимого, а не обещаем, что крошечная строка станет меньше: служебные данные могут перевесить выигрыш.

Для BZIP2/XZ уровни 1..9, 0 — по умолчанию; ZSTD имеет собственный диапазон. Современные libzip могут поддерживать больше методов, чем перечисляет краткая страница PHP. Не выбирайте уровень по абстрактному обещанию скорости: измерьте ваш корпус и учитывайте совместимость получателя.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::isCompressionMethodSupported, ZipArchive::statIndex.

ZipArchive::setEncryptionIndexЗащитить содержимое выбранного документа паролем перед отправкой.

PHP 7.2 / PECL 1.14+, libzip 1.2+; nullable password PHP 8.0+ · zip

setEncryptionIndex(int $index, int $method, ?string $password = null): bool

Защитить содержимое выбранного документа паролем перед отправкой.

Синтаксис: setEncryptionIndex(int $index, int $method, ?string $password = null): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
indexИндекс/имя записи, которую требуется шифровать.
methodEM_AES_128, EM_AES_192, EM_AES_256; EM_NONE — убрать шифрование. Доступность проверяется отдельно. EM_TRAD_PKWARE — устаревший слабый вариант совместимости.
passwordЯвный пароль записи либо null: взять default password от setPassword(). SensitiveParameter скрывает аргумент в некоторых трассировках, но не защищает логи/память/исходный код.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неподдерживаемом алгоритме/записи/настройке; ошибка может проявиться при close(). Некорректный пароль чтения проверяется при чтении данных, а не обязательно open().

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt содержит paid:42
  2. AES-256 + учебный пароль назначены записи
  3. close сохраняет шифрованные байты
  4. Открываем снова, задаём пароль и читаем
Шифрование одного документаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!ZipArchive::isEncryptionMethodSupported(ZipArchive::EM_AES_256, true)) throw new RuntimeException("Нужен AES-256");
if (!$zip->setEncryptionIndex(0, ZipArchive::EM_AES_256, "lesson-only-password")) throw new RuntimeException("Не зашифровано");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setPassword("lesson-only-password");
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Это явно учебный пароль. В рабочем сервисе секрет поступает из защищённого источника и не попадает в репозиторий/журнал. Требуется AES-поддержка сборки.

Один default password для выбранных записейСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!ZipArchive::isEncryptionMethodSupported(ZipArchive::EM_AES_256, true)) throw new RuntimeException("Нужен AES-256");
$zip->setPassword("lesson-only-password");
if (!$zip->setEncryptionIndex(0, ZipArchive::EM_AES_256)) throw new RuntimeException("Не настроено");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setPassword("lesson-only-password");
echo $zip->statIndex(0)["encryption_method"] === ZipArchive::EM_AES_256 ? "AES-256\n" : "Нет\n";
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
AES-256
paid:42

setPassword задаёт default, а setEncryption* выбирает шифрование конкретной записи. Пропуск второго шага оставит её открытой.

ZIP обычно раскрывает имена и комментарии: шифрование содержимого не означает сокрытия всей структуры. Не понижайте незаметно AES до слабого алгоритма. Надёжность зависит и от пароля/канала его передачи, и от поддержки получателя; шифрование архива не заменяет контроль доступа сервиса.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setPassword, ZipArchive::isEncryptionMethodSupported.

ZipArchive::setCompressionNameВыбрать степень сжатия текстового отчёта либо оставить уже сжатые данные без повторной работы.

PHP 7.0 / PECL 1.13+ · zip

setCompressionName(string $name, int $method, int $compflags = 0): bool

Выбрать степень сжатия текстового отчёта либо оставить уже сжатые данные без повторной работы.

Синтаксис: setCompressionName(string $name, int $method, int $compflags = 0): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИндекс/имя уже добавленной записи.
methodCM_STORE — без сжатия; CM_DEFLATE — deflate; CM_DEFAULT — выбор библиотеки. Прочие методы зависят от libzip.
compflagsПараметры зависят от метода. Для DEFLATE: 1..9 — уровень, 0 — стандартный уровень, НЕ «без сжатия». Для STORE уровень не нужен; CM_DEFAULT игнорирует flags.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неподдерживаемом методе, неверной записи/комбинации, read-only архиве. Пустое имя — ValueError.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt содержит paid:42
  2. CM_STORE выбирает хранение без сжатия
  3. close применяет выбор
  4. stat показывает comp_method=CM_STORE
Управлять форматом записиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!$zip->setCompressionName("order.txt", ZipArchive::CM_STORE)) throw new RuntimeException("Нет STORE");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->statIndex(0)["comp_method"] === ZipArchive::CM_STORE ? "Без сжатия\n" : "Другой метод\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Без сжатия

Не печатаем размеры сжатых байтов как константу: они зависят от кодека и его версии.

Сжать текстовый отчётСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!ZipArchive::isCompressionMethodSupported(ZipArchive::CM_DEFLATE, true)) throw new RuntimeException("Нужен DEFLATE");
if (!$zip->setCompressionName("order.txt", ZipArchive::CM_DEFLATE, 6)) throw new RuntimeException("Не настроено");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Проверяем восстановление содержимого, а не обещаем, что крошечная строка станет меньше: служебные данные могут перевесить выигрыш.

Для BZIP2/XZ уровни 1..9, 0 — по умолчанию; ZSTD имеет собственный диапазон. Современные libzip могут поддерживать больше методов, чем перечисляет краткая страница PHP. Не выбирайте уровень по абстрактному обещанию скорости: измерьте ваш корпус и учитывайте совместимость получателя.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::isCompressionMethodSupported, ZipArchive::statIndex.

ZipArchive::setEncryptionNameЗащитить содержимое выбранного документа паролем перед отправкой.

PHP 7.2 / PECL 1.14+, libzip 1.2+; nullable password PHP 8.0+ · zip

setEncryptionName(string $name, int $method, ?string $password = null): bool

Защитить содержимое выбранного документа паролем перед отправкой.

Синтаксис: setEncryptionName(string $name, int $method, ?string $password = null): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
nameИндекс/имя записи, которую требуется шифровать.
methodEM_AES_128, EM_AES_192, EM_AES_256; EM_NONE — убрать шифрование. Доступность проверяется отдельно. EM_TRAD_PKWARE — устаревший слабый вариант совместимости.
passwordЯвный пароль записи либо null: взять default password от setPassword(). SensitiveParameter скрывает аргумент в некоторых трассировках, но не защищает логи/память/исходный код.

Результат: true при принятой операции, false при отказе. Изменения записываются на диск только успешным close(); true отдельного изменения ещё не подтверждает сохранение.

Отказ: false при неподдерживаемом алгоритме/записи/настройке; ошибка может проявиться при close(). Некорректный пароль чтения проверяется при чтении данных, а не обязательно open().

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt содержит paid:42
  2. AES-256 + учебный пароль назначены записи
  3. close сохраняет шифрованные байты
  4. Открываем снова, задаём пароль и читаем
Шифрование одного документаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!ZipArchive::isEncryptionMethodSupported(ZipArchive::EM_AES_256, true)) throw new RuntimeException("Нужен AES-256");
if (!$zip->setEncryptionName("order.txt", ZipArchive::EM_AES_256, "lesson-only-password")) throw new RuntimeException("Не зашифровано");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setPassword("lesson-only-password");
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Это явно учебный пароль. В рабочем сервисе секрет поступает из защищённого источника и не попадает в репозиторий/журнал. Требуется AES-поддержка сборки.

Один default password для выбранных записейСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!ZipArchive::isEncryptionMethodSupported(ZipArchive::EM_AES_256, true)) throw new RuntimeException("Нужен AES-256");
$zip->setPassword("lesson-only-password");
if (!$zip->setEncryptionName("order.txt", ZipArchive::EM_AES_256)) throw new RuntimeException("Не настроено");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setPassword("lesson-only-password");
echo $zip->statIndex(0)["encryption_method"] === ZipArchive::EM_AES_256 ? "AES-256\n" : "Нет\n";
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
AES-256
paid:42

setPassword задаёт default, а setEncryption* выбирает шифрование конкретной записи. Пропуск второго шага оставит её открытой.

ZIP обычно раскрывает имена и комментарии: шифрование содержимого не означает сокрытия всей структуры. Не понижайте незаметно AES до слабого алгоритма. Надёжность зависит и от пароля/канала его передачи, и от поддержки получателя; шифрование архива не заменяет контроль доступа сервиса.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setPassword, ZipArchive::isEncryptionMethodSupported.

ZipArchive::setPasswordПередать пароль для чтения защищённых записей и default для их явного шифрования.

PHP 5.6 / PECL 1.12.4+; default для шифрования PHP 7.2 / libzip 1.2+ · zip

setPassword(string $password): bool

Передать пароль для чтения защищённых записей и default для их явного шифрования.

Синтаксис: setPassword(string $password): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
passwordНепустая строка; в сигнатуре PHP это SensitiveParameter. Пароль привязан к открытому архиву.

Результат: true означает, что default password установлен; это НЕ проверка пароля по всем записям и НЕ включение шифрования.

Отказ: Пустой пароль в PHP 8.5.11 возвращает false; неоткрытый архив — ValueError. Неверный пароль может обнаружиться позже при чтении.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. order.txt ещё обычный текст
  2. setPassword устанавливает default
  3. Без setEncryption* запись остаётся EM_NONE
Пароль сам по себе не шифруетСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
$zip->setPassword("lesson-only-password");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
echo $zip->statIndex(0)["encryption_method"] === ZipArchive::EM_NONE ? "Не зашифровано\n" : "Зашифровано\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
Не зашифровано

Это проверка распространённого ошибочного ожидания. Для защиты нужен setEncryptionName/Index.

Прочитать защищённый документСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
if (!$zip->addFromString('order.txt', 'paid:42')) {
    throw new RuntimeException('Запись не добавлена');
}
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!ZipArchive::isEncryptionMethodSupported(ZipArchive::EM_AES_256, true)) throw new RuntimeException("Нужен AES");
$zip->setEncryptionName("order.txt", ZipArchive::EM_AES_256, "lesson-only-password");
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if ($zip->open($path) !== true) {
    throw new RuntimeException('Архив не прочитан');
}
if (!$zip->setPassword("lesson-only-password")) throw new RuntimeException("Нет пароля");
echo $zip->getFromName("order.txt"), "\n";
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
if (is_file($path)) unlink($path);
Ожидаемый результат
paid:42

Пароль задаётся заново после повторного открытия; чтение доказывает, что он подходит к этой записи.

Не проверяйте правильность пароля только по bool setPassword(). Для разных паролей отдельных записей меняйте default перед чтением нужной записи. Не выводите пароль даже при диагностике отказа.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::setEncryptionName, ZipArchive::getFromName.

ZipArchive::registerProgressCallbackПоказать продвижение длительной сборки ZIP во время close().

PHP 8.0 / PECL 1.17+, libzip 1.3+ · zip

registerProgressCallback(float $rate, callable $callback): bool

Показать продвижение длительной сборки ZIP во время close().

Синтаксис: registerProgressCallback(float $rate, callable $callback): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
rateПорог изменения доли прогресса между сообщениями, обычно 0.1 (10%). Используйте конечное значение от 0 до 1.
callbackФункция получает float $state в диапазоне 0..1; её результат игнорируется. Она вызывается библиотекой при close(), а не сразу при регистрации.

Результат: true — callback зарегистрирован; false — отказ регистрации. Это не запуск фоновой задачи.

Отказ: Неверный callable — TypeError; архив должен быть открыт. Исключение callback не является штатным способом отмены; используйте отдельный cancel callback.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Добавлена строка отчёта
  2. Регистрируем функцию, сохраняющую долю
  3. close вызывает её
  4. Проверяем факт сообщения без обещания количества
Получить прогресс без нестабильного списка процентовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString("report.txt", str_repeat("order;42\n", 1000));
$seen = false;
$zip->registerProgressCallback(0.1, function (float $state) use (&$seen): void {
    if ($state >= 0.0 && $state <= 1.0) $seen = true;
});
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
echo $seen ? "Прогресс получен\n" : "Нет сообщений\n";
unlink($path);
Ожидаемый результат
Прогресс получен

use (&$seen) даёт callback доступ к внешнему флагу по ссылке. Доля относится к работе libzip при закрытии, а не ко всему HTTP-запросу.

Сохранить последнее наблюдениеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString("report.txt", str_repeat("line\n", 1000));
$last = 0.0;
$zip->registerProgressCallback(0.25, function (float $state) use (&$last): void { $last = $state; });
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
echo $last >= 0.0 && $last <= 1.0 ? "Доля в диапазоне\n" : "Ошибка\n";
unlink($path);
Ожидаемый результат
Доля в диапазоне

Не привязываемся к точному числу вызовов, времени или шагу. Для интерфейса задача обычно пишет прогресс в отдельное состояние, а не печатает в ZIP-ответ.

Callback выполняется синхронно; тяжёлая работа внутри замедлит архивирование. Не вызывайте рекурсивно close()/open()/изменение того же архива из callback. Уже открытый объект не делает код параллельным.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::registerCancelCallback, ZipArchive::close.

ZipArchive::registerCancelCallbackДать пользователю возможность отменить затратное сохранение архива.

PHP 8.0 / PECL 1.17+, libzip 1.6+ · zip

registerCancelCallback(callable $callback): bool

Дать пользователю возможность отменить затратное сохранение архива.

Синтаксис: registerCancelCallback(callable $callback): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
callbackФункция без параметров возвращает int: 0 продолжать, любое ненулевое значение отменить. Выбирайте явные 0/1.

Результат: true — регистрация успешна. При отмене последующий close() даёт warning/false и status=ER_CANCELLED.

Отказ: Неверный callable — TypeError; неправильный возвращаемый тип может вызвать TypeError. Не выбрасывайте исключение как протокол отмены; PHP 8.5.11 рассматривает исключение callback как отказ.

Перед этим вызовом open() должен успешно вернуть именно true. В PHP 8.5.11 метод без действующего открытого архива бросает ValueError. После close() для следующего чтения нужен новый open().

Как получить первый результат
  1. Создаётся отчёт
  2. Callback возвращает 0
  3. close сохраняет ZIP
  4. Вторая ситуация: callback возвращает 1 → отмена
Разрешить сохранениеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString("report.txt", "order;42");
$zip->registerCancelCallback(static fn(): int => 0);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
echo "Сохранено\n";
unlink($path);
Ожидаемый результат
Сохранено

Короткая стрелочная функция возвращает одно значение; static показывает, что ей не нужен $this.

Обработать штатную отменуСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString("report.txt", str_repeat("order;42\n", 1000));
$zip->registerCancelCallback(static fn(): int => 1);
$warning = false;
set_error_handler(function (int $level) use (&$warning): bool {
    if ($level !== E_WARNING) return false;
    $warning = true; return true;
});
try { $saved = $zip->close(); } finally { restore_error_handler(); }
echo !$saved && $warning && $zip->status === ZipArchive::ER_CANCELLED ? "Отменено\n" : "Другой результат\n";
if (is_file($path)) unlink($path);
Ожидаемый результат
Отменено

Перехват warning ограничен одним ожидаемым close(), затем восстановлен. Архив после этой попытки закрыт; повторный close() не нужен.

Проверки отмены происходят в точках работы libzip, это не принудительное завершение по точному таймеру. Для уже существующего ZIP неудачный commit должен оставить исходную версию; внешние побочные действия callback этим не откатываются.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::registerProgressCallback, ZipArchive::getStatusString.

zip_openРаспознать старый код, открывающий поставку ресурсом вместо объекта. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_open(string $filename): resource|int|false

Распознать старый код, открывающий поставку ресурсом вместо объекта. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_open(string $filename): resource|int|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
filenameПуть существующего ZIP. Нет параметра flags и нет создания нового архива.

Результат: Ресурс каталога ZIP; код ошибки int или false при отказе. Проверяйте is_resource(), а не truthiness.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. lesson.zip создан современным API
  2. zip_open(path) → ресурс архива
  3. is_resource подтверждает успех
  4. zip_close освобождает ресурс
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
echo is_resource($archive) ? "Ресурс ZIP\n" : "Нет\n";
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Ресурс ZIP

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$missing = zip_open(__DIR__ . "/missing.zip");
echo is_int($missing) ? "Код отказа\n" : "Иной результат\n";
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Код отказа

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::open.

zip_closeОсвободить ресурс архива после чтения старым API. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_close(resource $zip): void

Освободить ресурс архива после чтения старым API. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_close(resource $zip): void. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zipЖивой ресурс, ранее возвращённый zip_open(). Не ZipArchive и не ресурс отдельной записи.

Результат: Ничего не возвращает; закрывает ресурс архива.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. lesson.zip открыт через zip_open
  2. zip_close(archive) освобождает его
  3. is_resource возвращает false
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
zip_close($archive);
echo is_resource($archive) ? "Открыт\n" : "Закрыт\n";
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Закрыт

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
echo zip_entry_name($entry), "\n";
zip_entry_close($entry);
zip_close($archive);echo "Оба ресурса освобождены\n";
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
order.txt
Оба ресурса освобождены

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::close.

zip_readПоследовательно получить записи в старом импортере. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_read(resource $zip): resource|false

Последовательно получить записи в старом импортере. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_read(resource $zip): resource|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zipЖивой ресурс архива; каждый успешный вызов двигает внутренний указатель к следующей записи.

Результат: Ресурс следующей записи либо false на конце/при отказе. Хотя текст страницы PHP упоминает код ошибки, сигнатура и PHP 8.5.11 возвращают здесь resource|false, не int.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_read работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
echo zip_entry_name($entry), "\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
order.txt

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$names = [];
while (($entry = zip_read($archive)) !== false) {
    $names[] = zip_entry_name($entry);
    zip_entry_close($entry);
}
echo implode(",", $names), "\n";
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
order.txt

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getNameIndex.

zip_entry_openПравильно понять старую проверку открытия записи: её результат — bool. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_entry_open(resource $zip_dp, resource $zip_entry, string $mode = "rb"): bool

Правильно понять старую проверку открытия записи: её результат — bool. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_entry_open(resource $zip_dp, resource $zip_entry, string $mode = "rb"): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zip_dpРесурс архива.
zip_entryРесурс, полученный zip_read(), который продолжат передавать в zip_entry_read().
modeИсторический аргумент; игнорируется, поддерживается чтение rb. Он не даёт запись в ZIP.

Результат: true/false; не возвращает новый поток или ресурс.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_entry_open работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
$opened = zip_entry_open($archive, $entry);
echo $opened ? "Готово к чтению\n" : "Отказ\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Готово к чтению

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
if (!zip_entry_open($archive, $entry, "rb")) throw new RuntimeException("Не открыт");
echo zip_entry_read($entry, 4), "\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
paid

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getStream.

zip_entry_closeОсвободить отдельную запись, сохранив возможность обхода архива. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_entry_close(resource $zip_entry): bool

Освободить отдельную запись, сохранив возможность обхода архива. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_entry_close(resource $zip_entry): bool. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zip_entryЖивой ресурс записи от zip_read().

Результат: true при закрытии. После закрытия этот ресурс нельзя читать повторно.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_entry_close работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
var_export(zip_entry_close($entry)); echo "\n";
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
true

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$closed = 0;
while (($entry = zip_read($archive)) !== false) {
    if (zip_entry_close($entry)) $closed++;
}
echo $closed, " освобождённая запись\n";
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
1 освобождённая запись

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getStream.

zip_entry_readПрочитать байты документа порциями в унаследованном обработчике. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_entry_read(resource $zip_entry, int $len = 1024): string|false

Прочитать байты документа порциями в унаследованном обработчике. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_entry_read(resource $zip_entry, int $len = 1024): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zip_entryРесурс записи, не bool от zip_entry_open().
lenЗапрошенное число распакованных байтов; по умолчанию 1024. PHP 8.5.11 также заменяет неположительное значение на 1024.

Результат: Строка прочитанных байтов; "" на конце. В PHP 8.5.11 внутренний отрицательный результат чтения также превращается в "", поэтому этот API плохо различает EOF и повреждение; новый код использует ZipArchive.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_entry_read работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
echo zip_entry_read($entry, 4), "\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
paid

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
$body = "";
while (($chunk = zip_entry_read($entry, 3)) !== "" && $chunk !== false) {
    $body .= $chunk;
    if (strlen($body) > 1024) throw new RuntimeException("Лимит");
}
echo $body, "\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
paid:42

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getStream.

zip_entry_nameПолучить имя найденного документа без чтения его содержимого. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_entry_name(resource $zip_entry): string|false

Получить имя найденного документа без чтения его содержимого. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_entry_name(resource $zip_entry): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zip_entryРесурс записи от zip_read(); родительский архив ещё открыт.

Результат: Имя записи в ZIP или false при недоступном состоянии.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_entry_name работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
echo zip_entry_name($entry), "\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
order.txt

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
$name = zip_entry_name($entry);
echo $name === "order.txt" ? "Ожидаемый документ\n" : "Не импортировать\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Ожидаемый документ

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::getNameIndex.

zip_entry_filesizeОценить распакованный размер перед старым импортом. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_entry_filesize(resource $zip_entry): int|false

Оценить распакованный размер перед старым импортом. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_entry_filesize(resource $zip_entry): int|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zip_entryРесурс записи от zip_read().

Результат: Размер распакованных данных в байтах либо false. 0 — корректный размер пустой записи.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_entry_filesize работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
echo zip_entry_filesize($entry), " байтов\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
7 байтов

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
$size = zip_entry_filesize($entry);
echo $size !== false && $size <= 1024 ? "Предварительный лимит соблюдён\n" : "Отказ\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Предварительный лимит соблюдён

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::statIndex.

zip_entry_compressedsizeОтличить место внутри ZIP от объёма будущей распаковки. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_entry_compressedsize(resource $zip_entry): int|false

Отличить место внутри ZIP от объёма будущей распаковки. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_entry_compressedsize(resource $zip_entry): int|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zip_entryРесурс записи от zip_read().

Результат: Число сжатых байтов либо false. Это не размер всего ZIP с заголовками.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_entry_compressedsize работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
echo zip_entry_compressedsize($entry), " байтов STORE\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
7 байтов STORE

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
$packed = zip_entry_compressedsize($entry);
$plain = zip_entry_filesize($entry);
echo $packed === $plain ? "Для STORE размеры совпали\n" : "Размеры различаются\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Для STORE размеры совпали

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::statIndex.

zip_entry_compressionmethodПрочитать историческое текстовое имя способа сжатия. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

PHP 4.1+, PHP 5.2+, PECL zip 1.0+; deprecated PHP 8.0+ · zip

zip_entry_compressionmethod(resource $zip_entry): string|false

Прочитать историческое текстовое имя способа сжатия. Функция deprecated с PHP 8.0; изучаем для миграции, новый код пишем на ZipArchive.

Синтаксис: zip_entry_compressionmethod(resource $zip_entry): string|false. Параметры перечислены в порядке передачи. Значение после = — значение по умолчанию; знак | между типами означает варианты результата, а между флагами в вызове — объединение битовых признаков.

АргументДоговор
zip_entryРесурс записи от zip_read().

Результат: Историческая строка (stored, shrunk, reduced, imploded, tokenized, deflated, deflatedX, implodedX) или false для неизвестного метода/состояния. Это не CM_*.

Отказ: PHP 8+ сообщает E_DEPRECATED при каждом вызове. Неверный PHP-тип/закрытый или чужой ресурс — TypeError; отказ конкретной операции проверяйте по описанному результату. Для zip_open пустое имя — ValueError.

Как получить первый результат
  1. В lesson.zip лежит order.txt с paid:42
  2. zip_open → ресурс архива
  3. zip_read → ресурс order.txt
  4. zip_entry_compressionmethod работает с этой записью
  5. Сначала закрывается запись, затем архив
Разобрать существующий вызовСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
echo zip_entry_compressionmethod($entry), "\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
stored

Подготовка создаёт собственный архив. Обработчик перехватывает только E_DEPRECATED и только внутри учебного старого API; остальные ошибки не скрываются.

Практическая граница старого APIСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/lesson.zip';
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::EXCL) !== true) {
    throw new RuntimeException('Не удалось создать учебный архив');
}
$zip->addFromString('order.txt', 'paid:42');
$zip->setCompressionName('order.txt', ZipArchive::CM_STORE);
if (!$zip->close()) {
    throw new RuntimeException('Архив не сохранён');
}
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
    if ($level !== E_DEPRECATED) return false;
    $deprecated = true;
    return true;
});
try {
$archive = zip_open($path);
if (!is_resource($archive)) throw new RuntimeException('Не открыт ZIP');
$entry = zip_read($archive);
if ($entry === false) throw new RuntimeException('Нет записи');
$method = zip_entry_compressionmethod($entry);
echo $method === "stored" ? "Байты не сжимались\n" : "Другой или неизвестный метод\n";
zip_entry_close($entry);
zip_close($archive);
} finally {
    restore_error_handler();
}
unlink($path);
Ожидаемый результат
Байты не сжимались

Старые ресурсы не смешиваются с ZipArchive и обычными потоками. После этого разбора переходите к связанной современной карточке.

Оба примера сохраняют старый договор для понимания существующего приложения; не подавляйте deprecated глобальным @. При миграции меняется не только имя функции: архив становится объектом, запись выбирается именем/индексом, а поток получается getStream(). Закрывайте запись раньше архива. Метаданные и имена недоверенного ZIP всё равно требуют проверки.

Разбор общей подготовки и уборки: первый архив.

Продолжение: ZipArchive::statIndex.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.