Quantcast
Channel: blog.humaneguitarist.org » PHP
Viewing all articles
Browse latest Browse all 3

an improved PHP includes to DOT graph script by Carl

$
0
0

Almost two years ago, I posted this about making a DOT graph by linking PHP files via include() and require() commands.

The idea was to have a simple way to visualize dependencies of one PHP file to another, etc. So I wrote a little script to try and do that.

The idea of having a blog is, of course, about sharing information and putting things out there. But it's always better when people write in and comment on a post, share their ideas, and sometimes their code!

Carl wrote in last year with an improved script to make the DOT file. Unfortunately, because the indentation didn't get completely preserved by the WordPress comments, Python was returning errors in regard to the indentation.

For a long time I kept meaning to message Carl and ask if he could send along the script via an email attachment, so I could re-post it.

Well last night I decided to finally email him and this morning I woke up to a reply with the script attached.

The script still works as it did before in terms of command line arguments.

$ python analyseRequires.py path-to-my-PHP-script-folder 1 my-output-file.dot

You can read the former post to learn more about the arguments.

Anyway, thanks a bunch to Carl for sharing his code and for reading the blog.

I've pasted the code below with Carl's original notes about the changes he made.

# analyseRequires.py

"""
* removing duplicates
* working with absolute paths to identify file identities even if references through different relative paths
* improved regular expressions (sometimes the first letter of a file was missing)
* simplified file existence verification (to respect php's simple fallback for includes: if the path doesn't exist, the working directory is tried and the directories in the php.ini's include_path) 
"""

#####
#importing modules
import glob, re, sys, os, fnmatch
br = "\n"
tab = "\t"
#regex components
anything = ".*"
quotes = "(\"|')"
dependency = "(require|require_once|include|include_once)"
#####
#exiting if all 3 arguments are not passed via command line
def fail():
    print ("ERROR: " + str(len(sys.argv)-1) + " of 3 required arguments provided.")
    sys.exit()
 
 
#####
#getting arguments passed via command line
    
#testing for root DIRECTORY string
try: myDir = sys.argv[1]
except: fail()

rootDir = os.path.abspath(myDir)+"/"

#testing for RECURSION boolean
try: myRec = sys.argv[2]
except: fail()
 
#testing for OUTPUT filename string
try: myFile = sys.argv[3]
except: fail()
 
 
print "--------------------------------------------"
print "gathering files"
print "--------------------------------------------"

#####
#making list of PHP files within DIRECTORY
if myRec == "0": #without recursion
    myDir2 = myDir + "/*.php"
    PHP_list = glob.glob(myDir2)
elif myRec == "1": #with recursion
    PHP_list = []
    for dirname, dirnames, filenames in os.walk(myDir):
        for filename in filenames:
            if fnmatch.fnmatch (filename,("*.php")):
                match = os.path.join(dirname,filename)
                PHP_list.append(match)
 
for p in PHP_list:
    print os.path.abspath(p)

print "--------------------------------------------"
print "searching includes"
print "--------------------------------------------"

#make an empty list;
#tuples will go in the list;
#each tuple will contain a PHP filename and a PHP filename it includes
includeList = set()
 
#iterate through each PHP file and place tuples in the list
for phpFile in PHP_list:
    fileOpen = open(phpFile, "r")
    print "processing " + phpFile
    #for each line in a PHP file
    for line in fileOpen:
            m = re.match(anything+dependency+anything+"\((.*)\)", line)
            if m:
                inBracketExpression = m.group(2)
        m = re.match(".*"+quotes+"(.*)"+quotes, inBracketExpression)
        if m:
           targetBasename = m.group(2)
        else:
           continue

        # print tab + targetBasename

                if targetBasename[-4::] == ".php": #only PHP files
                    source = os.path.abspath(phpFile)
            source = source.replace(rootDir,"")
            # the include statement checks the path given, otherwise goes for the current working directory and finally checks the                 include_path in the php.ini
            # assume that the given path is valid
            targetFile = os.path.abspath(os.path.dirname(phpFile)+"/"+targetBasename)
            # if it is not try simulating php's fallback to current working dir methods
            if( not os.path.exists(targetFile)):
            print tab+" >>> ! >>> "+ targetFile + " doesn't exist, using"
                targetFile = os.path.abspath(rootDir+targetBasename)    
            print tab+targetFile+" instead"
                if( not os.path.exists(targetFile) ):
                print tab+"no success. skipping include."
                continue
            targetFile = targetFile.replace(rootDir,"")
                    includeList.add( (source, targetFile) )
            else: pass
 
print "--------------------------------------------"
print "writing output"
print "--------------------------------------------"

#creating DOT file
dot = open(myFile, "w")
 
#writing to DOT file
dot.write("digraph dependencies {" + br)
for dependency in includeList:
    print dependency
    dot.write(tab)
    dot.write("\"")
    dot.write(dependency[0])
    dot.write("\"")
    dot.write(" -> ")
    dot.write("\"")
    dot.write(dependency[1])
    dot.write("\"")
    dot.write(";")
    dot.write(br)
dot.write("}")
dot.close()
 
 
#####
#exiting
sys.exit()

Viewing all articles
Browse latest Browse all 3

Latest Images

Trending Articles





Latest Images