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 :

inclued1

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 :

inclued2

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