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étodo | Propó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) / numFiles | Inspecciona 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()oaddFromString()solo se escriben en disco cuando se llama aclose(). Olvidarclose()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 devuelvefalseen todos los errores. En caso de fallo devuelve un código de error entero (por ejemploZipArchive::ER_NOENTcuando el fichero no existe). Comparar estrictamente con=== truees la forma segura de detectar el éxito.- Zip Slip. Al extraer archivos no confiables, una entrada maliciosa con nombre como
../../etc/passwdpuede 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, prefiereextractTo()o transmite congetStream().
Temas relacionados
- PHP file_put_contents() — escribe los datos leídos de un archivo de vuelta al disco.
- PHP file_get_contents() — lee ficheros completos, por ejemplo antes de añadirlos a un zip.
- PHP file_exists() — confirma que un fichero existe antes de llamar a
addFile(). - PHP fread() — lee el contenido de ficheros por fragmentos.
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.