ZipArchive et ODT bug
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 !
Pas de commentaire pour le moment
Trackbacks
Pour ajouter un trackback (retrolien) sur ce ticket, utilisez cette adresse: http://www.metal3d.org/index.php/trackback/default/tb?id=blog%2F231

