Introduction

Le package PECL inclued est une extension PHP permettant d'affecter son comportement. Cette extension permet d'afficher des informations sur les différents fichiers inclus par les instructions de langage include(), include_once(), require() et require_once().
En plus de renvoyer des informations bien complètes sur l'arbre d'inclusion des fichiers, nous verrons un peu plus bas comment générer des graphes à partir de cette extension couplée à Graphviz.

Inclued peut être utile lors d'une phase de débug, lorsque vous jouez un peu avec les inclusions si celles-ci sont nombreuses. Les inclusions automatiques via la fonction __autoload() peuvent parfois être contraignantes lors d'une utilisation massive de classes.

Installation

Inclued vient à peine de sortir en version bêta peut s'installer via PECL. Pour cela, lancez votre Terminal, et installez inclued en forçant la main :

sudo pecl install -f inclued  
WARNING: failed to download pecl.php.net/inclued within preferred state "stable", will instead download version 0.1.1, stability "beta"  
downloading inclued-0.1.1.tgz ...  
Starting to download inclued-0.1.1.tgz (9,022 bytes)  
.....done: 9,022 bytes
5 source files, building  
running: phpize  
Configuring for:  
PHP Api Version:         20090626  
Zend Module Api No:      20090626  
Zend Extension Api No:   220090626  
# ...
Build process completed successfully  
Installing '/opt/local/lib/php/extensions/no-debug-non-zts-20090626/inclued.so'  
install ok: channel://pecl.php.net/inclued-0.1.1  
configuration option "php_ini" is not set to php.ini location  
You should add "extension=inclued.so" to php.ini  

N'oubliez pas de rajouter la directive inclued.enabled = 1; dans votre php.ini sinon l'extension sera chargée mais pas activée.
D'ailleurs pour cela :

<?php  
// Test du package inclued
echo '<h1>PHP: inclued extension test</h1><p>Current PHP version: ' . phpversion() . '<br />Inclued extension loaded: ';  
echo function_exists('inclued_get_data') ? 'true' : 'false';  
echo '<br />Inclued extension enabled: ';  
echo ini_get('inclued.enabled') ? 'true</p>' : 'false</p>';  
PHP: inclued extension test  
Current PHP version: 5.3.1  
Inclued extension loaded: true  
Inclued extension enabled: true  

Test

Placez-vous dans un répertoire avec quelques fichiers et classes et faites des inclusions de fichiers :

<?php  
include 'lab.php'; // includes a « require 'path.func.php' »  
include 'listFiles.class.php';  
require_once 'test_class.php';  
include_once 'listFiles.class.php';

$clue = inclued_get_data();

// On affiche les données recueillies
echo '<pre>';  
print_r($clue);  
echo '<//pre>';

// On sauve les données dans un fichier (pour la suite)
if (fopen('inclued.data', 'w+')) {  
    if (is_writable('inclued.data')) {
        file_put_contents('inclued.data', serialize($clue));
    }
}

De cette manière-ci, vous obtenez un tableau multi-dimensionnel contenant un tas d'informations dans même genre que celui-ci :

Array  
(
    [request] => Array
        (
            [_COOKIE] => Array
                (
                )

            [SCRIPT_FILENAME] => /htdocs/lab/inclued.php
            [REQUEST_URI] => /lab/inclued.php
            [REQUEST_TIME] => 1267217047
        )

    [includes] => Array
        (
            [0] => Array
                (
                    [operation] => include
                    [op_type] => 2
                    [filename] => lab.php
                    [opened_path] => /htdocs/lab/lab.php
                    [fromfile] => /htdocs/lab/inclued.php
                    [fromline] => 9
                )

            [1] => Array
                (
                    [operation] => require
                    [op_type] => 8
                    [filename] => path.func.php
                    [opened_path] => /htdocs/lab/path.func.php
                    [fromfile] => /htdocs/lab/lab.php
                    [fromline] => 46
                )

            [2] => Array
                (
                    [operation] => include
                    [op_type] => 2
                    [filename] => listFiles.class.php
                    [opened_path] => /htdocs/lab/listFiles.class.php
                    [fromfile] => /htdocs/lab/inclued.php
                    [fromline] => 10
                )

            [3] => Array
                (
                    [operation] => require_once
                    [op_type] => 16
                    [filename] => test_class.php
                    [opened_path] => /htdocs/lab/test_class.php
                    [fromfile] => /htdocs/lab/inclued.php
                    [fromline] => 11
                )

            [4] => Array
                (
                    [operation] => include_once
                    [op_type] => 4
                    [filename] => listFiles.class.php
                    [opened_path] => /htdocs/lab/listFiles.class.php
                    [duplicate] => 1
                    [fromfile] => /htdocs/lab/inclued.php
                    [fromline] => 12
                )

        )

    [inheritance] => Array
        (
        )

    [classes] => Array
        (
            [0] => Array
                (
                    [name] => listFiles
                    [filename] => /htdocs/lab/listFiles.class.php
                    [line] => 26
                )

            [1] => Array
                (
                    [name] => Foo
                    [mangled_name] => foo
                    [filename] => /htdocs/lab/test_class.php
                    [line] => 2
                )

            [2] => Array
                (
                    [name] => Bar
                    [mangled_name] => bar
                    [filename] => /htdocs/lab/test_class.php
                    [line] => 14
                    [parent] => Array
                        (
                            [name] => Foo
                            [filename] => /htdocs/lab/test_class.php
                            [line] => 2
                        )

                )

        )

)

