Accueil
Rechercher:
sur developpez.com sur les forums
Forums | Tutoriels | F.A.Q's | Participez | Hébergement | Contacts
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi MS-Office SQL & SGBD Oracle  4D  Business Intelligence
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
FORUM PHP FAQ PHP COURS PHP SOURCES PHP LIVRES PHP SCRIPTS PHP OUTILS PHP COMPARATIFS PHP TV Zend Framework

PHP 4 et 5 : l'extension ZIP

Date de publication : 28/04/2007

Par julp (Autres articles)
 

Le format ZIP fait indéniablement partie des standards en matière de compression. Il mérite donc que nous nous y attardions au travers d'un article d'autant plus que son support varie d'une version de PHP à une autre.


1. PHP4
1.1. Installation
1.1.1. Windows
1.1.2. Linux
1.1.2.1. Statique
1.1.2.2. Dynamique
1.2. Utilisation
1.2.1. Lister le contenu d'une archive
1.2.2. Extraire une archive
2. PHP5
2.1. Installation
2.1.1. Windows
2.1.2. Linux/Unix
2.1.2.1. Statique
2.1.2.2. Dynamique
2.2. Aperçu de la classe ZipArchive
2.3. Utilisation de la classe ZipArchive
2.3.1. Créer une archive
2.3.2. Ajouter/Modifier un fichier à une archive
2.3.3. Supprimer un fichier d'une archive
2.3.4. Lister une archive
2.3.5. Obtenir des informations sur un fichier particulier de l'archive
2.3.6. Extraire une archive
2.4. Lecture d'une archive comme d'un flux
3. Conclusion
3.1. Epilogue
3.2. Remerciements


1. PHP4

L'extension Zip pour ces versions de PHP est limitée à l'accès en lecture sur l'archive. Vous ne pouvez en outre que lire le contenu d'une archive et obtenir diverses informations sur les fichiers contenus dans celle-ci ou encore les extraire mais en aucun cas en créer ou modifier une.


1.1. Installation


1.1.1. Windows

Récupérez la version binaire de PHP pour Windows ou uniquement cette extension précise sur le site PECL4WIN. Placez la avec les autres (répertoire souvent nommé extensions) puis chargez l'extension dans le fichier php.ini à l'aide de la ligne suivante :
extension=php_zip.dll
Un redémarrage de votre serveur Apache sera requis afin de prendre en charge immédiatement cette nouvelle extension.


1.1.2. Linux

Réservez l'installation de PHP à partir de ses sources à des besoins particuliers surtout si vous êtes débutants dans ce domaine, privilégiez dans la mesure du possible les paquetages binaires officiels mis à votre disposition par le distributeur de votre système Linux auquel cas il ne devrait vous rester qu'à charger la nouvelle extension en éditant le fichier php.ini (référez vous à la partie intitulée Dynamique ci-dessous pour cela).
Sur une distribution Mandriva avec des médias correctement renseignés, la commande urpmi php4-zip suffira.

