PHP Zip
Gli archivi ZIP comprimono i file con l'algoritmo zip, riducendo le dimensioni e semplificando il trasferimento dei dati in PHP.
Un archivio ZIP è un file che raggruppa uno o più file e li comprime utilizzando l'algoritmo zip. Gli archivi sono ampiamente usati per ridurre le dimensioni dei download, raggruppare file correlati in un'unica unità distribuibile e creare backup dei dati. Un archivio zip porta normalmente l'estensione .zip.
Questo capitolo mostra come creare, leggere ed estrarre archivi ZIP in PHP moderno usando la classe integrata ZipArchive, con esempi eseguibili e le insidie più comuni da conoscere.
La classe ZipArchive
Le funzioni procedurali legacy zip_* sono state deprecate in PHP 7.4 e rimosse in PHP 8.0. Il codice moderno dovrebbe usare la classe orientata agli oggetti ZipArchive, che fa parte dell'estensione zip inclusa nel pacchetto (abilitare ext-zip se non è già attiva — verificare con extension_loaded('zip')).
I metodi più utilizzati sono:
| Metodo | Scopo |
|---|---|
open($filename, $flags) | Apre un archivio per la lettura o la scrittura. Restituisce true o un codice di errore. |
addFile($path, $entryName) | Aggiunge un file dal disco all'archivio. |
addFromString($entryName, $contents) | Aggiunge una voce da una stringa in memoria. |
addEmptyDir($dirName) | Aggiunge una voce di directory vuota. |
extractTo($directory, $entries) | Estrae tutte le voci (o un sottoinsieme scelto) in una directory. |
getFromName($entryName) | Legge una voce in una stringa senza toccare il disco. |
statIndex($i) / numFiles | Ispeziona le voci e le conta. |
getStatusString() | Restituisce un messaggio di stato leggibile per la gestione degli errori. |
close() | Scrive le modifiche in sospeso e chiude l'handle. |
Importante: Le modifiche apportate con
addFile()oaddFromString()vengono scritte su disco solo quando si chiamaclose(). Dimenticareclose()produce un archivio vuoto o corrotto.
Creazione di un archivio ZIP
Passare il flag ZipArchive::CREATE per creare un nuovo archivio, e aggiungere ZipArchive::OVERWRITE per ricominciare da capo se esiste già un file con quel 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";
}Assumendo che report.csv esista, questo stampa:
Adding 3 entries to documents.zip.
Archive saved.Leggere numFiles prima di close() — dopo la chiusura dell'handle l'oggetto non riflette più il conteggio delle voci.
Lettura delle voci senza estrazione
È possibile ispezionare il contenuto di un archivio, o estrarre una singola voce in memoria, senza decomprimere tutto su 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();
}Output:
readme.txt (16 bytes)
data/report.csv (20 bytes)
logs/ (0 bytes)
---
Hello from PHP!Estrazione di un archivio su disco
extractTo() decomprime l'archivio. Verificare sempre il valore restituito e segnalare gli errori 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.";
}Per estrarre solo file specifici, passare i loro nomi come secondo argomento:
$zip->extractTo($extractTo, ['readme.txt', 'data/report.csv']);Insidie comuni
- Chiamare sempre
close(). Finché non lo si fa, le aggiunte esistono solo in memoria e il file su disco potrebbe essere vuoto. open()non restituiscefalseper ogni errore. In caso di errore restituisce un codice di errore intero (ad esempioZipArchive::ER_NOENTquando il file è mancante). Confrontare rigorosamente con=== trueè il modo sicuro per rilevare il successo.- Zip Slip. Quando si estraggono archivi non attendibili, una voce dannosa denominata come
../../etc/passwdpuò sfuggire alla directory di destinazione. Convalidare o sanificare i nomi delle voci prima di estrarre file non creati dall'utente. - Memoria.
getFromName()carica l'intera voce in memoria; per voci di grandi dimensioni preferireextractTo()o lo streaming congetStream().
Argomenti correlati
- PHP file_put_contents() — scrivere i dati letti da un archivio su disco.
- PHP file_get_contents() — leggere interi file, ad esempio prima di aggiungerli a un zip.
- PHP file_exists() — verificare che un file sia presente prima di chiamare
addFile(). - PHP fread() — leggere il contenuto di file a blocchi.
Conclusione
La classe ZipArchive è il modo moderno e affidabile per lavorare con gli archivi ZIP in PHP. È possibile creare archivi con addFile() e addFromString(), ispezionarli con numFiles e statIndex(), estrarre singole voci in memoria con getFromName() e decomprimerli con extractTo() — ricordando sempre di chiamare close() e di proteggersi dai nomi di voci non attendibili durante l'estrazione.