Yesterday I set up to write unit tests for a few PHP classes I'm refactoring. Given my usual tendency to set aside serious tasks and concentrate on eye candy, after a few tests I decided to write my own test runner (something I've done at least twice before, never storing away the code for later use).
My test runner builds on a few assumptions:
The main test runner features are:
I made a few screenshots, showing a collapsed view of all available suites, the same for a single suite, with a warning indicator, and one suite expanded, showing test cases results and warnings.
The DynamicTestRunner code only needs to include the PEAR version of PHPUnit. Using a templating engine would have made the code more understandable, but at the cost of introducing a dependency.
If you want to preserve the option of being able to run each test file on its own, you can test if it has been called by testRunner with a simple if statement at the end of each test file (substituing of course testEntities with the name of the class declared in the file):
if (!isset($_SERVER['SCRIPT_FILENAME']) || $_SERVER['SCRIPT_FILENAME'] == __FILE__) { $suite = new PHPUnit_TestSuite('testEntities'); $result = PHPUnit::run($suite); echo $result->toString(); }
Since it was announced on the PEAR newsgroup a few years ago, I have been using the Var_Dump class by Frederic Poeydomenge as one of my favourite PHP development tools. Dumping variables at strategic places has always been a practical and effective (if a bit simple) method of debugging in PHP and other languages. Unfortunately, when you're working with complex data or with class instances, using the echo construct or the var_dump function is not very useful, unless you go to great lengths to extract what you really need to display from your data.
Var_Dump is a class that assists you in exactly this task, extracting all available information from your data and displaying it in a variety of ways. It is the PHP analogous to the Python pprint module, and one could spend an interesting few minutes comparing the two very different PHP and Python solutions to dumping (or pretty printing, which is more what's happening here) data.
In its simplest form, the Var_Dump class can be used through a single static method, and a few optional constants. At the other end of the scale, a Var_Dump instance can be customized through skins, color sets, and display options to tailor its output to your exact needs. In this brief document, we will show examples of its use through a static method, since it's the one you most probably will resort to during development. More complex examples can be found on the author's site.
We start by importing the class code, and setting up a few variables and a class instance, that we will feed to Var_Dump.
require_once 'Var_Dump.php'; $a = array(0,1,2,34); $b = array('a'=>'something', 'b'=>0, 0=>'b', 'c'=>$a); class B {} class A extends B { var $a; var $b = 1; function a($a) { $this->a = $a; } } $a_inst = new A(2);
Once we have that out of the way, we start feeding our data to Var_Dump by calling its display method as a static class method. First the simple array, without passing any extra argument to display.
Var_Dump::display($a);
Our friendly class spits out the following output:
0 Long (0) 1 Long (1) 2 Long (2) 3 Long (34)
Nice, isn't it? Ok, let's start using the optional arguments to display, and feed it something a bit more complex.
Var_Dump::display($b, 'b');
The second argument we passed to display in this example is the name of a key not to display, mainly used to avoid printing deeply nested data structures. As we can see from the following output, Var_Dump does a remarkable job of printing multidimensional arrays.
a String[9] (something) b Not parsed () 0 String[1] (b) c Array(4) + 0 Long (0) | 1 Long (1) | 2 Long (2) + 3 Long (34)
On to the class instance, and the third argument you can pass to display.
Var_Dump::display($a_inst, null, VAR_DUMP_DISPLAY_MODE_HTML_TABLE);
As you have probably guessed, the third argument controls the output generated by Var_Dump through a few self-explanatory constants:
The output of our instance is pretty interesting, as Var_Dump squeezes all available bits of information from it, including its class and its base classes, if any.
|
The fourth, and last of the arguments you can pass to display is again a constant from a self-explanatory set:
Let's see what happens if we feed our instance to display, by using the VAR_DUMP_NO_CLASS_INFO constant, and reverting to the simpler output.
Var_Dump::display($a_inst, null, VAR_DUMP_DISPLAY_MODE_HTML, VAR_DUMP_NO_CLASS_INFO);
Class name String[1] (a) Parent class String[1] (b) Object vars Array(2) + a Long (2) + b Long (1)
That's all for tonight, if you have suggestions for PHP topics you would like to read here, drop me a message.
I noticed from my logs that somebody using feedonfeeds is subscribed to my blog. Feedonfeeds is a nice PHP aggregator/reader, written by Steve Minutillo.
After shopping around for a while for a news aggregator, I settled on feedonfeeds a couple of months ago for a couple of reasons:
So I installed feedonfeeds, and staying up to date has become a joy.
Never satisfied, after a couple of days of use I decided I absolutely needed a few features that feedonfeeds was lacking. So I set about refactoring it, and the project turned out to be a full rewrite. As usual with these things, the result is a half-completed app, so that I now have the (minor) features I wanted, and lack most basic ones feedonfeeds provides out of the box. =)
My rewrite is template-based, and factors out most common operations (eg SQL statements etc) in a few classes, instead of using lots of functions. So far, I have:
update: I finally managed to add the most important missing functionality to my feedonfeeds rewrite, and package it.
As promised in a previous entry, I managed to give a look at PHPTAL. Since the entry is a bit long, I copied it as a standalone article, and left only this brief notice on the blog.
Read with interest today A Few Tips for Writing Useful Libraries in PHP (via PHP Everywhere), a nice article by the author of the MagpieRSS parser.
Posted a couple comments to the site, to which the author kindly replied by email. Nothing much, the usual "don't name your php files with an extension other than the one defined in your webserver", and an observation about error handling.
As all PHP developers know, PHP has no exceptions (and thus no try/something clauses), errors are either stored in special variables or accessed by calling functions, depending on what extension you're working with (PHP5 will introduce exceptions). All pretty messy, as the article author correctly points out.
One thing most developers don't know is that PEAR (the default PHP library bundled with the PHP source code) has a nice PEAR base class with great error handling features (why a good number of PHP developers stay away from PEAR is another matter entirely from the topic of this entry).
Basically, if your classes (you use PHP's OO features when writing reusable code, don't you?) inherit the PEAR base class, you get error handling for free (and a few other things, like destructors).
Using PEAR error handling is very easy, a quick example will suffice:
// our imaginary library (not very useful, is it?) define('MYLIB_ERROR', 100); require_once 'PEAR.php'; class myClass extends PEAR { function myClass($myargs = null) { $this->PEAR(); } function &raiseError($message, $method, $line) { $error = PEAR::raiseError(sprintf("%s.%s() line %d: %s", get_class($this), $method, $line, $message), MYLIB_ERROR); } function myMethod() { $this->raiseError("hmmm something went wrong.....", 'myMethod', __LINE__); } }
For our libray developer, that's all there is to it. Basically you import the base PEAR class, and use it as a base class for your library classes. Whenever you need to "throw" an exception, just raise a PEAR error. The raiseError method is what I use in my classes, makes much easier debugging what went wrong by appending the method name and the line number where the error occurred, and a constant I use to trap errors depending on the originating classes.
Let's see the user part of PEAR's error handling routines:
// user code accessing our library PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'errorHandler'); function errorHandler($err) { echo("<b>PEAR error</b><br>message: <i>" . $err->getMessage() . "</i><br>user info: <i>" . $err->getUserInfo() . "</i><br>"); } // or use any of the standard PEAR error handlers, eg // PEAR::setErrorHandling(PEAR_ERROR_PRINT); // for development or a more sophisticated handler for production, // eg mailing a copy of the error or saving it in a log $myinst =& new myClass(); $res = $myinst->myMethod(); if (PEAR::isError($res)) { // do something, the error is handled // (in our case printed) by the handler routine } else { // do something else }
The user only needs to import the PEAR base class (or alternatively use $myinst->isError() since it extends PEAR) and check method return values for possible errors. An added benefit of using PEAR's error handling routines is that by changing the function used to handle errors you can switch from debugging (ie print verbose error messages) to production (ie don't let the user see errors, handle them in your application) just by changing the error handling routine. A common practice is to automate this sort of things by checking the environment of the running server (eg hostname, etc.).
I've been thinking of writing a TAL|METAL package for PHP for some time, to replace my own version of the aging PHPlib Template class (no I don't like Smarty or other tamplating packages for PHP).
Yesterday I stumbled upon PHPTAL, which looks good and is even a PEAR package. Will try it soon.