-
Speaking at OSCON 2008 April 2nd, 2008
The OSCON 2008 website has published its talk schedule. I’ll be giving two talks at OSCON this year; one on the Python track and one on the PHP track.Supervisor as a Platform
I will quickly introduce you to Supervisor and the immediate benefits of running your server processes under it. We will then dive into how applications written specifically for Supervisor can take advantage of it as a platform — writing your own event listeners to observe Supervisor and the process lifecycle, controlling with XML-RPC, and extending the Supervisor core with your own Python extensions.
This will be an expanded version of the talk I gave with Chris McDonough at PyCon 2008. Since PyCon, there’s been quite a few interesting developments in Supervisor like the ability to extend
supervisorctland progress made on configuration reloading. We’ll touch on these as well, so if you attended the PyCon talk there will still be new and interesting material in this talk for you.Integration Testing PHP Applications
While more PHP developers are recognizing the importance and benefits of unit testing, the uptake of PHP developers using automated integration or acceptance testing is relatively slow. This testing is equally crucial to maintaining the integrity of applications.
I’m going to help get you started testing at the application level with practical tips and source code. We’ll look at how to structure your HTML markup so it’s more easily testable, making tests easier to write and maintain with CSS selectors, organizing your tests, and testing with or without a browser.
We write a lot of PHP applications with this kind of testing at Maintainable. Before the conference, we plan to release some open source PHP code to help you test that we’ll cover in the talk as well. I’d also suggest you check out Sebastian Bergmann’s tutorial session on PHPUnit’s integration with Selenium RC.
-
New Rails for PHP Developers Website February 18th, 2008
Rails for PHP Developers is a new website that’s a companion to the new book by the same name. Like the book, it’s aimed towards PHP developers who have an interest in Rails and Ruby. The website features articles that alternate between Ruby and PHP focus, so PHP developers that aren’t interested in Ruby should still find it useful.
-
Horde/Yaml 1.0 Released January 8th, 2008
Horde/Yaml is a PHP 5 library for easily working with YAML data. This is the package’s first stable release.
Chuck Hagenbuch started the library as an adaptation of Spyc around six months ago. Since then, he and I have been quietly using and improving it. Along the way, we fixed many issues, added support for pecl/syck, and wrote a test suite with PHPUnit.
There are a couple of other libraries also derived from Spyc, notably the
sfYamlclass from the Symfony framework. Since these efforts also found and corrected issues, we incorporated as many of these fixes as we could find and added them to the test suite as we went along.At Maintainable Software, we frequently use YAML files for configuring our custom applications because our clients tend to like the format more than the alternatives. We’ve been using Horde/Yaml successfully for quite some time so we think it should generally work well for you also.
There’s a nice tutorial on working with YAML in PHP 5 over on the new Rails for PHP Developers website. It includes everything you need to get started with Horde/Yaml.
-
Zend/PHP Conference 2007 September 23rd, 2007
I will be presenting again this year at the Zend/PHP Conference, together with Sebastian Bergmann and Matthew Weier-O’Phinney.
PHP development by teams of developers introduces challenges beyond the mere coding of great algorithms. Agreements are needed about tools, specifications, architecture, design patterns, coding standards, testing, documentation, source code control, staging and deployment. This tutorial session aims to introduce developers to a set of best practices that will help them deliver applications fast and move them to deployment with confidence.
We will present our full-day tutorial session, “PHP Development Best Practices”, on October 8th. I really enjoyed last year’s ZendCon and I look forward to this one.
-
New in Horde: Routes September 15th, 2007
I’m pleased to announce the first release of Horde/Routes, a new URL mapping system for PHP 5. This package provides classes for mapping URLs into the controllers and actions of an MVC system, inspired by Ruby on Rails.
There are already quite a few existing libraries that do this sort of thing for PHP. Horde/Routes is a compelling alternative.
At Maintainable, we examined most of these PHP solutions and found them all inadequate for various reasons, particularly because we wanted RESTful routing, named routes, sophisticated matching, PHP 5 E_STRICT, and extensive test coverage.
Since we do quite a bit of Ruby and Python programming, we surveyed the options in those languages and decided to do a full port of the Python library, Routes.
Horde/Routes provides these features and more:
- Supports route recognition and generation
- Sophisticated matching conditions like subdomains
- Named routes and RESTful route generation
- PEAR-style naming and coding standards
- PHP 5 E_STRICT compliant, web framework agnostic
- A comprehensive unit test suite
Maintainable decided to contribute the code to Horde’s Rampage project because Horde is one of PHP’s oldest and most successful projects. We’re using and contributing to other Horde libraries and we think Rampage is worth your attention.
The Python version has been around for some time and is very popular with different Python web frameworks. We’re happy to be part of that ecosystem now and our full port has already resulted in patches being committed back to the Python version.
At Maintainable, we’ve already used Horde_Routes on several applications. While Horde/Routes is relatively new, it is very feature-rich and well-tested.
Currently, Horde/Routes is a beta release. Over the coming weeks, we’ll be making some minor changes to the API and adding more documentation, and then it will quickly move to stable.
Update: The project now has its own pages on the Horde website and was featured on Chuck Hagenbuch’s blog.
-
Better PHPUnit Group Annotations September 4th, 2007
Last week, Sebastian Bergmann wrote about the new support for TestNG-style grouping of tests in the upcoming PHPUnit 3.2. This feature allows individual test methods to be grouped with an
@groupannotation.At my company, we typically organize our test case classes into high-level groups such as
unitandfunctional. Method-level group annotations are inconvenient for us because we’d need to annotate every method of every test case class.I was discussing this with Sebastian and not long after he had committed changesets 1293 and 1294 to the PHPUnit repository.
Now,
@groupannotations on the class inherit to all the methods of that class. When all the test methods of a class belong to the same group, just annotate the class:/** * @group functional */ class FooTest extends PHPUnit_Framework_TestCase { ...
With that one annotation to the class, a command like this one will run all the tests in the class above and any other tests annotated with
@group functional:$ phpunit --group functional AllTests.php
That’s a great shortcut. It shouldn’t take long to put
@groupannotations on the test case classes of a project.Having the ability to annotate individual test methods is still very useful because test methods (and even classes) may belong to multiple groups.
Sebastian pointed out a great use case for this in the form of testing bugs that span multiple test case classes. I was already commenting my tests with bug tracker numbers, now I’ve just converted them to
@groupannotations.Whenever we receive a bug report from a client, we will create tests to reproduce the bug and then fix the implementation so the tests pass. This ensures that the bug can be reproduced and that we won’t repeat the same mistake again later.
Now, the test methods associated with a bug can be annotated like
@group bug42. Runningphpunit --group bug42 AllTests.phpwill run only the tests associated with bug #42, regardless of what files and groups those test methods span.PHPUnit 3.2 is looking great.
-
Faster TDD with Stakeout.rb September 4th, 2007
I’m a big fan of Autotest and it runs almost constantly on my machine. Autotest automatically reruns your tests whenever your files change. Instead of constantly flipping to another shell to rerun your tests, just let Autotest cheerfully do it for you in the background. It’s highly addictive.
The only problem with Autotest is that it is specific to Ruby. At work, I do a mix of different kinds of programming including Ruby, PHP, Python, and C. I’d like my TDD to be accelerated for all of these languages.
Thanks to Geoffrey Grossenbach, last week I came across stakeout.rb from Mike Clark. This is a tiny, dead simple Ruby script that runs an arbitrary command when certain files change. This is a stripped-down Autotest for everybody else. I’m sure it has all kinds of other uses as well.
To get started testing with
stakeout.rb, you’ll need Ruby installed. Any recent version is fine and you might already have it installed. Next, grab the stakeout.rb script and add the shebang line to the top (Unix-like OS assumed):#!/usr/bin/env ruby -w if ARGV.size < 2 puts "Usage: stakeout.rb <command> [files to watch]+" ...
Make the file executable and put it somewhere in your
PATH. You can test it out by typingstakeout.rbfrom an arbitrary directory and you should see the help message.Next, change over to a project directory where you have some test files. Most of the projects that I am involved with tend to use some directory structure similar to this:
/project_name /lib /test ...
To test such a project, run
stakeout.rbfrom the/project_namedirectory. Most PHP projects using PHPUnit tend to have anAllTests.phpfile or equivalent to run all the tests, so we’ll assume this for the example:project_name$ stakeout.rb "php test/AllTests.php" **/*
The first argument is what command to run when the tests change. The second argument, and any subsequent arguments, are the files to watch for changes. These can use a Ruby globbing pattern. The pattern
**/*will watch all files underproject_namerecursively, which includeslib/andtest/.Once
stakeout.rbis run, it will show no output but will sit and wait for changes. As soon as you change a watched file,stakeout.rbwill automatically rerun you tests and will continue to do so until you exit with Control-C. -
Wrapping PHP Functions for Testability August 1st, 2007
One of the problems that hampers the testability of PHP code is the coupling created by accessing all of the PHP global functions. This happens often because a large number of useful extensions are accessed only through global functions. Consider the following code snippet:
$res = ldap_connect($host, $port); if (! $res) { // error logging return false; }
There are two code paths shown above: the connection succeeding, and it failing. Both of them are very difficult to test because of the coupling to the global function
ldap_connect()provided by the LDAP extension.To make it succeed, you’d need an LDAP server. Causing it to fail is easier but the could take a very long time until the connection timeout occurs. Also, the code can’t be tested at all without the LDAP extension. All of these problems are unacceptable.
The solution is to use to the extension through an object instead of calling the extension function directly. This way, we can inject either the extension wrapper or a mock object for testing.
However, writing these wrappers and maintaining them can be a pain and this is often the rationale given for not using them. There’s an easy answer to this excuse:
class ExtensionProxy { protected $ext; public function __construct($ext) { $this->ext = $ext; } public function __call($method, $args) { return call_user_func_array("{$this->ext}_{$method}", $args); } }
Since most PHP extensions prefix all of their functions with the name followed by an underscore, it’s easy to wrap them with something like the class above.
There’s some performance penalty from
call_user_func_array()in the above example but you can always write out a class later if that ever actually becomes a problem. Meanwhile, it can get you going very going quickly.Our connection example then simply becomes:
$ldap = new ExtensionProxy('ldap'); ... $res = $ldap->connect($host, $port); if (! $res) { // error logging return false; }
The difference in usage is trivial but this version is easily testable. It now depends only on an
$ldapinstance, which the class needing LDAP can receive in its constructor. To test, now just pass a mock object for$ldap.The technique of putting lightweight wrappers around PHP extension functions has been around for a long time. For example, Horde has a small wrapper around the IMAP extension for testing.
The continued improvements in PHP 5 allow for simple tricks like the
ExtensionProxyabove, and advances in tools like PHPUnit are making tests increasingly convenient and practical.Whatever methods you choose, there really is no excuse for untested (or untestable) PHP code these days. I consider anything without good tests to be broken and you should also.
-
Maintainable Software Hiring PHP Developer April 5th, 2007
Maintainable Software is now hiring a PHP developer. This is a full-time, salary position. We are headquartered in the San Francisco Bay Area but this is a telecommute position. We don’t care where you are as long as you are a talented developer and passionate about creating great software.
You should have:
- Strong understanding of XHTML, CSS, and standards-based design
- Object Oriented Programming experience and familiarity with testing
- Knowledge of key differences between PHP 4 and 5 and have used PHP 5
- Some experience with XML and web services
- Familiarity with MySQL and be able to write SQL queries
- Understanding of source control including branching and tagging
- Working knowledge of the UNIX shell and configuring Apache with PHP
- Strong work ethnic, be a team player, and have excellent references
Maintainable is a great company with some nice perks. You’ll work with other top notch developers on a variety of different projects, get exposure to new technologies, and even a MacBook Pro to keep you happy while you develop.
For more about the position and how to apply, please visit the careers section of the Maintainable website.
Update: The position has been filled. Thank you to everyone who applied and please continue to watch here for future openings.
-
Easier XML-RPC for PHP 5 February 19th, 2007
A few weeks ago, I rewrote the Zend XML-RPC client as part of a customer project at Maintainable. After fixing a fair number of bugs and writing a test suite, I made some enhancements and usability improvements. You can learn more about the new XML-RPC client from its documentation. Here it is in a nutshell:
Calling Remote Methods
The new XML-RPC client has always functioned similarly to many existing PHP implementations, providing a call() method:
$c = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc'); echo $c->call('test.sayHello');
The
call()instance method accepts an optional parameter with an array of parameters to marshal to the remote method. These may be native PHP types or PHP objects representing XML-RPC types. The latter is useful for the XML-RPC datatypes that do not map directly to PHP equivalents, such asbase64.Server Proxy Objects
The above usage works fine for many purposes but could read easier and gets tedious after many method calls. One of the few advantages of serializing method calls with a protocol like XML-RPC or SOAP is that with a little extra work in the client libraries, the remote service can be exposed in a way that’s very close to a native PHP object. This is where the server proxy comes in.
In the above example of
test.sayHello(), the remotesayHello()method is in the XML-RPC pseudo-namespacetest. We can use the new XML-RPC client’sgetProxy()method to get a proxy to this remote namespace and then use it similarly to a normal PHP object.$c = new Zend_XmlRpc_Client('http://framework.zend.com/xmlrpc'); $test = $c->getProxy('test'); echo $test->sayHello();
Namespaces may be nested to any depth so the XML-RPC method
foo.bar.baz()becomes$foo->bar->baz().Faults
Faults resulting from the remote method call are automatically thrown as PHP exceptions. XML-RPC fault responses are thrown as
Zend_XmlRpc_FaultExceptionand transport errors are thrown asZend_XmlRpc_HttpException. If this is not desirable for some reason, adoRequest()method provides a way to work with request and response objects directly.

