W3docs

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:

MetodoScopo
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) / numFilesIspeziona 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() o addFromString() vengono scritte su disco solo quando si chiama close(). Dimenticare close() 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 restituisce false per ogni errore. In caso di errore restituisce un codice di errore intero (ad esempio ZipArchive::ER_NOENT quando 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/passwd può 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 preferire extractTo() o lo streaming con getStream().

Argomenti correlati

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.

Esercitazione

Pratica
Quali delle seguenti affermazioni sono vere riguardo all'estensione ZIP in PHP?
Quali delle seguenti affermazioni sono vere riguardo all'estensione ZIP in PHP?
Was this page helpful?