В веб‑разработке нередко возникает задача упаковать файлы в ZIP‑архив или, наоборот, распаковать его. В PHP для этого есть встроенный класс ZipArchive — он не требует сторонних библиотек и покрывает большинство типовых сценариев.
Ниже разберём, как создавать ZIP‑архивы и извлекать из них файлы, с примерами и пояснениями.
Подготовка: проверка доступности расширения
Класс ZipArchive входит в стандартное расширение zip. Прежде чем писать код, убедитесь, что оно включено в вашей установке PHP. Проще всего проверить через:
<?php var_dump(class_exists('ZipArchive'));
Если вернётся bool(true) — можно работать. Если false, проверьте php.ini: должно быть включено extension=zip (или php_zip.dll на Windows).
Создание ZIP‑архива
Базовый пример: добавление одного файла
Допустим, у нас есть файл readme.txt в папке проекта, и мы хотим положить его в архив archive.zip.
<?php $zip = new ZipArchive(); $filename = 'archive.zip'; // Создаём архив; флаг ZipArchive::CREATE создаёт архив, если его нет if ($zip->open($filename, ZipArchive::CREATE) !== true) { die('Не удалось создать архив'); } // Добавляем файл в архив $zip->addFile('readme.txt', 'readme.txt'); $zip->close(); echo 'Архив создан успешно';
Здесь второй аргумент addFile — это имя файла внутри архива. Если его не указать, PHP возьмёт путь к файлу как имя внутри архива.
Добавление нескольких файлов и папок
Часто нужно упаковать целую директорию. Для этого удобно рекурсивно обойти папку и добавить файлы:
<?php function addFilesFromDir($zip, $sourceDir, $baseDir = '') { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($sourceDir), RecursiveIteratorIterator::SELF_FIRST ); foreach ($files as $file) { if (!$file->isFile()) { continue; } $filePath = $file->getRealPath(); $relativePath = preg_replace('#^' . preg_quote($sourceDir, '#') . '#', '', $filePath); // Убираем ведущий слэш, чтобы структура была корректной $relativePath = ltrim($relativePath, DIRECTORY_SEPARATOR); // Если задана базовая папка, добавляем её к пути внутри архива $archivePath = $baseDir ? $baseDir . DIRECTORY_SEPARATOR . $relativePath : $relativePath; $zip->addFile($filePath, $archivePath); } } $zip = new ZipArchive(); if ($zip->open('site-backup.zip', ZipArchive::CREATE) !== true) { die('Не удалось создать архив'); } addFilesFromDir($zip, __DIR__ . '/public', 'public'); $zip->close(); echo 'Архив site-backup.zip создан';
В этом примере содержимое папки public попадёт в архив с сохранением структуры, но внутри архива будет папка public/.
Важные нюансы при создании архива
- Права доступа: убедитесь, что у PHP есть права на чтение исходных файлов и запись в директорию, где создаётся архив.
- Ошибки при добавлении файлов:
addFile()возвращаетfalseпри ошибке. Можно проверять результат и логировать проблемы:phpif (!$zip->addFile($filePath, $archivePath)) { echo 'Ошибка добавления файла: ' . $filePath . PHP_EOL; }
- Кодировка имён файлов: в ZIP имена файлов могут быть в разных кодировках. В большинстве случаев UTF‑8 работает нормально, но на старых системах возможны проблемы с кириллицей. Если сталкиваетесь с искажёнными именами, можно попробовать явно задать кодировку или использовать альтернативные инструменты (например, консольный
zipчерезexec).
Распаковка ZIP‑архива
Для распаковки используется метод extractTo.
Простой пример: извлечь всё в папку
<?php $zip = new ZipArchive(); $filename = 'archive.zip'; $destination = __DIR__ . '/extracted'; if (!is_dir($destination)) { mkdir($destination, 0755, true); } if ($zip->open($filename) !== true) { die('Не удалось открыть архив'); } if ($zip->extractTo($destination) === false) { die('Ошибка при распаковке'); } $zip->close(); echo 'Файлы извлечены в ' . $destination;
Извлечение отдельных файлов
Иногда нужно достать только конкретные файлы. Для этого можно перебрать содержимое архива и извлечь нужные:
<?php $zip = new ZipArchive(); if ($zip->open('archive.zip') !== true) { die('Не удалось открыть архив'); } $destination = __DIR__ . '/partial-extract'; if (!is_dir($destination)) { mkdir($destination, 0755, true); } for ($i = 0; $i < $zip->numFiles; $i++) { $name = $zip->getNameIndex($i); // Допустим, нам нужны только файлы с расширением .txt if (pathinfo($name, PATHINFO_EXTENSION) === 'txt') { $zip->extractTo($destination, $name); } } $zip->close();
numFiles — общее количество записей в архиве, getNameIndex возвращает имя файла по индексу.
Проверка содержимого архива без распаковки
Полезно сначала посмотреть, что внутри, особенно если архив приходит от пользователя.
<?php $zip = new ZipArchive(); if ($zip->open('archive.zip') !== true) { die('Не удалось открыть архив'); } echo '<ul>'; for ($i = 0; $i < $zip->numFiles; $i++) { $stats = $zip->statIndex($i); echo '<li>' . htmlspecialchars($stats['name']) . ' (' . $stats['size'] . ' байт)</li>'; } echo '</ul>'; $zip->close();
Так можно вывести список файлов и их размеры, не извлекая ничего на диск.
Безопасность и типичные ошибки
При работе с архивами, особенно если они загружены пользователями, важно соблюдать меры предосторожности:
- Не извлекайте файлы поверх существующих без проверки. Лучше распаковывать в изолированную временную папку.
- Контролируйте пути внутри архива. В ZIP могут быть относительные пути вроде
../../etc/passwd. При извлечении это может перезаписать системные файлы. Проверяйте имена файлов перед распаковкой:phpif (strpos($name, '..') !== false || strpos($name, ':') !== false) { // Пропускаем потенциально опасные пути continue; }
- Ограничивайте размер и количество файлов. Если архив загружается пользователем, добавьте лимиты на общий размер, количество файлов и максимальный размер одного файла.
- Проверяйте целостность архива.
ZipArchive::checkZipConsistencyпозволяет проверить архив на повреждения (поддерживается не во всех сборках PHP).
Альтернативные подходы
- Консольные утилиты: на сервере может быть установлен
zipиunzip. Тогда можно вызывать их черезexec()илиshell_exec(). Это удобно для больших архивов и сложных сценариев, но требует осторожности с безопасностью. - Библиотеки для сложных случаев: если нужна продвинутая работа с ZIP (шифрование, потоковая обработка, специфические форматы), можно рассмотреть сторонние решения, но для большинства задач
ZipArchiveдостаточно.
Пример: безопасный импорт архива от пользователя
Представим, что пользователь загружает ZIP с файлами статей. Нам нужно:
- Проверить, что файл действительно ZIP.
- Посмотреть содержимое.
- Распаковать только разрешённые типы файлов в изолированную папку.
<?php $uploadFile = $_FILES['archive']['tmp_name']; $destination = __DIR__ . '/user-uploads/' . uniqid('upload_', true); mkdir($destination, 0755, true); $zip = new ZipArchive(); if ($zip->open($uploadFile) !== true) { die('Файл не является корректным ZIP‑архивом'); } for ($i = 0; $i < $zip->numFiles; $i++) { $stats = $zip->statIndex($i); $name = $stats['name']; // Защита от путей с ".." if (strpos($name, '..') !== false) { continue; } $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if (!in_array($ext, ['txt', 'md', 'html'])) { // Пропускаем неподдерживаемые типы continue; } $zip->extractTo($destination, $name); } $zip->close(); echo 'Файлы успешно обработаны и сохранены в ' . $destination;