La librairie ZZIPlib (et ses fichiers d'en-tête), version minimale 0.10.6, vous sera requise.


1.1.2.1. Statique

L'extension ZIP étant intégrée aux sources de PHP, il nous suffit de manifester notre intérêt en ajoutant l'option --with-zip au script de configuration :
./configure --prefix=/usr/local/php4 ... --with-zip
make
make install

1.1.2.2. Dynamique

Le recours à une compilation dynamique peut se faire après la compilation du coeur de PHP vous évitant de repasser par une recompilation complète de PHP mais permet également de mettre à jour l'un ou l'autre de façon plus ou moins indépendante. Les commandes à saisir sont alors les suivantes :
cd /répertoire/des/sources/de/l/extension
phpize
./configure
make
make install
Il est nécessaire d'indiquer à PHP de charger l'extension ZIP en éditant le fichier php.ini pour y ajouter la ligne suivante :
extension=zip.so
La directive extension_dir du même fichier doit être correcte sous peine d'erreurs car PHP sera alors incapable de trouver la librairie. Si PHP se présente en tant que module du serveur web, il vous sera nécessaire de redémarrer ce dernier afin de disposer du support de l'extension ZIP.


1.2. Utilisation


1.2.1. Lister le contenu d'une archive

Obtenir la liste des entrées d'une archive ZIP se résume dans un premier temps à l'ouvrir puis à en parcourir les entrées et pour terminer à la fermer.

L'ouverture se fait à l'aide de la fonction zip_open qui n'a pour seul paramètre le nom de l'archive et qui vous renverra FALSE en cas d'erreur ou alors une ressource qui vous permettra de l'explorer par la suite.

Le parcours des entrées de l'archive se fait une à une par le biais de la fonction zip_read, qui fournit pour chaque entrée une ressource décrivant celle-ci ou FALSE si la fin de l'archive a été atteinte. Il est possible d'exploiter cette ressource afin d'obtenir diverses informations sur le fichier compressé à raison d'une fonction par critère :
  • zip_entry_name() : le nom du fichier
  • zip_entry_compressedsize() : la taille du fichier après compression
  • zip_entry_compressionmethod() : la méthode de compression utilisée
  • zip_entry_filesize() : taille non compressée du fichier
N'omettons pas après cela de refermer l'archive avec la fonction zip_close.

Puisqu'un exemple vaut toujours mieux qu'un long discours :
<style type="text/css">
<!--
table.zip_details {
    border: 3px double black;
    border-collapse: collapse;
}

table.zip_details td,
table.zip_details th {
    border: 1px solid black;
}

table.zip_fichiers td,
table.zip_fichiers th {
    border: 0px none;
}
-->
</style>

<?php
function formater_taille($taille) {
    $unites = array('o', 'ko', 'Mo', 'Go');

    for ($u = count($unites); $u >= 0; $u--) {
        if (isset($unites[$u]) && $taille >= 1024 * pow(1024, $u - 1)) {
            $taille = $taille / pow(1024, $u);
            $unite = $unites[$u];
            break;
        }
    }

    if ($u > 0) {
        return number_format($taille, 2, ',', ' ') . ' ' . $unite;
    } else {
        return $taille . ' ' . $unite;
    }
}

function afficher_zip($archive) {
    if (($zip = zip_open($archive)) === FALSE) {
        return FALSE;
    }
    echo '<table class="zip_details">';
    echo '<tr><th colspan="2">' . $archive . '</th></tr>';
    echo '<tr><td>Taille :</td><td>' . formater_taille(filesize('sources.zip')) . '</td></tr>';
    $nbEntrees = 0;
    echo '<tr>
        <td>Fichiers archivés :</td>
        <td><table class="zip_fichiers">
        <tr>
            <th>Nom</th>
            <th>Taille compressée</th>
            <th>Taille non compressée</th>
        </tr>';
    while ($entree = zip_read($zip)) {
        echo '<tr>
            <td>' . zip_entry_name($entree) . '</td>
            <td align="center">' . formater_taille(zip_entry_compressedsize($entree)) . '</td>
            <td align="center">' . formater_taille(zip_entry_filesize($entree)) . '</td>
        </tr>';
        $nbEntrees++;
    }
    echo '</table></td></tr>';
    echo '<tr><td>Nombre de fichiers archivés :</td><td>' . $nbEntrees . '</td></tr>';
    echo '</table>';
    zip_close($zip);
    return TRUE;
}

# Exemple d'utilisation
afficher_zip('sources.zip');
?>
C'est la fonction afficher_zip qu'il faut regarder et comprendre. Le restant, la CSS et la fonction formater_taille, ne sont que secondaires et permettent d'améliorer quelque peu l'affichage généré par afficher_zip().


1.2.2. Extraire une archive

Cette opération implique peu de changements et de nouvelles fonctions par rapport au listing d'une archive. En effet, l'archive est parcourue de la même manière et les seules nouvelles fonctions que nous allons découvrir ont pour but d'accéder au contenu d'une entrée et de la lire :
  • zip_entry_open(zip, entrée) : ouvrir en lecture le fichier correspondant à la ressource entrée (obtenue par la fonction zip_read) de l'archive représentée par la ressource zip (résultat de la fonction zip_open)
  • zip_entry_read(entrée, longueur) : lire le contenu de l'entrée (ressource retournée par zip_read) à hauteur de longueur octets (valeur par défaut 1024).
  • zip_entry_close(entrée) : libère les ressources pour la lecture du fichier correspondant à la ressource entrée
Ces fonctions bien que portant des noms différents, se rapprochent fortement de fopen, fread couplée à filesize (afin de récupérer le contenu du fichier en une fois) et fclose.
function mkdir_recursif($dir) {
    $parties = preg_split('#/|' . preg_quote(DIRECTORY_SEPARATOR) . '#', $dir, -1, PREG_SPLIT_NO_EMPTY);
    $base = '';
    foreach ($parties as $p) {
        if (!file_exists($base . $p)) {
            mkdir($base . $p);
        }
        $base .= $p . DIRECTORY_SEPARATOR;
    }
}

function extractTo($archive, $destination, $ecrase = FALSE, $fichiers = NULL) {
    if (($zip = zip_open($archive)) === FALSE) {
        die(var_dump($zip));
        return FALSE;
    }
    if (!file_exists($destination)) {
        mkdir_recursif($destination);
    }
    while ($entree = zip_read($zip)) {
        $fichier = zip_entry_name($entree);
        if (is_array($fichiers) && !in_array($fichier, $fichiers)) {
            continue;
        }
        if (zip_entry_open($zip, $entree)) {
            $contenu = zip_entry_read($entree, zip_entry_filesize($entree));
            zip_entry_close($entree);
            if ($ecrase || !file_exists($destination . DIRECTORY_SEPARATOR . $fichier)) {
                if (strpos($fichier, '/') !== FALSE) {
                    mkdir_recursif($destination . DIRECTORY_SEPARATOR . dirname($fichier));
                }
                $fp = fopen($destination . DIRECTORY_SEPARATOR . $fichier, 'w');
                fwrite($fp, $contenu);
                fclose($fp);
            }
        } else {
            zip_close($zip);
            return FALSE;
        }
    }
    zip_close($zip);
    return TRUE;
}

# Exemples d'utilisation
# Extraction de l'ensemble des fichiers qui composent l'archive
extractTo('sources.zip', '/home/julp/www/zip');
# Extraction limitée aux fichiers fichier1 et fichier2. S'ils existent déjà ils seront écrasés.
extractTo('mon_archive.zip', '/home/julp', TRUE, array('fichier1', 'fichier2'));

2. PHP5


2.1. Installation


2.1.1. Windows

Téléchargez la version binaire pour Windows ou allez sur la page PECL4WIN, puis placez cette extension avec les autres (répertoire ext en général) et enfin activez l'extension dans le fichier php.ini :
extension=php_zip.dll
Redémarrez finalement votre serveur Apache pour à présent profiter du support de l'extension ZIP.


2.1.2. Linux/Unix

L'étape de compilation ne concerne uniquement une installation de PHP à partir de ses sources qui s'adresse aux initiés pour répondre à des besoins particuliers (application de patchs par exemple). Dans les autres cas, il est recommandé d'employer les paquetages fournis par votre système ou distribution auquel cas vous devriez simplement avoir besoin d'activer l'extension via votre fichier php.ini (voir ci-dessous dans la partie "Dynamique").
Par exemple l'installation sur la distribution Mandriva se fait avec la commande urpmi php5-zip si vos médias sont convenablement configurés.

Vous devez disposer de la librairie zlib (librairies et fichiers d'en-têtes) avant d'aller plus loin.


2.1.2.1. Statique

Ceux qui utilisent une version antérieure à 5.2.0 doivent, au préalable, effectuer quelques opérations supplémentaires car l'extension Zip n'était pas fournie avec PHP :
  1. Télécharger les sources de cette extension sur le site PECL.

  2. Les extraire dans le répertoire ext des sources de PHP :
    tar xzf zip-<version> -C /usr/local/src/php-5.X.Y/ext
    ln -s /usr/local/src/php-5.X.Y/ext/zip-<version> /usr/local/src/php-5.X.Y/ext/zip
    
  3. Les intégrer pour la compilation :
    cd /usr/local/src/php-5.X.Y
    ./buildconf --force
    
Démarrez la compilation en ajoutant l'option --enable-zip lors de la configuration :
./configure --prefix=/usr/local/php5 ... --enable-zip
make
make install

2.1.2.2. Dynamique

L'avantage de cette méthode c'est que vous n'avez pas besoin de recompiler PHP. Vous compilez en effet, sous forme dynamique, donc autonome, l'extension Zip. Nous aurons besoin des sources de celle-ci si elles ne sont pas déjà inclues à PHP. Si tel n'est pas le cas, je vous invite à les télécharger du site PECL puis à les décompresser. Vous voilà prêts à les compiler :
cd /répertoire/des/sources/de/l/extension
phpize
./configure
make
make install
Pour terminer, il faut indiquer à PHP de charger cette extension, c'est pourquoi nous rajouterons la ligne suivante à notre fichier de configuration php.ini :
extension=zip.so
Profitez-en pour vérifier que les chemins vers ces extensions sont correctement renseignés à la directive extension_dir. Redémarrez Apache pour prendre cette nouvelle extension en compte.


2.2. Aperçu de la classe ZipArchive

En PHP 5, la manipulation d'archives est déléguée à une classe nommée ZipArchive dont vous trouverez ci-dessous sa structure documentée :
class ZipArchive
{
    /* Constantes */
    // Modes d'ouverture
    const CREATE;            // Crée l'archive si celle-ci n'existe pas
    const EXCL;              // Echoue si l'archive existe déjà
    const CHECKCONS;         // Effectue des contrôles supplémentaires
    const OVERWRITE;         // Remplace l'archive existante
    // Options
    const FL_NOCASE;         // La casse est ignorée sur l'entrée du nom
    const FL_NODIR;          // La partie répertoire est ignorée
    const FL_COMPRESSED;     // Lit les données compressées
    const FL_UNCHANGED;      // Utilise les données originales de l'archive (ignore les changements)
    // Modes de compression
    const CM_DEFAULT;        // Compression par défaut (offre le meilleur taux)
    const CM_STORE;          // Stockage uniquement (sans compression)
    const CM_SHRINK;         // Une variante de l'algorithme LZW
    const CM_REDUCE_1;       // Taux de compression réduit (niveau 1)
    const CM_REDUCE_2;       // Taux de compression réduit (niveau 2)
    const CM_REDUCE_3;       // Taux de compression réduit (niveau 3)
    const CM_REDUCE_4;       // Taux de compression réduit (niveau 4)
    const CM_IMPLODE;        // Réunit les séquences identiques avant compression
    const CM_DEFLATE;        // Utilisation de l'algorithme Deflate (RFC 1951)
    const CM_DEFLATE64;      // Variante de l'algorithme Deflate travaillant sur des blocs de 64 ko au lieu de 32
    const CM_PKWARE_IMPLODE; // Méthode officielle
    const CM_BZIP2;          // Compression utilisant l'algorithme bzip2
    // Erreurs
    const ER_OK;             // Aucune erreur
    const ER_MULTIDISK;      // Archives multi-disques non supportée
    const ER_RENAME;         // Erreur lors de la modification du nom du fichier temporaire
    const ER_CLOSE;          // Erreur lors de la fermeture de l'archive
    const ER_SEEK;           // Erreur de déplacement
    const ER_READ;           // Erreur de lecture
    const ER_WRITE;          // Erreur d'écriture
    const ER_CRC;            // Erreur dans la somme CRC
    const ER_ZIPCLOSED;      // L'archive ZIP est fermée
    const ER_NOENT;          // Fichier inexistant
    const ER_EXISTS;         // Le fichier est déjà présent
    const ER_OPEN;           // Ne peut ouvrir le fichier
    const ER_TMPOPEN;        // Erreur lors de la création du fichier temporaire
    const ER_ZLIB;           // Erreur interne liée à la librairie Zlib
    const ER_MEMORY;         // Erreur d'allocation dynamique de mémoire
    const ER_CHANGED;        // L'entrée a été modifiée
    const ER_COMPNOTSUPP;    // Méthode de compression non supportée
    const ER_EOF;            // Fin de fichier rencontrée prématurément
    const ER_INVAL;          // Argument invalide
    const ER_NOZIP;          // Ne s'agit pas d'une archive au format ZIP
    const ER_INTERNAL;       // Erreur interne
    const ER_INCONS;         // Archive inconsistante
    const ER_REMOVE;         // Ne peut supprimer un fichier
    const ER_DELETED;        // Entrée supprimée

    /* Propriétés */
    public $status;          // Numéro de l'erreur de la librairie libzip
    public $statusSys;       // Numéro de l'erreur système (errno) ou de la librairie zlib
    public $numFiles;        // Nombre de fichiers dans l'archive
    public $filename;        // Nom et chemin de l'archive
    public $comment;         // Commentaire associé à l'archive

    /* Méthodes */
    /**
     * Ouvrir une archive en vue de sa création ou manipulation
     * @param fichier  : le nom de l'archive à créer ou manipuler
     * @param drapeaux : mode d'ouverture de l'archive (CREATE, EXCL, CHECKCONS ou OVERWRITE)
     * @return TRUE ou un code décrivant l'erreur (constantes de classe commençant par ER_)
     **/
    public function open(chaîne fichier [, entier drapeaux]);

    /**
     * Fermer l'archive
     * @return un booléen, TRUE en cas de succès sinon FALSE
     **/
    public function close();

    /**
     * Ajouter un répertoire vide à l'archive
     * @param répertoire : le nom du répertoire à ajouter
     * @return un booléen, FALSE en cas d'échec et TRUE dans le cas contraire
     **/
    public function addEmptyDir(chaîne répertoire);

    /**
     * Ajouter un fichier virtuel (ie qui n'a pas d'existence physique) à l'archive
     * @param nom_local : nom sous lequel figurera le fichier au sein de l'archive
     * @param contenu   : le contenu de ce fichier
     * @return un booléen, FALSE si une erreur survient et TRUE si tout se passe normalement
     **/
    public function addFromString(chaîne nom_local, chaîne contenu);

    /**
     * Ajouter un fichier (physique) à l'archive
     * @param fichier   : le chemin complet vers le fichier à ajouter
     * @param nom_local : son nom dans l'archive s'il doit être différent
     * @param début     : position dans le fichier source à partir de laquelle son contenu doit être inséré
     * @param longueur  : nombre de caractères maximal à inclure
     * @return un booléen, FALSE en cas d'erreur et TRUE si tout va bien
     **/
    public function addFile(chaîne fichier [, chaîne nom_local [, entier début [, entier longueur]]]);

    /**
     * Renommer le fichier indiqué à partir de son indice
     * @param indice      : indice du fichier