Создание и управление архивными файлами в PHP
Работа с архивами в PHP: обзор инструментов
Архивация файлов является повседневной задачей при разработке на PHP. Это может быть создание резервных копий, упаковка дистрибутивов, обработка загруженных архивов. В PHP существует несколько способов работы с форматами ZIP и TAR. Рассмотрим каждый из них с примерами кода и разбором типичных сложностей.
Основное решение: встроенный класс ZipArchive
Как создать ZIP архив на PHP без внешних библиотек?
Класс ZipArchive (расширение zip) предоставляет все необходимые методы для создания, модификации и извлечения ZIP архивов. Он встроен в PHP и не требует дополнительных установок, кроме включения расширения в php.ini (обычно включено по умолчанию).
$zip = new ZipArchive();
$zipName = '/tmp/example.zip';
if ($zip->open($zipName, ZipArchive::CREATE) === TRUE) {
$zip->addFile('file1.txt', 'file1.txt');
$zip->addFromString('hello.txt', 'Hello World');
$zip->close();
echo 'Архив создан: ' . $zipName;
} else {
echo 'Ошибка создания архива';
}
файлы архив php (работа с архивами в php (zip, tar))
Пояснение шагов:
- Создаётся экземпляр ZipArchive.
- Метод open открывает архив на запись. Флаг ZipArchive::CREATE создаёт новый файл, если его нет.
- addFile добавляет существующий файл из файловой системы.
- addFromString добавляет данные из строки (полезно для динамического контента).
- close закрывает архив и сохраняет изменения.
Типичные ошибки и решения
- ZipArchive не определён: расширение zip не установлено или не включено. Решение: проверить php.ini (extension=zip) или установить пакет php-zip.
- Файл архива не открывается на запись: нет прав на запись в целевую директорию. Решение: установить права 755 или 777 для временной папки.
- Добавление файла с пустым именем: метод addFile вернёт FALSE. Решение: перед добавлением проверять существование файла и корректность пути.
Вариант: PharData для работы с TAR архивами
Как создать TAR архив в PHP, если расширение phar доступно?
Класс PharData является частью расширения Phar и позволяет упаковывать файлы в форматы TAR (с возможностью сжатия Gzip или Bzip2). Он поддерживает как полные архивы, так и потоковое добавление.
$phar = new PharData('/tmp/example.tar');
$phar->addFile('file1.txt', 'file1.txt');
$phar->addFromString('hello.txt', 'Hello World');
// Для сжатия Gzip:
$phar->compress(Phar::GZ);
Метод compress создаёт файл example.tar.gz и удаляет несжатый исходник (если не указать Phar::NONE).
Проблемы:
- Расширение Phar может быть отключено в целях безопасности. Решение: включить в php.ini (phar.readonly = Off).
- Сжатие Gzip требует расширение zlib. Решение: установить php-zlib.
- PharData работает только с TAR, не поддерживает ZIP напрямую.
Вариант: Использование системных утилит tar или zip
Как создать архив, используя внешние программы, если нужна полная совместимость с командной строкой?
Этот подход полезен, когда необходимо использовать продвинутые возможности оригинальных утилит (например, многотомные архивы, исключение по маске, сохранение прав доступа). Вызов происходит через функции exec, shell_exec или system.
$dirToArchive = '/path/to/dir';
$outputArchive = '/tmp/backup.tar.gz';
$command = "tar -czf " . escapeshellarg($outputArchive) . " -C " . escapeshellarg(dirname($dirToArchive)) . " " . escapeshellarg(basename($dirToArchive));
exec($command, $output, $status);
if ($status === 0) {
echo "Архив создан успешно";
} else {
echo "Ошибка: " . implode("\n", $output);
}
Риски и ограничения:
- Безопасность: требуется тщательно экранировать аргументы (используйте escapeshellarg).
- Зависимость от операционной системы. Команды tar/zip могут отсутствовать или иметь другой синтаксис (Windows).
- Обработка вывода ошибок: необходимо проверять код возврата и сообщения.
Вариант: Сторонние библиотеки (Composer)
Как работать с архивами, если нужны расширенные возможности (например, поддержка RAR, 7z, или более удобное API)?
Библиотеки вида maennchen/zipstream-php, pear/archive_tar или splitbrain/php-archive предоставляют альтернативные решения, часто более гибкие или кроссплатформенные.
// Пример с maennchen/zipstream-php (создание ZIP на лету)
use ZipStream\ZipStream;
use ZipStream\Option\Archive;
$opt = new Archive();
$zip = new ZipStream('output.zip', $opt);
$zip->addFile('file1.txt', 'содержимое');
$zip->finish();
Эта библиотека особенно полезна для потоковой передачи больших архивов в ответе HTTP, так как не сохраняет весь архив в памяти.
Недостатки:
- Требуют установки через Composer и могут иметь зависимости.
- Некоторые библиотеки не поддерживают распаковку, только создание.
Выбор метода зависит от требований к проекту. Для большинства случаев достаточно встроенного ZipArchive. Для работы с TAR хорошо подходит PharData. Если нужна максимальная производительность или специфические форматы, следует рассмотреть внешние утилиты или специализированные пакеты.
Расширенные примеры работы с архивами в PHP
Пример 1: Рекурсивное архивирование директории в ZIP
Как упаковать целую папку со всеми вложенными файлами и каталогами?
function archiveDirectory($sourceDir, $zip, $basePath = '') {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
$filePath = $file->getRealPath();
$relativePath = $basePath . $file->getSubPathName();
if ($file->isDir()) {
$zip->addEmptyDir($relativePath);
} else {
$zip->addFile($filePath, $relativePath);
}
}
}
$zip = new ZipArchive();
if ($zip->open('archive.zip', ZipArchive::CREATE) === TRUE) {
archiveDirectory('/path/to/folder', $zip);
$zip->close();
echo 'Архив создан';
} else {
echo 'Ошибка';
}
Результат: файл archive.zip содержит иерархию исходной директории.
Пример 2: Создание ZIP архива в памяти (без записи на диск)
Как отправить архив пользователю непосредственно в ответе HTTP, не сохраняя временный файл?
$zip = new ZipArchive();
$tmpFile = tempnam(sys_get_temp_dir(), 'zip');
if ($zip->open($tmpFile, ZipArchive::CREATE) === TRUE) {
$zip->addFromString('readme.txt', 'Этот архив создан в памяти');
$zip->addFile('image.png', 'images/image.png');
$zip->close();
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="memory.zip"');
header('Content-Length: ' . filesize($tmpFile));
readfile($tmpFile);
unlink($tmpFile);
exit;
}
Результат: браузер предложит скачать файл memory.zip.
Пример 3: Установка пароля на ZIP архив (PHP 7.2+)
Как защитить архив паролем (шифрование AES-256)?
$zip = new ZipArchive();
if ($zip->open('protected.zip', ZipArchive::CREATE) === TRUE) {
$zip->addFromString('secret.txt', 'Конфиденциальные данные');
$zip->setPassword('my_strong_password');
// Шифрование всех файлов
for ($i = 0; $i < $zip->numFiles; $i++) {
$zip->setEncryptionName($zip->getNameIndex($i), ZipArchive::EM_AES_256);
}
$zip->close();
echo 'Архив защищён паролем';
}
Обратите внимание: для работы шифрования требуется PHP собранный с libzip >= 1.2.0.
Пример 4: Чтение и извлечение файлов из TAR с помощью PharData
Как программно получить содержимое TAR архива и извлечь определённые файлы?
$tar = new PharData('example.tar');
// Вывести список файлов
foreach ($tar as $file) {
echo $file->getPathname() . "\n";
}
// Извлечь все файлы в директорию
$tar->extractTo('/destination');
// Извлечь только один файл
$tar->extractTo('/destination', 'path/inside/archive/myfile.txt');
Вывод списка: перечисляются все пути внутри архива.
Пример 5: Создание tar.gz с прогрессом (комбинация внешней утилиты)
Как отображать прогресс создания большого архива через командную строку?
$source = '/var/www/html';
$target = '/backup/site.tar.gz';
$command = "tar -czf " . escapeshellarg($target) . " -C " . escapeshellarg(dirname($source)) . " " . escapeshellarg(basename($source));
$descriptorspec = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
];
$process = proc_open($command, $descriptorspec, $pipes);
if (is_resource($process)) {
// Закрываем stdin
fclose($pipes[0]);
// Читаем stderr (tar выдаёт предупреждения туда)
$errorOutput = stream_get_contents($pipes[2]);
fclose($pipes[2]);
// stdout может быть пустым
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$return_value = proc_close($process);
if ($return_value === 0) {
echo "Архив создан успешно.\n";
} else {
echo "Ошибка: $errorOutput\n";
}
}
Результат: в директории /backup появится файл site.tar.gz.
Пример 6: Фильтрация файлов при архивации (исключение временных файлов)
Как добавить в ZIP только определённые типы файлов, исключая, например, .log и .tmp?
$zip = new ZipArchive();
$zip->open('filtered.zip', ZipArchive::CREATE);
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/var/log', RecursiveDirectoryIterator::SKIP_DOTS)
);
$allowedExtensions = ['log', 'txt']; // измените на нужные
foreach ($files as $file) {
if ($file->isFile()) {
$ext = strtolower($file->getExtension());
if (in_array($ext, $allowedExtensions)) {
$zip->addFile($file->getRealPath(), 'logs/' . $file->getFilename());
}
}
}
$zip->close();
echo 'Архив с отфильтрованными файлами создан';
Пример 7: Работа с архивами через потоковую библиотеку zipstream-php
Как организовать потоковую передачу очень большого ZIP без загрузки в память?
require 'vendor/autoload.php';
use ZipStream\ZipStream;
use ZipStream\Option\Archive;
use ZipStream\Option\File;
$opt = new Archive();
$opt->setSendHttpHeaders(true);
$zip = new ZipStream('large.zip', $opt);
// Добавление большого файла из потока
$fileOpt = new File();
$fileOpt->setMethod(File::METHOD_DEFLATE);
$zip->addFileFromStream('bigfile.dat', fopen('/tmp/bigfile.dat', 'rb'), $fileOpt);
// Добавление из строки
$zip->addFile('notes.txt', 'Some notes');
$zip->finish();
Результат: браузер получит поток с архивом без временных файлов на сервере.
Все перечисленные примеры предоставляют гибкие возможности для работы с архивами. Выбор конкретного подхода зависит от нагрузки, требований к безопасности и доступных расширений PHP.