I’ll be Reviewing “Zend Framework 1.8 Web Application Development” by Keith Pope
A Marketing Research Executive from Packt Publishing contacted me several days ago about reviewing a recently (September 2009) released book by Keith Pope entitled Zend Framework 1.8 Web Application Development. I am honored to be considered a developer with enough knowledge of the Zend Framework to offer my thoughts and opinions on such a book. Since I already read Mr. Pope's blog I've followed along during the writing process and cannot wait to read the actual book.
Keith posted a sample chapter of the book on his blog if you are interested. Otherwise, stay tuned for my review!
The Modern PHP Workflow
Over time we have all seen projects that become great success stories and our fair share of projects that become epic failures. Some of this can be blamed on the idea behind the project but I challenge you to consider that part of failure can be attributed to a lack of constant supervision of the project. This is not to say that any one person is in charge of ensuring a project is on target with the original goals, or even that a project manager is responsible for testing the project throughout development. Rather, I'd like you to consider the work flow used on those failed projects and compare it with a more modern approach to development. While this won't guarantee the success of every project, using proven tools and methods such as unit testing with PHPUnit, an automated build system such as Phing, and a continuous integration server such as Xinc will greatly improve the quality and timeliness of your future projects.
Preparations
In order to follow along with this article you will need to equip yourself with the proper tools. Luckily between pear and the pecl obtaining these tools is the matter of a few characters typed into the command line. Note that thorough installation instructions can be found on the websites for each of the tools which are http://www.phpunit.de for PHPUnit, http://xinc.googlecode.com for Xinc, and http://phing.info for Phing.
The Foundation: PHPUnit
Personally before I began my journey into the world of “Agile” development practices, specifically unit testing, I was already familiar with the basic concepts behind why it is a good idea but I was a little intimidated by the seemingly steep learning curve. If you're anything like me you have thought about the amount of additional time your projects will take because of the added burden of writing tests, you think that testing is something reserved for the extra weeks you tacked on to the schedule for this project and comes in the flavor of “Try out this application and see if you can break it.” or worse yet you think that your code is so impressively impeccable that you could not possibly benefit from writing these silly little tests. I am here to tell you that I was wrong in all of those assumptions. It took a while for me to realize the benefit and I will not try to convince you that it took no time at all to adapt my thought process to testing, but I can tell you that as a result I am more confident in my code, my projects are easier to maintain, and my clients and employers are much more satisfied with my work.
The toughest part of getting started with unit testing is deciding where to being your journey of learning how to test your applications. To help you along we are going to work together to develop an application to track the whereabouts of various items needed for school as a collegiate student. For this purpose we are going to assume that our student needs to track his notebooks, his textbooks, his planner, and his laptop which can be in his desk or in his backpack. Over the course of this example I may introduce a few bugs--some purposely, some otherwise--if there is any doubt in your mind about the code, assume it is for example sake.
Coupled with unit testing the technique of test-driven development is thrown into the mix and we're going drive the development of our application with testing. The principle behind test-driven development (TDD) is that we first write tests that describe how our code functions, more specifically how it is used, and then we write the code that allows the tests to pass. This might seem counter-intuitive at first, but we write failing tests first then make the code conform to our tests thus making our tests the contract to which we design our code. Coding the opposite direction generally leads to bending test code to fit the workings of your code base which in turn will lead to massive refactoring and potential code hernias in the future. If you aren't familiar with the term “code hernia” it is essentially a fragment of code that is in place that may be difficult to understand, most likely causes awkward use of the code base or API, and is only explained by lengthy comment blocks. The maintenance nightmare associated with such awkward code is exactly the type of problem we hope to solve by unit testing our code.
Object Oriented Power, err, Programming
I started working on integration between three libraries that were not meant to work together. Well, I should say they were not developed with this type of integration in mind. Naturally there were some roadblocks, but eventually I was able to overcome them and create what should be a decently elegant solution. But that's a post for another day. This post is targeted at developers searching for the purpose of Object Oriented Programming.
I suppose it's not a secret that I make liberal use of the Zend Framework. (It's nothing personal Symfony, CodeIgnitor, Django, etc., Zend Framework just fits my style!) Part of my integration experiment led me to ZF-7367 because I too needed to tell the front controller to return the response rather than output it. The first instinct is to hack up the copy of the Zend Framework to make this work. The patch suggested in the issue gets us part of the way there, but we also have to hack on the Zend_Application component including the Bootstrap implementation. This would make future upgrades a pain if this feature request is rejected, so it's obviously not the best solution.
Thinking about the problem a little deeper I immediately realized that I already extend the Zend_Application_Bootstrap_Bootstrap (yes, the naming scheme is quite verbose and suffers from a bit of redundant redundancy…) so it should not be too difficult to override the run() method to do what I want. Let's look at the original run() method inside Zend_Application_Bootstrap_Bootstrap.
/**
* Run the application
*
* Checks to see that we have a default controller directory. If not, an
* exception is thrown.
*
* If so, it registers the bootstrap with the 'bootstrap' parameter of
* the front controller, and dispatches the front controller.
*
* @return void
* @throws Zend_Application_Bootstrap_Exception
*/
public function run()
{
$front = $this->getResource('FrontController');
$default = $front->getDefaultModule();
if (null === $front->getControllerDirectory($default)) {
throw new Zend_Application_Bootstrap_Exception(
'No default controller directory registered with front controller'
);
}
$front->setParam('bootstrap', $this);
$front->dispatch();
}
All we really need to do here is configure the front controller instance to return the response object which is accomplished using the Zend_Controller_Front::returnResponse(bool) method. Inside the application bootstrap class we simply use this implementation:
public function run()
{
$front = $this->getResource('FrontController');
$front->returnResponse(true);
parent::run();
return $front->getResponse();
}
Now I have the response I need returned from the bootstrap file, but the Zend_Application::run() method does not return this value. Here's where what I've been rambling about should click. I merely have to override the Zend_Application::run() method with one of my own, like so:
class My_Application extends Zend_Application
{
public function run()
{
return $this->getBootstrap()->run();
}
}
This protects me from hassle when my copy of the Zend Framework needs updates or upgrades but still allows for the functionality I needed. In the index.php file I switch out all references to Zend_Application with My_Application and everything should roll.