On notera la présence de l'entrée [duplicate] => 1 lors de l'include_once de la classe listFiles.class.php. Vraiment pratique et précis.

Vous avez maintenant l'arbre d'inclusion de vos fichiers décrit de façon assez précis mais pas très bien exploitable encore. Il serait beaucoup plus ludique et accessible d'obtenir une présentation plus tape à l'oeil, un peu comme des graphes.

Générer un graphe avec Graphviz

Installez Graphviz avec votre gestionnaire de paquets préféré mais attention, il y a une forte dépendance de paquets alors ne faite pas cela quand vous êtes pressé...

sudo port install graphviz  

Graphiz travaille avec des fichiers .DOT donc il vous faudra convertir votre tableau multi-dimensionnel en ce genre de fichier. Heureusement, l'un des deux développeurs de inclued a pensé à cela et donc vous devriez trouver le fichier gengraph.php dans votre include_path :

php -r 'echo get_include_path();'  
.:/opt/local/lib/php%
php /opt/local/lib/php/gengraph.php  
/opt/local/lib/php/gengraph.php -i  [-t includes|classes] [-o ] [-d docroot]
zsh: exit 1     php /opt/local/lib/php/gengraph.php  

On va donc transformer notre fichier inclued.data en inclued.dot et générer inclued.png :

php -f /opt/local/lib/php/gengraph.php -- -i inclued.data -o inclued.dot  
Written inclued.dot...  
To generate images: dot -Tpng -o inclued.png inclued.dot  
dot -Tpng -o inclued.png inclued.dot  

Théoriquement vous devriez avoir un graphe de ce genre :

Personnellement, je ne trouve pas cela terrible et j'aime bien qu'un arbre soit descendant. Puis dans mon exemple, je n'ai que faire des chemins absolus.
Editons le fichier gengraph.php :

<?php  
// On rajoute quelques basename() et le tour est joué
$short_fromfile = basename(str_replace($docroot, '', $inc['fromfile']));
$short_opened_path = basename(str_replace($docroot, '', $inc['opened_path']));
<?php  
// On rajoute aussi un basename() ici
$short_fromfile = basename(str_replace($docroot, '', $autoload['fromfile']));
<?php  
// On supprime la restriction de taille (la clause "size")
$content =  <<<EOF
digraph phpdeps {  
    node [shape = ellipse];
    node [color="#add960", style=filled];
    graph [bgcolor="#ffffff"];
EOF;  
<?php  
/* On supprime la clause qui permet au graphe de s'étendre horizontalement plutôt que verticalement (par défaut)
    $content =  <<<EOF
        rankdir = "LR";
EOF;  
    fwrite($fp, $content);
*/

Ainsi ces modifications faites, relancez vos commandes et vous devriez avoir un magnifique résultat :

Rien à voir, cela fait vraiment pro'. On remarquera toujours le duplicate avec sa flèche en pointillés.
Les possibilités d'enrichissement graphiques sont très grandes car Graphviz offre réellement une configuration poussée et la possibilité de générer des graphiques très avancés. Voie à explorer...

Vous savez désormais comment générer de manière très lisible un graphe d'arbre d'inclusion avec PHP et pour PHP. D'ailleurs, j'en ai profité pour lancer un petit projet avec une classe Inclued.


Joris Berthelot