Programmation.php : graphe des liaisons entre fichiers

Posté par Xavier Teyssier (Jabber id, page perso, ) le 19 août 2004
0
Bonjour !



J'ai un "site" en php d'une soixantaine de pages, avec plein de variables et d'inclusions de fichiers.



J'aimerais me faire un graph qui me permette de savoir quel page appelle quelle page, quels variables sont définis où, etc.



Savez vous s'il existe déjà une petite appli permettant ça (je ne tiens pas forcément à ce que l'appli établissent les liens elle-mêmes. Je peux très bien rentrer toutes les informations à la main. C'est juste que sur PC, c'est plus facile pour modifier le graph lors de l'évolution de certaines pages...) ?

> Lire le message (4 commentaires, moyenne: 1,5).  

Vous avez demandé le commentaire #462553.

Une solution

Posté par bobert () le 21/08/2004 à 09:05. (lien). Évalué à 1.

J'ai trouvé ton problème intéressant et j'ai pondu un petit programme en python (php2dot) qui génère un graphe des relations d'inclusion entre fichiers php d'une hiérarchie de répertoires.

La subtilité, pour éviter de pourrir le graphe, c'est de regrouper en un même noeud les fichiers qui ont les mêmes inclusions.

Sinon une limitation fondamentale du programme c'est qu'il attend des fichiers aux noms distincts ; lever cette limitation demanderait pas mal d'efforts supplémentaires... moi je m'arrête là.

[ Répondre ]

  • [^]php2dot

    Posté par bobert () le 21/08/2004 à 09:06. (lien). Évalué à 2.

    #!/usr/bin/python
    # -*- coding: iso-8859-1 -*-
    #
    # Copyright (C) 2004 Nicolas Girard < bobert at dlfp dot org >
    #
    # This program is free software; you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation; either version 2, or (at your option)
    # any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program; if not, write to the Free Software
    # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    import os,sys,re
    from os.path import join
    from itertools import ifilter
    from sets import Set
    
    class Finder:
        """Parcourt une hiérarchie de répertoires à partir de startDirectory"""
        def __init__(self,startDirectory):
            self.walker = os.walk(startDirectory)
        def walk(self):
            while 1:
                root, dirs, files = self.walker.next()
                for file in files:
                    yield (root,file)
        def phpFiles(self):
            isPhp = lambda (r,f):f.endswith('.php') or f.endswith('.php3')
            return ifilter(isPhp,self.walk())
            
    def find(dir):
        return Finder(dir)
        
    class PhpFile:
        include = re.compile(".*include\s*\(['\"](\S+)['\"]\)\s*;")
        def __init__(self,dir,name):
            (self.dir,self.name)=(dir,name)
        def includes(self):
            f=open(join(self.dir,self.name),'r')
            lines=f.readlines()
            f.close()
            for line in lines:
                if PhpFile.include.match(line):
                    yield PhpFile.include.match(line).group(1).split('/')[-1]
            
    class Node:
        def __init__(self,name,labels=[],children=Set()):
            (self.name,self.labels,self.children,self.nodes)=(name,labels,children,None)
        def mergeWith(self,node):
            self.labels+=node.labels
        def label(self):
            return "\\n".join(self.labels)
        def edges(self):
            return "\n".join(map(lambda child,s=self:"%s -> %s"%(s.name,child.name),self.children))
        def __eq__(self,other):
            return self.name==other.name
        def __str__(self):
            return "%s [label=\"%s\"]\n%s"%(self.name,self.label(),self.edges())
        def rank(self):
            """calcule le nombre de parents du noeud"""
            return sum(map(lambda o,s=self,:s in o.children,self.nodes))
        def isExcluded(self):
            return self.rank()==0 and len(self.children)==0
            
    class Graph:
        def __init__(self,ranksep=2,nodesep=0.5,minlen=1.5,concentrate=True,nodes=[]):
            self.nodes=nodes
            self.head="""
    digraph Schema {
      node[shape=box];
      concentrate=%s;
      ranksep=%f;
      nodesep=%f;
      edge[minlen=%f];\n"""%(concentrate,ranksep,nodesep,minlen)
            self.foot="}\n"
        def append(self,node):
            node.nodes=self.nodes
            self.nodes.append(node)
        def __getitem__(self,nodeName):
            res=filter(lambda node,n=nodeName:node.name==n,self.nodes)
            if len(res)==1:
                return res[0]
            else:
                return None
        def checkChildren(self):
            """On supprime les fichiers inclus ne correspondant pas à un fichier trouvé
            et les doublons"""
            for node in self.nodes:
                node.children = filter(lambda ch,fath=node:ch and fath!=ch,
                  map(lambda c,s=self:self[c],node.children))
        def removeExcluded(self):
            """supprime les noeuds exclus du graphe"""
            for excluded in filter(Node.isExcluded,self.nodes):
                self.nodes.remove(excluded)
        def clusterize(self):
            """Regroupe les fichers non-inclus par d'autres, et incluant les mêmes fichiers"""
            nodes = filter(lambda n:n.rank()==0,self.nodes)
            for node in nodes:
                (node.clustered,node.removed)=(False,False)
            for node in nodes:
                ch = node.children[:]
                for other in nodes:
                    if other == node or other.clustered or other.removed:
                        continue
                    if ch==other.children:
                        node.mergeWith(other)
                        node.clustered=True
                        other.removed=True
            for node in filter(lambda n:n.removed,nodes):
                self.nodes.remove(node)
        def __str__(self):
            for i in range(len(self.nodes)):
                self.nodes[i].name="node%d"%i
            return self.head+"\n".join(map(str,self.nodes))+self.foot                    
            
                
    usage="""
    php2dot - Génère un script dot des relations d'inclusion entre fichiers PHP
         
    Syntaxe: php2dot startDir
        
      startDir : répertoire de recherche des fichiers PHP.
                 La recherche est récursive.
                 
    Synopsis:
      php2dot startDir > graph.dot
      dot -Tpng -ograph.png graph.dot
      
    Limitation:
      Les noms des fichiers PHP trouvés doivent être distincts
      
    """
            
    if __name__=="__main__":
        if len(sys.argv)==1:
            print usage
            sys.exit(0)
        startDir=sys.argv[1]
        graph = Graph()
        for (dir,name) in find(startDir).phpFiles():
            phpFile = PhpFile(dir,name)
            if not graph[name]:
                graph.append(Node(name,[name],phpFile.includes()))
        graph.checkChildren()
        graph.removeExcluded()
        graph.clusterize()
        print graph
    

    [ Répondre ]