PHP Zip
Arquivos ZIP agrupam e comprimem ficheiros. Saiba como criar, ler e extrair arquivos ZIP em PHP moderno com a classe ZipArchive.
Um arquivo ZIP é um ficheiro que agrupa um ou mais ficheiros e os comprime usando o algoritmo zip. Os arquivos são amplamente usados para reduzir o tamanho de downloads, agrupar ficheiros relacionados numa única unidade distribuível e criar cópias de segurança de dados. Um arquivo zip normalmente possui a extensão de ficheiro .zip.
Este capítulo mostra como criar, ler e extrair arquivos ZIP em PHP moderno usando a classe integrada ZipArchive, com exemplos executáveis e as armadilhas mais importantes a conhecer.
A classe ZipArchive
As funções procedurais legadas zip_* foram descontinuadas no PHP 7.4 e removidas no PHP 8.0. O código moderno deve usar a classe orientada a objetos ZipArchive, que faz parte da extensão zip incluída (ative ext-zip se ainda não estiver ativa — verifique com extension_loaded('zip')).
Os métodos que serão usados com mais frequência são:
| Método | Finalidade |
|---|---|
open($filename, $flags) | Abre um arquivo para leitura ou escrita. Retorna true ou um código de erro. |
addFile($path, $entryName) | Adiciona um ficheiro do disco ao arquivo. |
addFromString($entryName, $contents) | Adiciona uma entrada a partir de uma string em memória. |
addEmptyDir($dirName) | Adiciona uma entrada de diretório vazio. |
extractTo($directory, $entries) | Extrai todas as entradas (ou um subconjunto escolhido) para um diretório. |
getFromName($entryName) | Lê uma entrada para uma string sem tocar no disco. |
statIndex($i) / numFiles | Inspeciona entradas e as conta. |
getStatusString() | Retorna uma mensagem de estado legível por humanos para tratamento de erros. |
close() | Grava as alterações pendentes e fecha o identificador. |
Importante: As alterações feitas com
addFile()ouaddFromString()só são gravadas no disco quando se chamaclose(). Esquecerclose()produz um arquivo vazio ou corrompido.
Criando um arquivo ZIP
Passe o sinalizador ZipArchive::CREATE para criar um novo arquivo e adicione ZipArchive::OVERWRITE para começar do zero se já existir um ficheiro com esse nome:
$zip = new ZipArchive();
$zipName = 'documents.zip';
if ($zip->open($zipName, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
// Add an entry from a string (no temp file needed).
$zip->addFromString('readme.txt', "Hello from PHP!\n");
// Add a file from disk, optionally under a folder inside the archive.
$zip->addFile('report.csv', 'data/report.csv');
// Add an empty directory entry.
$zip->addEmptyDir('logs');
echo "Adding {$zip->numFiles} entries to {$zipName}.\n";
$zip->close(); // Must be called for the archive to be written.
echo "Archive saved.\n";
} else {
echo "Could not create the archive.\n";
}Assumindo que report.csv existe, isto imprime:
Adding 3 entries to documents.zip.
Archive saved.Leia numFiles antes de close() — após fechar o identificador, o objeto não reflete mais a contagem de entradas.
Lendo entradas sem extrair
É possível inspecionar o conteúdo de um arquivo, ou carregar uma única entrada para a memória, sem descompactar tudo para o disco:
$zip = new ZipArchive();
if ($zip->open('documents.zip') === true) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$entry = $zip->statIndex($i);
echo $entry['name'] . ' (' . $entry['size'] . " bytes)\n";
}
// Read one entry straight into a string.
echo "---\n" . $zip->getFromName('readme.txt');
$zip->close();
}Saída:
readme.txt (16 bytes)
data/report.csv (20 bytes)
logs/ (0 bytes)
---
Hello from PHP!Extraindo um arquivo para o disco
extractTo() descompacta o arquivo. Verifique sempre o valor de retorno e reporte falhas com getStatusString():
$zip = new ZipArchive();
$filename = 'documents.zip';
$extractTo = './extracted_files';
if ($zip->open($filename) === true) {
if (!is_dir($extractTo)) {
mkdir($extractTo, 0755, true);
}
if ($zip->extractTo($extractTo) === true) {
echo "Archive extracted successfully.";
} else {
echo "Extraction failed: " . $zip->getStatusString();
}
$zip->close();
} else {
echo "Failed to open archive.";
}Para extrair apenas ficheiros específicos, passe os seus nomes como segundo argumento:
$zip->extractTo($extractTo, ['readme.txt', 'data/report.csv']);Armadilhas comuns
- Chame sempre
close(). Até que o faça, as adições existem apenas em memória e o ficheiro no disco pode estar vazio. open()não retornafalseem todos os erros. Em caso de falha, retorna um código de erro inteiro (por exemploZipArchive::ER_NOENTquando o ficheiro está em falta). Comparar estritamente com=== trueé a forma segura de detetar sucesso.- Zip Slip. Ao extrair arquivos não confiáveis, uma entrada maliciosa com nome como
../../etc/passwdpode escapar do diretório de destino. Valide ou sanitize os nomes das entradas antes de extrair ficheiros que não foram criados por si. - Memória.
getFromName()carrega toda a entrada para a memória; para entradas grandes, prefiraextractTo()ou transmita comgetStream().
Tópicos relacionados
- PHP file_put_contents() — grave os dados lidos de um arquivo de volta no disco.
- PHP file_get_contents() — leia ficheiros inteiros, por exemplo, antes de os adicionar a um zip.
- PHP file_exists() — confirme que um ficheiro está presente antes de chamar
addFile(). - PHP fread() — leia o conteúdo de ficheiros em fragmentos.
Conclusão
A classe ZipArchive é a forma moderna e fiável de trabalhar com arquivos ZIP em PHP. É possível criar arquivos com addFile() e addFromString(), inspecioná-los com numFiles e statIndex(), carregar entradas individuais para a memória com getFromName() e descompactá-los com extractTo() — lembrando sempre de chamar close() e de proteger contra nomes de entradas não confiáveis ao extrair.