source: spip-zone/_galaxie_/code.spip.net/autodoc/trunk/src/autodoc/Helpers/Generator.php @ 92999

Last change on this file since 92999 was 92999, checked in by marcimat@…, 5 years ago

Pour générer la page d'accueil avec toutes les descriptions des plugins documentés,
il faut récupérer leurs infos (titre, descriptif, etc), même si leur documentation n'a pas
besoin d'être régénérée.

File size: 34.6 KB
Line 
1<?php
2
3/*
4 * Commande d'execution …
5 */
6
7namespace autodoc\Helpers;
8
9use Symfony\Component\Console\Input\InputInterface;
10use Symfony\Component\Console\Input\StringInput;
11use Symfony\Component\Console\Input\ArgvInput;
12use Symfony\Component\Console\Output\OutputInterface;
13use autodoc\Application as Autodoc;
14
15/**
16 * Exécuter l'application…
17 */
18class Generator
19{
20    private $input;
21    private $output;
22    private $autoloader; 
23
24    private $dirs     = array();
25    private $files    = array();
26    private $svn      = array(); // informations sur le dépot SVN
27    private $options  = array('dirs' => array()); // forcer des répertoires en dehors des options de ligne de commande
28    private $commands = array(); // forcer des commandes spécifiques à phpdocumentor
29
30    // les infos de plugins sont enregistrées dedans pour l'index.html de la commande generateFromFile()
31    private $infos_plugins = array();
32
33    /**
34     * Description de la documentation à générer
35     * @var string $description */
36    private $description = "";
37
38    /**
39     * Présentation de la documentation à générer
40     * @var string $presentation */
41    private $presentation = "";
42
43    /**
44     * Constructeur
45     *
46     * @param InputInterface $input
47     * @param OutputInterface $output
48    **/
49    public function __construct(InputInterface $input, OutputInterface $output, $autoloader = null)
50    {
51        $this->input  = $input;
52        $this->output = $output;
53        $this->autoloader = $autoloader;
54
55        $this->setApplicationDirectories();
56    }
57
58
59    /**
60     * Générer la documentation à partir du SVN de SPIP
61     *
62     * Retrouve le nom de la version utilisée et s'en sert de titre.
63     *
64     * @param string $chemin Chemin dans le svn
65    **/
66    public function generateFromSpip($chemin) {
67        $core = "svn://trac.rezo.net/spip/$chemin";
68
69        $prefixe = $this->input->getOption('prefixe');
70        $ok =  $this->createDirectories($prefixe)
71            && $this->getSvnSource($core);
72
73        if (!$ok) return false;
74
75
76        $titre = "";
77        if ($this->input->hasOption('titre')) {
78            $titre = $this->input->getOption('titre');
79        }
80
81        // retrouver la version de SPIP
82        if (!$titre and file_exists($inc_version = $this->dirs['input'] . '/ecrire/inc_version.php')) {
83            $inc_version = file_get_contents($inc_version);
84            if (preg_match('/spip_version_branche = "([^"]+)";/', $inc_version, $res)) {
85                $titre = "SPIP " . $res[1];
86            }
87        }
88
89        if ($titre) {
90            $this->setCommand('title', $titre);
91            $this->setOption('titre', $titre);
92            $this->setOption('description', "Documentation du code PHP de " . $titre);
93        }
94
95        return $this->generateFromDirectory($this->dirs['input'], true);
96    }
97
98    /**
99     * Générer la documentation à partir du SVN de la Zone de SPIP
100     *
101     * @param string $chemin Chemin dans le svn
102    **/
103    public function generateFromZone($chemin) {
104        $zone = "svn://zone.spip.org/spip-zone/$chemin";
105        return $this->generateFromSvn($zone);
106    }
107
108
109    /**
110     * Générer la documentation à partir d'une url SVN
111     *
112     * @param string $source URL SVN
113    **/
114    public function generateFromSvn($source) {
115        $prefixe = $this->input->getOption('prefixe');
116        $ok =  $this->createDirectories($prefixe)
117            && $this->getSvnSource($source)
118            && $this->generateFromDirectory($this->dirs['input'], true);
119        return $ok;
120    }
121
122
123    /**
124     * Générer la documentation à partir d'un répertoire indiqué
125     *
126     * @param string $dir Chemin du répertoire source
127     * @param bool $is_ready
128     *     Indique si les préparatifs (création des répertoires) sont déjà faits
129    **/
130    public function generateFromDirectory($dir, $is_ready = false) {
131        $prefixe = $this->input->getOption('prefixe');
132        $ok = true;
133
134        if (!$is_ready) {
135            # forcer le chemin de la source spécifique
136            $this->options['dirs']['input'] = $dir;
137            $ok = $this->createDirectories($prefixe);
138            if (!$ok) return false;
139        }
140
141        $this->retrouverInfoPaquetXml();
142
143        $ok =  $this->prepareConfigXml()
144            && $this->clearLogs()
145            ;
146
147        if (!$ok) {
148            return false;
149        }
150
151        $this->execute();
152
153        return true;
154    }
155
156
157    /**
158     * Générer la documentation à partir d'un fichier listant les documentations à exécuter
159     *
160     * On duplique le fichier chez soi et on gère une rotation de ce fichier à chaque lancement
161     * afin de supprimer les documentations devenues inutiles (disparues du nouveau fichier).
162     *
163     * @param string $file Chemin du fichier ou URL SVN
164    **/
165    public function generateFromFile($file) {
166
167        $timerStart = microtime(true);
168
169        // Option boussole SPIP => topnav.
170        if ($this->getOption('avec_boussole_spip')) {
171            $this->setOption('topnav', '//boussole.spip.net/?page=spipnav.js&lang=fr');
172        }
173
174        // si un prefixe spécifique de plugin est indiqué,
175        // seul lui sera actualisé dans la liste des plugins.
176        // et on considère que le fichier autodoc.txt n'a pas bougé dans le cas.
177        $prefixe = $this->getOption('prefixe', null);
178
179        // définir les chemins et faire tourner le backup précédent
180        $this->files['autodoc.txt'] = $this->dirs['work'] . '/autodoc.txt';
181        $this->files['autodoc.txt.bak'] = $this->files['autodoc.txt'] . '.bak';
182
183        // à quel endroit sont générées les documentation des fichiers
184        $output_base = $this->input->getOption('sorties');
185        if (!$output_base) {
186            $output_base = $this->dirs['work'] . '/output';
187        }
188        $this->setOption('site', '../index.html');
189
190
191        if ($prefixe) {
192            // autodoc.txt est censé être déjà là
193            $this->output->writeln("* Lecture des informations du fichier (sans l'actualiser)");
194            $presents = $this->parseFile($this->files['autodoc.txt']);
195            // ne conserver que ce prefixe
196            $presents = array_intersect_key($presents, array($prefixe => ''));
197        } else {
198            // autodoc.txt doit être téléchargé ou mis à jour
199            $this->actualiser_liste_source($file);
200
201            $this->output->writeln("* Lecture des informations du fichier");
202            $anciens  = $this->parseFile($this->files['autodoc.txt.bak'], false);
203            $presents = $this->parseFile($this->files['autodoc.txt']);
204
205            if ($anciens) {
206                $absents = array_diff_key($anciens, $presents);
207                $this->supprimer_vieux_plugins($absents);
208            }
209        }
210
211        $nb_erreur = 0;
212        $nb_update = 0;
213
214        foreach ($presents as $_prefixe => $description) {
215            $ok = $this->obtenir_fichiers_a_documenter($_prefixe, $description);
216            if ($ok) {
217                $nb_update++;
218                $this->execute();
219            } elseif ($ok === false) {
220                $nb_erreur++;
221            }
222        }
223
224        $this->output->writeln("\n");
225        if ($nb_erreur) {
226            $this->output->writeln("<comment>$nb_erreur documentation(s) non générée(s) sur " . count($presents) . ".</comment>");
227        } else {
228            $this->output->writeln("<comment>Documentations générées : " . $nb_update . " / " . count($presents) . ".</comment>");
229        }
230
231        // générer la page sommaire (si pas uniquement 1 seul prefixe actualisé
232        if (!$prefixe) {
233            $this->generer_sommaire_documentations($presents, $output_base);
234        }
235
236        $this->output->writeln(sprintf("%-'-81s", ""));
237        $this->output->write(sprintf('%-68.68s .. ', "Temps total"));
238        $this->output->writeln(sprintf('%8.3fs', microtime(true) - $timerStart));
239        $this->output->writeln("Fin");
240    }
241
242    /**
243     * Télécharge ou actualise les fichiers d'une documentation
244     *
245     * Si la mise à jour ne modifie pas les fichiers,
246     * la documentation n'est pas à actualiser (retourne null).
247     *
248     * @param string $prefixe
249     *      Préfixe de cette documentation
250     * @param array $description
251     *      Description. La clé 'source' a l'url SVN
252     * @return bool|null
253     *     - True si OK et mise à jour à faire.
254     *     - null : ok, mais mise à jour inutile
255     *     - False : erreur
256    **/
257    public function obtenir_fichiers_a_documenter($prefixe, $description) {
258        $this->output->writeln("\n");
259        $titre = "Générer la documentation de $prefixe";
260        $this->output->writeln("<comment>$titre</comment>");
261        $this->output->writeln("<comment>" . str_repeat("-", strlen($titre)) . "</comment>");
262
263        if (!$this->createDirectories($prefixe)) {
264            $this->output->writeln("<error>* Documentation ignorée à cause d'une erreur.</error>");
265            return false;
266        }
267
268        $revision_actuelle = $this->recuperer_revision_svn();
269        if (!$this->getSvnSource($description['source'])) {
270            $this->output->writeln("<error>* Documentation ignorée à cause d'une erreur.</error>");
271            return false;
272        }
273
274        // ces options sont créées par retrouverInfoPaquetXml() SI elles n'existent pas.
275        // il faut les nettoyer à chaque passage !
276        $this->setOption('titre', null);
277        $this->setOption('description', null);
278        $this->setOption('presentation', null);
279
280        // Retrouver les informations de paquet.xml
281        if (!$this->retrouverInfoPaquetXml()) {
282            $this->output->writeln("<error>* Documentation ignorée : paquet.xml introuvable.</error>");
283            return false;
284        }
285
286        // pas besoin de mettre à jour, si l'update n'a pas augmenté la révision
287        $revision_nouvelle = $this->recuperer_revision_svn();
288        if ($revision_nouvelle <= $revision_actuelle) {
289            $this->output->writeln("* Documentation déjà à jour.");
290            return null;
291        }
292
293        $ok = $this->prepareConfigXml() && $this->clearLogs();
294
295        if (!$ok) {
296            $this->output->writeln("<error>* Documentation ignorée à cause d'une erreur.</error>");
297            return false;
298        }
299
300        return true;
301    }
302
303
304    /**
305     * Générer le sommaire des documentations réalisés à partir d'un fichier source d'url
306     *
307     * @param array $presents
308     *      Liste des plugins du fichier
309     * @param string $outut_base
310     *      Répertoire où on été enregistré les documentations
311     * @return
312    **/
313    public function generer_sommaire_documentations($presents, $output_base) {
314
315        // générer un sommaire de toutes ces documentations
316        $this->output->writeln("Création du sommaire des documentations");
317
318        // ajouter les dépendances du fichier html
319        if (!is_dir($data = $output_base . '/__data')) {
320            mkdir($data);
321            $template = $this->dirs['template'];
322            foreach (array('bootstrap', 'images', 'css', 'js') as $dir) {
323                exec("cp -r '$template/$dir' '$data/$dir'");
324            }
325            copy("$template/favicon.png", "$output_base/favicon.png");
326        }
327
328        if (file_exists($index = "$output_base/index.html")) {
329            unlink($index);
330        }
331
332        // obtenir les description des plugins
333        $plugins = array();
334        foreach ($presents as $prefixe => $present) {
335            if (isset($this->infos_plugins[$prefixe])) {
336                $plugins[$prefixe] = $this->infos_plugins[$prefixe];
337            } else {
338                // ce n'était pas un plugin ou erreur
339                $plugins[$prefixe] = array(
340                    'prefixe'       => $prefixe,
341                    'nom'           => $prefixe,
342                    'slogan'        => '',
343                    'description'   => '',
344                    'annuaire'      => '',
345                    'documentation' => '',
346                    'developpement' => '',
347                );
348            }
349        }
350        usort($plugins, function($a, $b) {
351            return ($a['nom'] < $b['nom']) ? -1 : 1;
352        });
353
354        // charger Twig, générer la page et l'enregitrer
355        $loader = new \Twig_Loader_Filesystem( $this->dirs['helper'] . '/Template' );
356        $twig   = new \Twig_Environment($loader);
357
358        $topnav = $this->getOption('topnav', '');
359
360        $content = $twig->render('index.html', array(
361            'titre' => 'Documentation automatique des plugins SPIP',
362            'plugins' => $plugins,
363            'topnav' => $topnav,
364        ));
365
366        file_put_contents($index, $content);
367    }
368
369
370    /**
371     * Télécharge la source de liste des documentations à faire dans autodoc.txt
372     * et fait une rotation avec l'ancienne liste
373     *
374     * @param strig $file
375     *     URL du fichier source (svn) de la liste des docémentations à générer
376     * @return bool True si ok.
377    **/
378    public function actualiser_liste_source($file) {
379        // supprimer l'ancienne rotation
380        if (file_exists( $this->files['autodoc.txt.bak'] )) {
381            unlink($this->files['autodoc.txt.bak']);
382        }
383        // rotation
384        if (file_exists( $this->files['autodoc.txt'] )) {
385            copy($this->files['autodoc.txt'], $this->files['autodoc.txt.bak']);
386            unlink($this->files['autodoc.txt']);
387        }
388
389        // copier le fichier de liste chez soi
390        if (0 === strpos($file, 'svn://')) {
391            $this->output->write("* Obtenir <info>$file</info>  ");
392
393            // si c'est en svn, on l'exporte simplement.
394            if ($res = $this->makeSvnCommand("export $file autodoc.txt", true, $this->dirs['work'])) {
395                $this->output->writeln("[<info>OK</info>]");
396            } else {
397                $this->output->writeln("[<info>Error</info>]");
398                throw new \Exception("Impossible de récupérer le fichier SVN : $file");
399            }
400        } else {
401            if (file_exists($file)) {
402                copy($file, $this->files['autodoc.txt']);
403            } else {
404                throw new \Exception("Impossible de trouver le fichier : $file");
405            }
406        }
407
408        return true;
409    } 
410
411    /**
412     * Effacte une ou plusieurs documentations qui ne sont plus listés
413     * dans la description du fichier autodoc.txt
414     *
415     * @param array $absents
416     *     Liste [ prefixe => url ]
417     * @return bool
418     *     True si au moins une documentation, et ses caches, ont été effacés.
419    **/
420    public function supprimer_vieux_plugins($absents) {
421        if ($absents) {
422            $this->output->writeln("<comment>Certaines documentations ne sont plus à générer.</comment>");
423            foreach ($absents as $prefixe=>$absent) {
424                $this->output->writeln("<comment>- Effacement de $prefixe.</comment>");
425                $this->deleteDirectoryContent($output_base . '/$prefixe', true);
426                $this->deleteDirectoryContent($this->dirs['work'] . 'log/$prefixe', true);
427                $this->deleteDirectoryContent($this->dirs['work'] . 'input/$prefixe', true);
428                $this->deleteDirectoryContent($this->dirs['work'] . 'cache/$prefixe', true);
429            }
430            return true;
431        }
432        return false;
433    }
434
435    /**
436     * Analyse un fichier listant les documentations à générer.
437     *
438     * @param string $file Chemin du fichier
439     * @param bool $write_errors Ecrire les erreurs dans la console ?
440     * @return array|bool
441     *     - false si echec,
442     *     - couples (prefixe => array) sinon. Le tableau de description a les clés suivantes :
443     *          - ligne : numéro de ligne
444     *          - type : 'svn'
445     *          - source : url du svn
446    **/
447    private function parseFile($file, $write_errors = true) {
448        if (!file_exists($file)) return false;
449        if (!$lines = file($file)) return false;
450
451        $liste = array();
452        foreach ($lines as $lineno => $line) {
453            if (!$line) continue;
454            $line = trim($line);
455            if (!$line OR $line[0] == '#') continue;
456            $couples = explode(';', $line);
457            $lineno++;
458            if (count($couples) != 2) {
459                if (count($couples) == 1) {
460                    $this->output->writeln("<error>Ligne $lineno omise. Le préfixe ne semble pas défini. Contenu : $line</error>");
461                } else {
462                    $this->output->writeln("<error>Ligne $lineno omise. Trop de paramètres indiqués. Contenu : $line</error>");
463                }
464                continue;
465            }
466
467            list ($url, $prefixe) = $couples;
468            if (!$url) {
469                $this->output->writeln("<error>Ligne $lineno omise. L'URL ne semble pas définie. Contenu : $line</error>");
470                continue;
471            }
472            if (!$prefixe) {
473                $this->output->writeln("<error>Ligne $lineno omise. Le préfixe ne semble pas défini. Contenu : $line</error>");
474                continue;
475            }
476            if (isset($liste[$prefixe])) {
477                $this->output->writeln("<error>Ligne $lineno omise. Le prefixe $prefixe est déjà déclaré ligne : " . $liste[$prefixe]['ligne'] . "</error>");
478                continue;
479            }
480
481            // pas d'erreur !
482            $liste[$prefixe] = array(
483                'type' => 'svn',
484                'ligne' => $lineno,
485                'source' => $url
486            );
487        }
488        return $liste;
489    }
490
491
492    /**
493     * Effacer le contenu du répertoire log.
494     *
495     * On ne gardera du coup que les logs de la prochaine exécution.
496     *
497     * @return bool true si réussi
498    **/
499    private function clearLogs() {
500        return $this->deleteDirectoryContent($this->dirs['log']);
501    }
502
503    /**
504     * Définit les répertoires utiles à l'application
505    **/
506    private function setApplicationDirectories() {
507        # ce répertoire
508        $this->dirs['helper'] = realpath(__DIR__);
509
510        # executable php de l'application autodoc (extension de l'application phpdocumentor)
511        $this->dirs['bin'] = realpath( $this->dirs['helper'] . '/../../../bin');
512
513        # répertoire racine (celui depuis lequel on execute ce script).
514        exec('pwd', $output);
515        $this->dirs['root'] = $output[0];
516
517        # répertoire de travail (celui où on écrira tout).
518        $this->dirs['work'] = $this->dirs['root'] . '/work';
519
520        # répertoire du template zora
521        $this->dirs['template'] = realpath( $this->dirs['helper'] . '/../../../templates/zora');
522
523        # fichier de config xml pour phpdocumentor
524        $this->files['phpdoc.xml'] = $this->dirs['work'] . '/phpdoc.xml';
525    }
526
527
528    /**
529     * Définit et crée les répertoires nécessaires au fonctionnement de ce programme
530     *
531     * @param string $prefixe Préfixe utilisé pour cette génération
532     * @return bool true si réussi
533    **/
534    private function createDirectories($prefixe) {
535        $_work = $this->dirs['work'];
536        $this->output->writeln("* Vérifier/créer les répertoires de travail dans <info>$_work</info>");
537
538        # si un répertoire pour toutes les sorties est indiqué, forcer son utilisation.
539        if ($this->input->hasOption('sorties') and $dir_output = $this->input->getOption('sorties')) {
540            $this->setOption('dirs/output', $dir_output . "/$prefixe");
541        }
542
543        # si un répertoire de sortie est indiqué, forcer son utilisation.
544        if ($this->input->hasOption('sortie') and $dir_output = $this->input->getOption('sortie')) {
545            $this->setOption('dirs/output', $dir_output);
546        }
547
548        foreach (array('output', 'input', 'log', 'cache') as $dir) {
549            // valeur par défaut, en fonction du préfixe
550            $this->dirs[$dir] = "$_work/$dir/$prefixe";
551
552            // valeur forcée dans certains cas
553            if ($path = $this->getOption("dirs/$dir")) {
554                $this->dirs[$dir] = $path;
555            }
556        }
557
558        foreach (array('output', 'input', 'log', 'cache') as $dir) {
559            if (!$this->createDirectory( $this->dirs[$dir] )) {
560                return false;
561            }
562        }
563
564        return true;
565    }
566
567
568    /**
569     * Créer un des répertoires d'utilisation
570     *
571     * Vérifie également que le répertoire est utilisable en écriture.
572     *
573     * @param string $chemin Répertoire à créer
574     * @return bool true si réussi
575    **/
576    private function createDirectory($dir) {
577        if (!is_dir($dir)) {
578            $this->output->writeln("  - Création du répertoire <info>$dir</info>");
579
580            if (!@mkdir($dir, 0755, true)) {
581                $this->output->writeln("<error>Impossible de créer le répertoire : $dir</error>");
582                $error = error_get_last();
583                $this->output->writeln($error['message']);
584                return false;
585            }
586
587        }
588        if (!is_writable($dir)) {
589            $this->output->writeln("<error>Le répertoire $dir n'est pas accessible en écriture</error>");
590            return false;
591        }
592        return true;
593    }
594
595
596    /**
597     * Télécharge ou met à jour la source SVN indiquée
598     *
599     * @param string $source URL de la source SVN
600     * @return bool true si réussi
601    **/
602    private function getSvnSource($source) {
603        $this->output->writeln("* Obtenir <info>$source</info>");
604
605        $this->svn = array();
606
607        // si c'est en svn et le bon, on fait svn up simplement.
608        if ($res = $this->makeSvnCommand('info --xml')) {
609            // nous avons un svn… verifions que c'est le bon !
610            $xml = simplexml_load_string(implode("", $res));
611            $source_actuelle = (string)$xml->entry->url;
612
613            if ($source_actuelle == $source) {
614                #$this->output->write("<comment>  - Update    </comment>");
615
616                if (!$res = $this->makeSvnCommand("update")) {
617                    $this->output->writeln("<error>[Echec Update]</error>");
618                    return false;
619                }
620
621                $last = array_pop($res);
622                # $this->output->writeln("[<info>OK</info>]  ($last)");
623            }
624        }
625
626        // erreur, donc pas un dossier svn ou pas le bon
627        else {
628            // on nettoie le contenu pour svn co
629            $this->deleteDirectoryContent($this->dirs['input']);
630
631            #$this->output->write("<comment>  - Checkout    </comment>");
632
633            if (!$res = $this->makeSvnCommand("checkout $source .")) {
634                $this->output->writeln("<error>[Echec Checkout]</error>");
635                return false;
636            }
637            $last = array_pop($res);
638            if ($last) $last = "($last)";
639
640            # $this->output->writeln("<info>[OK]</info>  $last");
641        }
642
643        // On récupère le numéro de dernière révision, ça peut servir
644        if ($revision = $this->recuperer_revision_svn()) {
645            $this->svn['revision'] = $revision;
646        }
647
648        return true;
649    }
650
651    /**
652     * Retrouver le numéro de la dernière révision du dossier de travail
653     *
654     * @return string Numéro de révision (si connu)
655    **/
656    function recuperer_revision_svn() {
657        $revision = '';
658
659        // Ici, on a les bons fichiers SVN à jour
660        // on récupère le numéro de dernière révision
661        if ($res = $this->makeSvnCommand('info --xml')) {
662            // nous avons un svn… verifions que c'est le bon !
663            $xml = simplexml_load_string(implode("", $res));
664            $revision = (string)$xml->entry->commit['revision'];
665        }
666
667        return $revision;
668    }
669
670    /**
671     * Exécuter la commande svn indiquée
672     *
673     * @param string $cmd Commande SVN
674     * @param bool $no_error true : Envoie les erreurs dans /dev/null
675     * @return mixed|bool false en cas d'erreur
676    **/
677    private function makeSvnCommand($cmd, $no_error = true, $dir = null) {
678        if (is_null($dir)) {
679            $dir = $this->dirs['input'];
680        }
681        $svn = '/usr/bin/svn';
682        $no_error = $no_error ? '2> /dev/null' : '';
683
684        exec("cd $dir && $svn $cmd $no_error", $res, $error);
685        if ($error) return false;
686        return $res;
687    }
688
689
690    /**
691     * Suppression du contenu d'un repertoire.
692     *
693     * @link http://www.php.net/manual/en/function.rmdir.php#92050
694     *
695     * @param string $dir Chemin du repertoire
696     * @param string $delete_me Supprimer aussi le répertoire ?
697     * @return bool Suppression reussie.
698     */
699    function deleteDirectoryContent($dir, $delete_me = false) {
700        if (!file_exists($dir)) return true;
701        if (!is_dir($dir) || is_link($dir)) return @unlink($dir);
702
703        foreach (scandir($dir) as $item) {
704            if ($item == '.' || $item == '..') continue;
705            if (!$this->deleteDirectoryContent($dir . "/" . $item, true)) {
706                @chmod($dir . "/" . $item, 0777);
707                if (!$this->deleteDirectoryContent($dir . "/" . $item, true)) return false;
708            };
709        }
710
711        if ($delete_me) {
712            return @rmdir($dir);
713        }
714
715        return true;
716    }
717
718
719    /**
720     * Obtient une option, soit de la ligne de commande, soit forcée par le générateur
721     *
722     * @param string $name Nom de l'option
723     * @return mixed
724    **/
725    private function getOption($name, $default = null) {
726        if (strpos($name, '/') === false) {
727            if ($this->input->hasOption($name) and $valeur = $this->input->getOption($name)) {
728                return $valeur;
729            }
730
731            if (isset($this->options[$name]) and $this->options[$name]) {
732                return $this->options[$name];
733            }
734
735            return $default;
736        }
737
738        $name = explode('/', $name);
739        $pointeur = &$this->options;
740        while (true) {
741            $n = array_shift($name);
742            if (!isset($pointeur[$n])) {
743                return $default;
744            }
745            if (!count($name)) {
746                return $pointeur[$n];
747            }
748            if (!is_array($pointeur[$n])) {
749                return $default;
750            }
751            $pointeur = &$pointeur[$n];
752        }
753    }
754
755
756    /**
757     * Enregistre une option (forcée par le générateur)
758     *
759     * Permet d'enregistrer avec des chemins par /
760     *
761     * @example
762     *      $this->setOption('dirs/output', $dir)
763     *
764     * @param string $name Nom de l'option
765     * @param string $value Valeur de l'option
766    **/
767    private function setOption($name, $value) {
768        $name = explode('/', $name);
769        $n = array_shift($name);
770        $pointeur = &$this->options;
771        while ($name) {
772            if (!is_array($pointeur[$n])) {
773                $pointeur[$n] = array();
774            }
775            $pointeur = &$pointeur[$n];
776            $n = array_shift($name);
777        }
778        $pointeur[$n] = $value;
779    }
780
781    /**
782     * Récupère une option de commande pour phpDocumentor
783     *
784     * @param string $command Nom de la commande
785     * @return mixed
786    **/
787    private function getCommand($command) {
788        if (isset($this->commands[$command])) {
789            $val = $this->commands[$command];
790            return " --$command=\"$val\"";
791        }
792        return null;
793    }
794
795    /**
796     * Enregistre une option de commande pour phpDocumentor
797     *
798     * @param string $command Nom de la commande
799     * @param string $value Valeur de la commande
800    **/
801    private function setCommand($command, $value) {
802        $this->commands[$command] = $value;
803    }
804
805
806
807    /**
808     * Retrouver des infos du paquet.xml si on le trouve
809     *
810     * Définir avec le titre, et une présentation (si ce n'est déjà fait).
811     *
812     * @return bool true si on a trouvé un paquet.xml
813    **/
814    private function retrouverInfoPaquetXml() {
815        $source = $this->dirs['input'];
816        $is_spip = false;
817
818        if (!file_exists($paquet = $source . '/paquet.xml')) {
819            $is_spip = true;
820            if (!file_exists($paquet = $source . '/ecrire/paquet.xml')) {
821                return false;
822            }
823        }
824
825        $paquet = simplexml_load_file($paquet);
826        if (!$paquet) {
827            $this->output->writeln("<comment>! Aucun paquet.xml</comment>");
828            return false;
829        }
830
831        $nom     = (string)$paquet->nom;
832        $version = (string)$paquet['version'];
833        $prefixe = (string)$paquet['prefix'];
834        $this->infos_plugins[$prefixe] = array(
835            'nom'         => $nom,
836            'prefixe'     => $prefixe,
837            'slogan'        => '',
838            'description'   => '',
839            'annuaire'      => '',
840            'documentation' => '',
841            'developpement' => '',
842        );
843        $documentation = $developpement = $lien_plugins_spip = '';
844
845        if (isset($paquet['documentation'])) {
846            $documentation = $paquet['documentation'];
847            $this->infos_plugins[$prefixe]['documentation'] = $documentation;
848        }
849        if (isset($paquet['developpement'])) {
850            $developpement = $paquet['developpement'];
851            $this->infos_plugins[$prefixe]['developpement'] = $developpement;
852        }
853
854        if (!$is_spip) {
855            $lien_plugins_spip = "http://plugins.spip.net/$prefixe.html";
856            $this->infos_plugins[$prefixe]['annuaire'] = $lien_plugins_spip;
857
858            // récupérer les infos dans les chaînes de langue.
859
860            $langue = $source . "/lang/paquet-$prefixe"."_fr.php";
861            if (file_exists($langue)) {
862                $GLOBALS['idx_lang'] = 'pour_autodoc';
863                defined('_ECRIRE_INC_VERSION') || define('_ECRIRE_INC_VERSION', 1);
864                @include $langue;
865                $infos = $GLOBALS['pour_autodoc'];
866
867                if (is_array($infos)) {
868                    if (!$nom and isset($infos["$prefixe" . "_nom"])) {
869                        $nom = $infos["$prefixe" . "_nom"];
870                        $this->infos_plugins[$prefixe]['nom'] = $nom;
871                    }
872                    if (isset($infos["$prefixe" . "_slogan"])) {
873                        $this->infos_plugins[$prefixe]['slogan'] = $infos["$prefixe" . "_slogan"];
874                    }
875                    if (isset($infos["$prefixe" . "_descriptif"])) {
876                        $this->infos_plugins[$prefixe]['descriptif'] = $infos["$prefixe" . "_descriptif"];
877                    }
878                }
879            }
880        }
881
882        if (!$this->getOption('titre') and $nom) {
883            $this->setOption('titre', $nom);
884        }
885
886        if (!$this->getOption('presentation')) {
887
888            if ($is_spip) {
889                $presentation = "Cette documentation est issue du code source PHP de SPIP $version.&#13;&#10;&#13;&#10;";
890            } else {
891                $rev = "";
892                if (isset($this->svn['revision']) and $revision = $this->svn['revision']) {
893                    $rev .= " (révision $revision)";
894                }
895                $presentation = "Cette documentation est issue du code source PHP du plugin « ".$nom." », version $version$rev.&#13;&#10;&#13;&#10;";
896            }
897
898            if (!$is_spip OR $documentation OR $developpement) {
899                if (!$is_spip) {
900                    $presentation .= "- [Description dans l'annuaire des plugins]($lien_plugins_spip)&#13;&#10;";
901                }
902                if ((string)$documentation) {
903                    $presentation .= "- [Documentation]($documentation)&#13;&#10;";
904                }
905                if ((string)$developpement) {
906                    $presentation .= "- [Outil de développement]($developpement)&#13;&#10;";
907                }
908            }
909            $this->setOption('presentation', $presentation);
910        }
911
912        return true;
913    }
914
915    /**
916     * Utilise le template de configuration phpdoc.xml en modifiant ses variables
917     * et l'enregistre pour qu'il soit utilisé par l'application
918     *
919     * @return bool true si réussi.
920    **/
921    private function prepareConfigXml() {
922
923        $this->output->write("* Préparer le fichier phpdoc.xml   ");
924        $template = $this->dirs['helper'] . '/phpdoc_helper.xml';
925        $template = file_get_contents($template);
926
927        // c'est pas le meilleur endroit pour faire ça… mais bon.
928        if ($this->getOption('avec_boussole_spip')) {
929            $this->setOption('topnav', '//boussole.spip.net/?page=spipnav.js&lang=fr');
930        }
931
932        $substitutions = array(
933            '@DIR_CACHE@'  => $this->dirs['cache'],
934            '@DIR_OUTPUT@' => $this->dirs['output'],
935            '@DIR_INPUT@'  => $this->dirs['input'],
936            '@DIR_LOG@'    => $this->dirs['log'],
937            '@DIR_ROOT@'   => $this->dirs['root'],
938
939            '@OPT_TITRE@'         => $this->escape_xml_attr($this->getOption('titre', '')),
940            '@OPT_DESCRIPTION@'   => $this->escape_xml_attr($this->getOption('description', '')),
941            '@OPT_PRESENTATION@'  => $this->escape_xml_attr($this->getOption('presentation', '')),
942            '@OPT_TITRE_ONGLETS@' => $this->escape_xml_attr($this->getOption('titre_onglets', 'Sommaire')),
943            '@OPT_SITE@'          => $this->escape_xml_attr($this->getOption('site', '')),
944            '@OPT_TOPNAV@'        => $this->escape_xml_attr($this->getOption('topnav', '')),
945        );
946
947        $template = str_replace(array_keys($substitutions), array_values($substitutions), $template);
948
949        $destination = $this->dirs['work'] . '/phpdoc.xml';
950        @unlink($destination);
951
952        if (!@file_put_contents($this->files['phpdoc.xml'], $template)) {
953            $this->output->writeln("<error>[Echec]</error>");
954            return false;
955        }
956
957        $this->output->writeln("<info>[OK]</info>");
958        return true;
959    }
960
961
962    /**
963     * Exécute l'application phpDocumentor avec le phpdoc.xml qui a été créé
964     *
965     * @return bool true si réussi.
966    **/
967    private function execute() {
968        $conf = $this->files['phpdoc.xml'];
969        $command = "project:run --config=$conf --parseprivate";
970
971        if ($title = $this->getCommand('title')) {
972            $command .= $title;
973        }
974
975        // l'option --config=x de phpdocumentor (contrairement aux autres) n'est lue que via ArgvInput.
976        // de la sorte, pour transmettre cette option, on crée l'argv qui va bien. Ce n'est pas très glorieux !
977        $command_helper = $_SERVER['argv'];
978        $command = new StringInput("autodoc $command");
979        // pas moyen d'obtenir le tableau adapté directement… on triche par une réflexion.
980        $reflectedCommand = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput');
981        $tokens = $reflectedCommand->getProperty('tokens');
982        $tokens->setAccessible(true);
983        $_SERVER['argv'] = $tokens->getValue($command);
984
985        $app = new Autodoc( $this->autoloader );
986        $app->run();
987
988        $_SERVER['argv'] = $command_helper;
989    }
990
991
992    /**
993     * Échappe une chaîne pour l'insérer dans un fichier xml, en tant que valeur d'attribut
994     *
995     * @param string $string
996     * @return string
997    **/
998    private function escape_xml_attr($string) {
999        return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
1000    }
1001}
Note: See TracBrowser for help on using the repository browser.