Si vous êtes capable d'extraire dans un fichier toute les informations qui ont changé, vous trouverez ci-dessous un script qui peut vous faire gagner du temps pour re-générer le document mis à jour.

Principe du script

Les fichiers OpenXML (*.pptx, *.docx, *.xlsx) sont en fait des fichiers zippés qui contient des répertoires et des fichiers xml (entre autres). Après avoir extrait le zip (avec l'extension PHP Zip, qui est donc nécessaire !), il est donc facile de remplacer des mots ou "variables" dans les fichiers xml (texte). Le script ci-dessous utilise la fonction preg_match_all() pour trouver les occurences de l'expression '${*}' ou '*' est le nom d'une variable. Si vous mettez donc '${mavariable}' n'importe où dans votre document (avec votre bien vieux microsoft Office), elle sera trouvée et remplacée par la valeur que vous aurez indiquée pour cette variable dans un fichier de configuration (ini).

Le principe est simple, limité, mais fonctionnel. Abusez-en !

Création du fichier template

Commencez par créer / modifier votre fichier pptx (ou docx ou xlsx) en ajoutant des '${mavariable}' à la place des mots, phrases, ou valeurs que voulez modifier dynamiquement, appellons ce fichier 'template.pptx' (par exemple)

Création du fichier de propriétés

Créer un fichier 'properties.ini' (par exemple) qui contient la liste de toute les variables que vou voulez utiliser dans votre document :

doc.title = Le titre du document ici
doc.author.name = Mon nom ici
...

Création du script de génération

Copier le script ci-dessous dans le fichier 'openxml.php'

<?php
/**
 * Content of the openxml.php file
 *
 * @usage php openxml.php <iniFile> <templateFile> <finalFile>
 *
 * @copyright none.
 */

/**
 * Replaces all ${variable.name} by the value of 'variable.name' of your configuration file in the specified file
 *
 * @param string $file the source file
 * @param array  $vars the list of variable to replace
 *
 * @return void
 */
function parseFile($file,$vars)
{
    $content = file_get_contents($file);

    $matches = null;
    if(preg_match_all('/\$\{([^\}]+)\}/',$content,$matches)>0) {
        for($i=0;$i<count($matches[0]);$i++){
            $v = '$vars';
            $vl='';
            $found = trim($matches[1][$i]);
            foreach(explode('.',$found) as $t){
                $v.= '["'.strip_tags($t).'"]';
                $vl.=($vl?'.':'').strip_tags($t);
            }
            eval('$value = isset('.$v.') ? '.$v.' : (isset($vars["'.$vl.'"]) ? $vars["'.$vl.'"] : "[Variable not found: \'$vl\']");');
            $content = str_replace('${'.$matches[1][$i].'}',$value,$content);
        }
    }

    file_put_contents($file,$content);
}

/**
 * Lists all files in specified location
 *
 * @param string $location the location to list files in
 * 
 * @return array
 */
function listFiles($location,$root=null)
{
    if(null==$root) {
        $root = $location;
    }
    $files = array();
    if (is_dir($location)){
        $dh = opendir($location);
        while(false !== ($file = readdir($dh))){
            if ('.'===$file || '..' === $file) continue;
            $files = array_merge($files,listFiles($location.'/'.$file,$root));
        }
        closedir($dh);
    }
        $files[] = $location;
    sort($files);
    if($root === $location) {
        array_shift($files);
    }
    return $files;
}

/**
 * Deletes all files in specified locaton
 *
 * @param string $location
 *
 * @return void
 */
function delete($location)
{
    if (is_dir($location)){
        $dh = opendir($location);
        while(false !== ($file = readdir($dh))){
            if ('.'===$file || '..' === $file) continue;
            delete($location.'/'.$file);
        }
        closedir($dh);
    }
    @unlink($location);
}

$iniFile = $_SERVER['argv'][1];
$templateFile =  $_SERVER['argv'][2];
$finalFile = $_SERVER['argv'][3];

// 1. imports ini file

$vars = parse_ini_file($iniFile);

// 2. unzips the file into a temporary directory

$tempDir = sys_get_temp_dir().'/openxml';
if(!file_exists($tempDir)){
    mkdir($tempDir,0777,true);
}

$zip = new ZipArchive();
if (true !== $zip->open($templateFile)) {
    throw new RuntimeException("Unable to read zip file '$templateFile'");
}
$zip->extractTo($tempDir);
$zip->close();

// 3. parses extracted files to replace variables

$files = listFiles($tempDir);

foreach($files as $file) {
    if(substr($file,strlen($file)-4)!=='.xml') continue;
    parseFile($file,$vars);
}

// 4. Re-zip all files into the final pptx file

$zip = new ZipArchive();

if(file_exists($finalFile)){
    unlink($finalFile);
}
if ($zip->open($finalFile, ZIPARCHIVE::CREATE)!==TRUE) {
    throw new RuntimeException("Unable to create zip archive '$finalFile'");
}

foreach($files as $file) {
    $fileName = str_replace('\\','/',substr($file,strlen($tempDir)+1));
    $zip->addFile($file,$fileName);
}
$zip->close();

// 5. Deletes all artifacts

delete($tempDir);

echo "Your openxl document has been generated in '$finalFile' from template '$templateFile' and variables set in configuration file '$iniFile'\n";

Exécutez le script !

$ php openxml.php properties.ini template.pptx document.pptx

Vous devriez avoir un fichier 'document.pptx' créé à partir du fichier template (modèle) et dont les variables ont été remplacé par leur valeur trouvée dans le fichier de configuation.

Pour aller plus loin

Ce script n'est pas parfait, mais il fonctionne très bien pour les cas simples. Sous excel, vous aurez peut être quelques petits soucis car les valeurs remplacées sont considérées comme du texte et pas des chiffres, ce qu posent problèmes ou formule (il y a un palliatif)

Je vous laisse le soin de faire évoluer ce script pour :

  • ajouter des images générées dynamiquement par php
  • réaliser une api objet à intégrer dans vos scripts et applications/sites.

N'hésitez pas à poster vos commentaires surtout si vous trouvez des outils plus complets ailleurs, ou que vous faites évoluer ce script !