source: spip-zone/_galaxie_/code.spip.net/autodoc/trunk/src/autodoc/Plugin/Core/TwigZora.php @ 74904

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

La liste des éléments SPIP des plugins (ie. balises > onglet plugins) est triée par répertoire de plugin.

File size: 17.3 KB
Line 
1<?php
2/**
3 * autodoc
4 *
5 * @author    Matthieu Marcillaud
6 */
7
8namespace autodoc\Plugin\Core;
9
10use phpDocumentor\Plugin\Core\Twig\ExtensionInterface;
11use phpDocumentor\Transformer\Transformation;
12use phpDocumentor\Descriptor\ProjectDescriptor;
13use phpDocumentor\Descriptor\Collection;
14use Zend\Config\Factory;
15use Zend\Config\Config;
16
17/**
18 * Extension Twig pour l'usage avec SPIP et phpDocumentor (pour le template zora)
19 */
20class TwigZora extends \Twig_Extension implements ExtensionInterface
21{
22    /**
23     * @var ProjectDescriptor
24     */
25    protected $data = null;
26   
27    /**
28     * @var Zend\Config\Config
29     */
30    protected $configuration = null;
31
32
33    /**
34     * Initializes the runtime environment.
35     *
36     * @param Twig_Environment $environment The current Twig_Environment instance
37     */
38    public function initRuntime(\Twig_Environment $environment) {
39        # ajout de l'extension debug
40        # $environment->addExtension(new \Twig_Extension_Debug()); // trop tard ici.
41        # activer le debug pour les tests...
42        $environment->enableDebug();
43    }
44
45    /**
46     * Registers the structure and transformation with this extension.
47     *
48     * @param ProjectDescriptor $project        Represents the complete Abstract Syntax Tree.
49     * @param Transformation    $transformation Represents the transformation meta data used in the current generation
50     *     cycle.
51     */
52    public function __construct(ProjectDescriptor $project, Transformation $transformation)
53    {
54        $this->data = $project;
55
56        // obtenir les informations de configuration (en trichant) afin d'envoyer des options au template zora
57        // Note (5 aout 2013) :
58        //   <marcimat> Is there a way to access to the configuration of phpDocumentor (ie. like phpdoc.xml informations)
59        //              in a TWIG plugin ?  We recieve a ProjectDescriptor and a Transformation but neither seems to have that.
60        //   <mvriel>   marcimat: not yet, a minor refactoring of the configuration
61        //              is something that I desire and then to expose it in Twig
62        $this->configuration = $this->getPhpDocumentorConfiguration();
63    }
64
65    /**
66     * Returns the name of this extension.
67     *
68     * @return string The extension name
69     */
70    function getName()
71    {
72        return 'TwigZora';
73    }
74
75
76    /**
77     * Retrouver la configuration de phpDocumentor.
78     *
79     * Attention, cette configuration ne tient pas compte d'éventuelles
80     * surcharge via la ligne de commande de l'application.
81     *
82     * On triche en recalculant la configuration, vu qu'on n'y a pas encore
83     * accès depuis un plugin Twig.
84     *
85     * @return Zend\Config\Config
86     */
87    protected function getPhpDocumentorConfiguration()
88    {
89       // Cf. Application.php (de phpdocumentor).
90        $user_config_file = (file_exists(getcwd() . DIRECTORY_SEPARATOR . 'phpdoc.xml'))
91            ? getcwd() . DIRECTORY_SEPARATOR . 'phpdoc.xml'
92            : getcwd() . DIRECTORY_SEPARATOR . 'phpdoc.dist.xml';
93        $config_files     = array(__DIR__ . '/../../../../vendor/phpdocumentor/phpdocumentor/data/phpdoc.tpl.xml');
94        if (is_readable($user_config_file)) {
95            $config_files[] = $user_config_file;
96        }
97
98        $config = Factory::fromFiles($config_files, true);
99
100        // pareil pour la version de phpDocumentor
101        $phpDocConfiguration = new Config(array(
102            "phpDocumentor" => array(
103                "version" => file_get_contents(__DIR__ . '/../../../../vendor/phpdocumentor/phpdocumentor/VERSION')
104            )
105        ));
106
107        return $config->merge($phpDocConfiguration);
108    }
109
110
111    /**
112     * Returns an array of global variables to inject into a Twig template.
113     *
114     * @return mixed
115     */
116    public function getGlobals()
117    {
118        return array(
119            'configuration' => $this->configuration,
120        );
121    }
122
123    /**
124     * Liste des fonctions ajoutés à Twig
125     * @return array
126    **/
127    public function getFunctions() {
128        return array(
129            /**
130             * Indiquer si le projet analysé est issu d'un code source de SPIP
131             *
132             * @param array $context
133             *     Contexte du template (transmis automatiquement par twig)
134             * @return bool
135             *     true si c'est un SPIP.
136             */
137            'is_spip' => new \Twig_SimpleFunction('is_spip',
138                function ($context) {
139                    $fqsens = $context['project']->getIndexes()->get('elements');
140                    return (bool)$fqsens->get("./ecrire/inc_version.php");
141                }, array('needs_context' => true)
142            ),
143        );
144    }
145
146    /**
147     * Liste des filtres ajoutés à Twig
148     * @return array
149    **/
150    public function getFilters()
151    {
152        return array(
153            /**
154             * Trier des éléments d'une Collection selon une demande
155             *
156             * @example
157             *     node.functions|trier('name')
158             *     file.allerrors|trier('line')
159             *     project.indexes.spip.pipelines|trier(
160             *          'tags.pipeline.0.description',
161             *          'getFullyQualifiedStructuralElementName'
162             *     )
163             *
164             * @param Collection|null $collection
165             *     Liste d'éléments de phpDocumentor
166             * @param string $tri
167             *     Critère de tri
168             * @param string $tri2
169             *     Critère de tri en cas d'équivalence du premier tri
170             * @return Collection
171             *     Liste d'éléments triée
172             */
173            'trier' => new \Twig_SimpleFilter('trier',
174                function ($collection, $tri = "", $tri2 = "") {
175                    if (!is_object($collection)) {
176                        return new Collection();
177                    }
178
179                    if ($collection->count() <= 1) {
180                        return $collection;
181                    }
182
183                    $array = $collection->getAll();
184                    uasort($array, function($a, $b) use ($tri, $tri2) {
185                        $t1 = strtolower(TwigZora::walkObjectTree($a, $tri));
186                        $t2 = strtolower(TwigZora::walkObjectTree($b, $tri));
187                        if ($tri2 and ($t1 == $t2)) {
188                            return(
189                                strtolower(TwigZora::walkObjectTree($a, $tri2)) >
190                                strtolower(TwigZora::walkObjectTree($b, $tri2))
191                            );
192                        } else {
193                            return ($t1 > $t2);
194                        }
195                    });
196                    return new Collection($array);
197                }
198            ),
199            /**
200             * Sélectionner des éléments d'une Collection selon une demande
201             *
202             * @example
203             *     node.functions|selectionner('tags.api')
204             *     node.functions|selectionner('', ['tags.deprecated', 'tags.api'])
205             *
206             * @param Collection $collection
207             *     Liste d'éléments de phpDocumentor
208             * @param string|array $selection_avec
209             *     Condition ou liste de conditions nécessaires pour que l'élément soit accepté
210             * @param string|array $selection_sans
211             *     Condition ou liste de conditions excluant un élément
212             * @return Collection
213             *     Liste d'éléments validant les conditions
214             */
215            'selectionner' => new \Twig_SimpleFilter('selectionner',
216                function (Collection $collection, $selection_avec = array(), $selection_sans = array()) {
217                    if (!$selection_avec and !$selection_sans) {
218                        return $collection;
219                    }
220                    if (!is_array($selection_avec)) $selection_avec = array($selection_avec);
221                    if (!is_array($selection_sans)) $selection_sans = array($selection_sans);
222
223                    $selection = new Collection();
224                    foreach ($collection as $node) {
225                        // passer les nodes qu'on ne veut pas
226                        foreach ($selection_sans as $sans) {
227                            if ($sans and TwigZora::walkObjectTree($node, $sans)) {
228                                continue 2;
229                            }
230                        }
231                        // ajouter les nodes que l'on veut
232                        foreach ($selection_avec as $avec) {
233                            if ($avec and !TwigZora::walkObjectTree($node, $avec)) {
234                                continue 2;
235                            }
236                        }
237                        $selection->add($node);
238                    }
239                    return $selection;
240                }
241            ),
242            /**
243             * Liste les différents plugins contenus dans une collection
244             *
245             * @param array $context
246             *     Contexte du template (transmis automatiquement par twig)
247             * @param Collection|null $collection
248             * @return Collection nom du répertoire => description du répertoire
249            **/
250            'lister_plugins' => new \Twig_SimpleFilter('lister_plugins',
251                function ($context, $collection) {
252                    if (!is_object($collection)) {
253                        return new Collection();
254                    }
255                    $selection = new Collection();
256                    foreach ($collection as $element) {
257                        $path = $element->getPath();
258                        $path = explode('/', $path, 3);
259                        $start = array_shift($path);
260                        if (in_array($start , array('plugins-dist', 'plugins'))) {
261                            $plugin = array_shift($path);
262                            if (!$selection->get($plugin)) {
263                                $fqsens = $context['project']->getIndexes()->get('elements');
264                                // identifiant unique de répertoire, pour retrouver sa description
265                                $fqsen = './' . $start . '/' . $plugin . '/';
266                                $selection->set($plugin, $fqsens->get($fqsen));
267                            }
268                        }
269
270                    }
271                    return $selection;
272                }, array('needs_context' => true)
273            ),
274            /**
275             * Retrouve le nom du répertoire contenant un plugin
276             *
277             * Si un path est inclu dans plugins-dist ou plugins,
278             * on considère que le répertoire suivant est un nom
279             * de répertoire de plugin.
280             *
281             * @example
282             *     file|repertoire_de_plugin
283             *
284             * @param Object $element
285             *     Élément de phpDocumentor
286             * @return string
287             *     Nom du répertoire, sinon vide.
288             */
289            'repertoire_de_plugin' => new \Twig_SimpleFilter('selectionner_fichiers_dans',
290                function ($element) {
291                    if ($path = $element->getPath()) {
292                        $path = explode('/', $path, 3);
293                        $start = array_shift($chemin);
294                        if (in_array($start , array('plugins-dist', 'plugins'))) {
295                            return array_shift($path);
296                        }
297                    }
298                    return '';
299                }
300            ),
301            /**
302             * Sélectionne les fonctions d'une collection de fonctions
303             * dont les fichiers ont un chemin commençant par l'argument transmis
304             *
305             * @example
306             *     elements|selectionner_fichiers_dans('plugins-dist')
307             *
308             * @param Collection|null $collection
309             *     Liste d'éléments de phpDocumentor
310             * @param string $debut
311             *     Le chemin commence par
312             * @return Collection
313             *     Liste d'éléments
314             */
315            'selectionner_fichiers_dans' => new \Twig_SimpleFilter('selectionner_fichiers_dans',
316                function ($collection, $debut = "") {
317                    if (!is_object($collection)) {
318                        return new Collection();
319                    }
320                    if (!$debut) {
321                        return $collection;
322                    }
323
324                    $selection = new Collection();
325                    foreach ($collection as $element) {
326                        if (0 === strpos($element->getPath(), $debut)) {
327                            $selection->add($element);
328                        }
329                    }
330                    return $selection;
331                }
332            ),
333            /**
334             * Sélectionne les fonctions d'une collection de fonctions
335             * dont les fichiers ont un chemin ne commençant pas par l'argument transmis
336             *
337             * @example
338             *     elements|selectionner_fichiers_hors('plugins-dist')
339             *
340             * @param Collection|null $collection
341             *     Liste d'éléments de phpDocumentor
342             * @param string $debut
343             *     Le chemin commence par
344             * @return Collection
345             *     Liste d'éléments triée
346             */
347            'selectionner_fichiers_hors' => new \Twig_SimpleFilter('selectionner_fichiers_hors',
348                function ($collection, $debut = "") {
349                    if (!is_object($collection) OR !$debut) {
350                        return new Collection();
351                    }
352
353                    $selection = new Collection();
354                    foreach ($collection as $element) {
355                        if (! (0 === strpos($element->getPath(), $debut))) {
356                            $selection->add($element);
357                        }
358                    }
359                    return $selection;
360                }
361            ),
362            /**
363             * Transforme les sauts de paragraphe HTML `p` en simples passages à la ligne `br`
364             *
365             * @param string $texte
366             *     Texte à transformer
367             * @return string
368             *     Texte sans paraghaphes
369            **/
370            'ptobr' => new \Twig_SimpleFilter('ptobr',
371                function ($texte){
372                    $texte = preg_replace("@</p>@iS", "\n", $texte);
373                    $texte = preg_replace("@<p\b.*>@UiS", "<br />", $texte);
374                    $texte = preg_replace("@^\s*<br />@Su", "", $texte);
375                    return $texte;
376                }
377            ),
378            /**
379             * Retourne le nom du Descriptor d'un élément
380             *
381             * À partir d'une instance issue de phpDocumentor\Descriptor\DescriptorAbstract,
382             * déduit le type de Descriptor concerné. Par exemple
383             * phpDocumentor\Descriptor\ClassDescriptor retournera 'Class'
384             *
385             * @param Object $class
386             * @return string
387            **/
388            'descriptorType' => new \Twig_SimpleFilter('descriptorType',
389                function ($class){
390                    return substr( end(explode('\\',  get_class($class) )), 0, -10);
391                }
392            ),
393        );
394    }
395
396
397
398
399    /**
400     * Trouver le chemin !
401     *
402     * Voir : phpDocumentor\Plugin\Core\Transformer\Writer\Twig::walkObjectTree
403     * Presque identique : utiliser une classe séparée dès qu'elle sera disponible.
404     *
405     * Changements :
406     * - static sur la fonction
407     * - teste également les objets ArrayAccess
408     *
409     * Walks an object graph and/or array using a twig query string.
410     *
411     * Note: this method is public because it is used in a closure in {{@see getDestinationPath()}}.
412     *
413     * @param \Traversable|mixed $objectOrArray
414     * @param string             $query         A path to walk separated by dots, i.e. `namespace.namespaces`.
415     *
416     * @todo move this to a separate class and make it more flexible.
417     *
418     * @return mixed
419     */
420    public static function walkObjectTree($objectOrArray, $query)
421    {
422        $node = $objectOrArray;
423        $objectPath = explode('.', $query);
424
425        // walk through the tree
426        foreach ($objectPath as $pathNode) {
427            if (is_array($node)) {
428                if (isset($node[$pathNode])) {
429                    $node = $node[$pathNode];
430                    continue;
431                }
432            } elseif (is_object($node)) {
433                if (($node instanceof ArrayAccess) and isset($node[$pathNode])) {
434                    $node = $node[$pathNode];
435                    continue;
436                } elseif (isset($node->$pathNode) || (method_exists($node, '__get') && $node->$pathNode)) {
437                    $node = $node->$pathNode;
438                    continue;
439                } elseif (method_exists($node, $pathNode)) {
440                    $node = $node->$pathNode();
441                    continue;
442                } elseif (method_exists($node, 'get' . $pathNode)) {
443                    $pathNode = 'get' . $pathNode;
444                    $node = $node->$pathNode();
445                    continue;
446                } elseif (method_exists($node, 'is' . $pathNode)) {
447                    $pathNode = 'is' . $pathNode;
448                    $node = $node->$pathNode();
449                    continue;
450                }
451            }
452
453            return null;
454        }
455
456        return $node;
457    }
458}
Note: See TracBrowser for help on using the repository browser.