W3docs

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étodoFinalidade
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) / numFilesInspeciona 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() ou addFromString() só são gravadas no disco quando se chama close(). Esquecer close() 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 retorna false em todos os erros. Em caso de falha, retorna um código de erro inteiro (por exemplo ZipArchive::ER_NOENT quando 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/passwd pode 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, prefira extractTo() ou transmita com getStream().

Tópicos relacionados

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.

Prática

Prática
Qual(is) das seguintes afirmações é/são verdadeira(s) sobre a extensão ZIP em PHP?
Qual(is) das seguintes afirmações é/são verdadeira(s) sobre a extensão ZIP em PHP?
Was this page helpful?