In the development of things, there comes a point when it escapes the vision and control of one man/one mind. PHP frameworks are such ... beasts. But the simplicity a machine took away can be made to return. And such an attempt at zooming out of the complex file structure bureaucracy of most php projects was inclued.
When I hacked up that extension, nearly a year back, I wished that it would shame at least some php programmers into writing better code. And slowly, thanks to a few slides from Rasmus, people are actually slowly realizing how messy their include hierarchy really is. And here's an example of what I'm talking about.
That was the Zend Framework 1.5.2, as blogged by phpimpact - download the big one and look at it. The joomla CMS has also got its very own pretty picture elsewhere. Rasmus has a bunch of inclued traces from various frameworks - CakePHP, Symfony, Drupal, and perhaps the cleanest of them all, CodeIgniter.
Now, all that remains is a php-graphviz + svg mode which renders these in-browser as an iframe - or maybe someone can help me with the graph reduction to take a collection of the inclued dumps & create a "package". There's none an end to the bells and whistles I want to tack onto this.
But as long as people are scrambling head over heels to reduce the number of includes & include_onces, I think I've done my part here.
--Must I hold a candle to my shames?
-- William Shakespeare, "The Merchant of Venice"
After procrastinating for nearly two weeks with the code nearly done, I've managed to find the energy (and some caramel coffee) required to fix it up for the public to use - and here it is. In the process, I also threw out all the ZendEngine2 hacks and started to use zend_user_opcode_set_handler, which should let people use this with the faster CGOTO vm core, though I would advise against using that just yet.
The new & improved inclued can dump out class inheritance dependencies (though not the interfaces, as of now). This gives a slightly bigger picture view of what files depend on what other files and provide a tree of the classes clustered into their own files. For example, this is the graph pulled out from the relatively minimal PEAR::HTML_QuickForm2 library.
The usage is as before, the gengraph.php script now has a -t option which will accept either "classes" or "includes". At the very least, it should help people documenting OOP php code. Next up are interface implementations, the data is already in the dumped files, but not output in any human readable format.
--It is a very sad thing that nowadays there is so little useless information.
-- Oscar Wilde
Finally, I got bored enough to update my inclued extension (as promised at OSCON). The extension now comes with a nearly completely non-intrusive data dumping mode. The new inclued.dumpdir can be used to dump the inclued data onto a temporary file without ever modifying any of your php scripts. Also included is some php code to transform the dump data into graphviz formatted .dot files.
Pick up your free & complementary copy of the source code on your way out. And stay clued-in about your includes.
--This quote intentionally not included.
Previously while talking about inclusion checks I had included a few helpful digraphs of php includes. Those were drawn with the help of a gdb macro and a bit of sed/awk. But that becomes a real hassle to actually use very quickly while inspecting fairly large php applications.
The solution to repeatable include graphs from php came from the way the include_once override hack was implemented. By overriding the INCLUDE_OR_EVAL opcode in Zend, I could insert something which could potentially log all the various calls being made to it.
That is exactly what inclued does. The extension provides a single function which provides a copy of the collected data, aptly named inclued_get_data().
<?php include("x.php"); print_r(inclued_get_data());
The above peice of code returns an array with too much information.
Array ( [includes] => Array ( [0] => Array ( [operation] => include [op_type] => 2 [filename] => x.php [opened_path] => /tmp/x.php [fromfile] => /tmp/z.php [fromline] => 2 ) ) )
Overriding the opcode implies that more information can be collected, like whether the include parameter was a constant (i.e APC can optimize it to a full path), the function the include was in (__autoload *cough*) and the stream wrapper (phar: for instance). The information also includes info about whether the include_once was ignored because of duplication.
Only Data: The extension however does not make any judgements on the code. I have resisted the temptation to put an error log saying "If eval() is the answer, you're almost certainly asking the wrong question" . But there are interesting ways to massage this data into something useful. For example, I just put the following lines into the Wordpress index.php.
$fp = fopen("/tmp/wp.json", "w"); fwrite($fp, json_encode(inclued_get_data());
Loading the JSON data and pushing out a graphviz layout was almost too easy. Here's a clipped section of how my graph ended up looking.
The graph shows about 40-odd includes being hit for a single request, but isn't messy at all. Hopefully, someone finds an actual peice of sphagetti includes which shows off the beauty of this ext (remember, this shows include_once misses & hits).
Hope this helps people debug "Where is this include coming from ?" question I've run into so many times :)
--It was the kind of mental picture you tried to forget. Unsuccessfully.
-- Terry Pratchett, "The Light Fantastic"