ZipArchive et ODT bug

07/10/2009

Je viens enfin de rendre mon article à PHP Solutions magazine qui devrait paraître en novembre sur l’édition de document OpenOffice via PHP. Durant la rédaction, je remarque que plus aucun de mes anciens exemples ne fonctionnent, la faute à ZipArchive qui a un bug depuis quelques versions. Vous trouverez ici l’explication http://bugs.php.net/bug.php?id=48763.

J’ai donc une solution: dériver ZipArchive pour faire ce dont j’ai besoin: décompresser tout, virer l’archive, recompresser… c’est plus lent, mais ça marche.

Voici donc pour vous mes chers lecteurs, la classe ZipArchive_Proxy, présentée dans le magazine PHP Solutions du mois prochain, qui permet de recréer une archive correcte.

` <?php /** -Zip system to use for ODT files… because a bug exists in ZipArchive for PHP 5.2 * -@author Patrice Ferlet metal3d@copix.org

-@license GNU/GPLv3

class ZipArchive_Proxy{

//temporary directory
private $temp='';  

//file to use  
private $file = '';

/**

-Create/open zip instance *

-@param string $filename

public function __construct($file = null) {
    if(!is_null($file)) $this->open($file);
}

/**

-Init zip handler -@param string $filename

-@return class instance

public function open($file){

    $this->file = $file;

    //prepare temporary directory
    if(is_dir('/dev/shm')) {
        $this->temp = '/dev/shm/'.uniqid('tmp_zip_');
        $this->cleanDir($this->temp);
    }
    else {
        $this->temp = sys_get_temp_dir().'/'.uniqid('tmp_zip_');
        $this->cleanDir($this->temp);
    }    
    mkdir($this->temp);       

    //unzip archive in temporary directory
    $zip = new ZipArchive();
    $zip->open($file);
    $zip->extractTo($this->temp);
    $zip->close();
    return $this;
}

/**

-Return content by filename from archive * -@param string $filename

-@return string file_content or null

public function getFromName ($file){
    $curpath = getcwd(); 
    chdir($this->temp);

    if(file_exists($file)) $toReturn = file_get_contents($file);
    else $toReturn = false;

    chdir($curpath);
    return $toReturn;
}


/**

-Insert content in file in archive * -@param string $filename -@param mixed content

-@return class instance

public function addFromString ($file, $content){
    echo "current path: ". getcwd();
    $curpath = getcwd(); 
    chdir($this->temp);        
    file_put_contents($file, $content);        
    chdir($curpath);
}


/**

-Close archive

-@return bool $ok

public function close(){
    unlink($this->file);        
    $zip = new ZipArchive(); 
    $zip->open($this->file, ZIPARCHIVE::CREATE); 
    $curpath = getcwd(); 
    chdir($this->temp);
    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.')); 
    foreach ($iterator as $key=>$value) { 
       $key = preg_replace('/^\.\//','',$key); 
       echo "Append: $key::$key\n"; 
       $zip->addFile($key,$key ) or die ("ERROR: Could not add file: $key"); 
    }
    $zip->close();
    chdir($curpath);
    $this->cleanDir($this->temp);        
    return true;
}

/**

-Recursive method to clean directory

private function cleanDir($dir){ 
    if(!is_dir($dir)) return; 
    $handle = opendir($dir); 
    while (($entry = readdir($handle)) !== FALSE) { 
        if( $entry == '.' || $entry == '..') continue; //ne pas compresser les répertoires parents 
        $path = $dir.'/'.$entry; 
        if (is_dir($path)) $this->cleanDir($path); //supprime le répertoire trouvé 
        else unlink ($path); //supprime le fichier 
    } 
    closedir($handle); 
    //et en fin on supprime le répertoire parent 
    rmdir($dir); 
}

} `

Ainsi, voici comment je m’y prend pour modifier le content.xml contenu dans un odt:

//ouvre l'archive odt
$zip = new ZipArchive_Proxy('test.odt');

//récupère le contenu de content.xml et modifie le contenu
$content = $zip->getFromName('content.xml');
$content = str_replace ('{titre}', 'Le titre du document', $content);

//on remet content.xml en place
$zip->addFromString('content.xml',$content);

//et on ferme
$zip->close();

Pour information, j’utilise si possible /dev/shm afin de taper dans la mémoire plutôt que le disque dur, si cela est impossible alors j’utilise le répertoire temporaire proposé par le système d’exploitation.

En espérant que cela vous rende service.

PS: L’article en question qui paraitra dans PHP Solutions parle évidemment de odtPHP, la classe incontournable pour éditer du ODT en PHP. Je vous donne rendez-vous au prochain numéro !

Ça peut vous intéresser aussi


OpenOffice en mode serveur

Il est parfois compliqué de créer des documents dignes de ...


PHP optimisations et astuces

On vient d’en discuter avec Guillaume, et on est ...


PHP 5.3 et les fonctions anonymes

Je les utilise depuis des lustres en Javascript et Java,...


PHP les grands nombres et la notation scientifique

PHP gère les grands nombres sans trop de souci, malheureusement ...

Merci de m'aider à financer mes services

Si vous avez apprécié cet article, je vous serai reconnaissant de m'aider à me payer une petite bière :)

Si vous voulez en savoir plus sur l'utilisation de flattr sur mon blog, lisez cette page: Ayez pitié de moi

Commentaires

Ajouter un commentaire

Ajouter un commentaire

(*) Votre e-mail ne sera ni revendu, ni rendu public, ni utilisé pour vous proposer des mails commerciaux. Il n'est utilisé que pour vous contacter en cas de souci avec le contenu du commentaire, ou pour vous prévenir d'un nouveau commentaire si vous avez coché la case prévue à cet effet.