W3docs

PHP Zip

Los archivos ZIP comprimen uno o más ficheros para reducir su tamaño y facilitar su transferencia. Aprende a crearlos, leerlos y extraerlos en PHP.

Un archivo ZIP es un fichero que agrupa uno o más archivos y los comprime mediante el algoritmo zip. Los archivos ZIP se utilizan ampliamente para reducir el tamaño de las descargas, agrupar ficheros relacionados en una única unidad distribuible y hacer copias de seguridad. Normalmente tienen la extensión de archivo .zip.

Este capítulo muestra cómo crear, leer y extraer archivos ZIP en PHP moderno usando la clase integrada ZipArchive, con ejemplos ejecutables y los errores más comunes que conviene conocer.

La clase ZipArchive

Las antiguas funciones procedurales zip_* fueron declaradas obsoletas en PHP 7.4 y eliminadas en PHP 8.0. El código moderno debe usar la clase orientada a objetos ZipArchive, que forma parte de la extensión zip incluida (activa ext-zip si aún no lo está — compruébalo con extension_loaded('zip')).

Los métodos que usarás con más frecuencia son:

MétodoPropósito
open($filename, $flags)Abre un archivo para leer o escribir. Devuelve true o un código de error.
addFile($path, $entryName)Añade un fichero del disco al archivo.
addFromString($entryName, $contents)Añade una entrada desde una cadena en memoria.
addEmptyDir($dirName)Añade una entrada de directorio vacío.
extractTo($directory, $entries)Extrae todas las entradas (o un subconjunto elegido) en un directorio.
getFromName($entryName)Lee una entrada en una cadena sin tocar el disco.
statIndex($i) / numFilesInspecciona entradas y las cuenta.
getStatusString()Devuelve un mensaje de estado legible para el manejo de errores.
close()Escribe los cambios pendientes y cierra el manejador.

Importante: Los cambios realizados con addFile() o addFromString() solo se escriben en disco cuando se llama a close(). Olvidar close() produce un archivo vacío o corrupto.

Crear un archivo ZIP

Pasa el indicador ZipArchive::CREATE para crear un nuevo archivo, y añade ZipArchive::OVERWRITE para comenzar desde cero si ya existe un fichero con ese nombre:

$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";
}

Suponiendo que report.csv existe, esto imprime:

Adding 3 entries to documents.zip.
Archive saved.

Lee numFiles antes de close() — una vez cerrado el manejador, el objeto ya no refleja el recuento de entradas.

Leer entradas sin extraer

Puedes inspeccionar el contenido de un archivo, o cargar una sola entrada en memoria, sin desempaquetar todo en 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();
}

Salida:

readme.txt (16 bytes)
data/report.csv (20 bytes)
logs/ (0 bytes)
---
Hello from PHP!

Extraer un archivo al disco

extractTo() desempaqueta el archivo. Comprueba siempre el valor de retorno e informa de los fallos con 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 extraer solo ficheros específicos, pasa sus nombres como segundo argumento:

$zip->extractTo($extractTo, ['readme.txt', 'data/report.csv']);

Errores comunes

  • Llama siempre a close(). Hasta que lo hagas, las adiciones solo existen en memoria y el fichero en disco puede estar vacío.
  • open() no devuelve false en todos los errores. En caso de fallo devuelve un código de error entero (por ejemplo ZipArchive::ER_NOENT cuando el fichero no existe). Comparar estrictamente con === true es la forma segura de detectar el éxito.
  • Zip Slip. Al extraer archivos no confiables, una entrada maliciosa con nombre como ../../etc/passwd puede escapar del directorio de destino. Valida o sanea los nombres de entrada antes de extraer ficheros que no hayas creado tú mismo.
  • Memoria. getFromName() carga toda la entrada en memoria; para entradas grandes, prefiere extractTo() o transmite con getStream().

Temas relacionados

Conclusión

La clase ZipArchive es la forma moderna y fiable de trabajar con archivos ZIP en PHP. Puedes crear archivos con addFile() y addFromString(), inspeccionarlos con numFiles y statIndex(), cargar entradas individuales en memoria con getFromName(), y desempaquetarlos con extractTo() — recordando siempre llamar a close() y protegerte contra nombres de entrada no confiables al extraer.

Práctica

Práctica
¿Cuál de las siguientes afirmaciones es/son verdaderas sobre la extensión ZIP en PHP?
¿Cuál de las siguientes afirmaciones es/son verdaderas sobre la extensión ZIP en PHP?
Was this page helpful?