Professional Development with the Zend Framework: Unit Testing
Table of contents for Professional PHP Development
- Professional Development with the Zend Framework: Introduction
- Professional Development with the Zend Framework: Unit Testing
I started this serious with a very specific goal in mind. I wanted to highlight some of the qualities I find most useful and most important within the context of the Zend Framework code as a way to show developers how to look at other projects and build upon their own professionalism. One of the most important facets—in my opinion of course—of the framework's code base is something that is often overlooked: the comprehensive test suite.
Testing is most often regarded as a liability by managers because it does not provide any measurable value to a product in the eyes of anyone excepting developers. But the value is definitely measurable and quite obvious if one would take the time to observe project development through the maintenance phase and, quite possibly even more importantly, in multi-developer environments.
The Zend Framework Test Suite
In the coming sub-sections we will look at the aspects of the framework's test suite that I find most influential to its success.
Organization
The first thing to notice about the tests directory for the framework is the slim profile. These few files and directories are a testament to the organizational aspect of the suite. This is important because tests that are difficult to find or inappropriately named will not be maintained.
It is completely possible to not use the AllTests method for suite organization which in smaller projects might seem like a burden. But the use of these groupings allows different subsets of tests to be run for a given component with consistent state and configuration.
Configuration
Since there are developers working on various components, features, and bugs throughout the world it is essential that each be able to have a consistent environment in which to test. This test suite uses a configuration file, TestConfiguration.php, and a helper file, TestHelper.php, to configure the environment through PHP constants and set up include paths, etc., respectively.
Tests that can only be run on a specific machine are almost as useless as no tests at all.
Verbose Case Naming
Looking at an individual test file you will notice that the cases, or methods, are named to signify exactly what the test will assert. This is incredibly helpful when staring at code after a while. Using an IDE's outline functionality it is simple to find the test that verifies that a useful message is thrown if a class is not found by the factory because it is contained with the testStaticFactoryClassNotFound() method.
Use of Grouping Comments
PHPUnit uses comments to accomplish some meta-data and grouping functionality. Throughout the test suite you will run across individual test methods that have a @group annotation which, most of the time, references an issue in the issue tracker. For example a comment might look as follows for a test case that reproduces the issue ZF-2724.
/**
* Handle file not found errors
*
* @group ZF-2724
* @param int $errnum
* @param string $errstr
* @return void
*/
This is another way to run specific tests. Using the command-line test runner's --group switch you can specify the group of tests to run as follows.
phpunit --group ZF-2724 .
Which tests to run and which to skip can also be configured using the phpunit.xml configuration file.
Testing For Backwards Compitability
One of the Zend Framework project's goals is to ensure that no BC breaks occur except in major releases — this should be a common goal for all projects, but that's just my opinion. Throughout the test code you will find cases that both document and expect that the "old way" and the "new and improved way" of accomplishing something are tested.
This is an area often overlooked during major improvement cycles. I know I am guilty of changing something significant or deprecating a method, etc., and then updating my client code to use the new API but leaving other team members out in the cold. I didn't do it purposely, it was just a novice mistake.
The concept to take away is to not delete or rewrite a test case because you add a new API method (unless it's supposed to be a BC break, then by all means). Instead, write a new case to test the improved method while still maintaining the functionality of the older method. You will make the developers that use your code happy.
These are just a few of the many excellent qualities of the Zend Framework test code. I encourage you to share something you found and admire.
Coding to an Interface
In a recent user group presentation the topic of using interfaces to enforce coding standards, ensure component compatibility, etc. was touched upon. The inevitable discussion about the definition of coding to an interface commenced.
The Literal Definition
Most people when first introduced to the concept of coding to an interface or even "design by contract" type concepts take the literal definition. At this basic level the technique means to use your programming languages OOP interface to define the behavior of the objects you will be using and accept objects that implement that interface where necessary, as illustrated in the following listing.
interface Deleteable
{
//some methods
}
class Book implements Deleteable
{
//implementation
}
class BookDataSource
{
public function delete(Deleteable $book)
{
$this->purge($book);
}
}
With this arrangement any methods that are necessary to prepare an object for deletion will be implemented in the Book class and the data source will only allow objects that implement the Deleteable interface to be purged from existence.
This technique is employed to allow better unit testing, for polymorphism, to ensure that objects have certain behavior, and a variety of other reasons. Coding to the contract provided by the interface is a great practice.
An Expanded Definition
While the literal definition above is definitely accurate and valuable, the interface extends into other areas. In modern software projects there are a variety of resourced one can call upon to learn about the behavior and properties of software components. Each of these resources is a part of the contract and useful as an interface to which developers should code.
Documentation
The most obvious resource for defining behavior and properties is documentation. Whether this is some sort of specification document in a wiki, in a binder, on notecards, on napkins, etc., or code comments documentation is a useful interface as long as it is maintained. Maintenance is the pitfall of using documentation as a contract because it is so often and so easily neglected.
Because documentation is most likely out of date — hey, I code in the real world — it is my last choice for an interface-defining resource. However it's better than having nothing.
Unit Tests
Thinking about it, unit tests are the ultimate supplemental contract for code. They will immediately notify the developer if the terms of the contract (the assertions) are broken. They also provide usage documentation that will help integration efforts.
In Your Code
The message I hope to get across with this post is that the concept of design by contract or coding to an interface is broader than the literal implications. Using supplemental contracts for your projects will make your projects better if only for that day in the future when you wonder why you made the decision you did.
Working Outside Your Comfort Zone
No, I'm not going to inundate you with quotes of famous and not-so-famous people about how stepping out of your comfort zone will revolutionize your life. Instead, I'd like to simply encourage you to step out of your box every once in a while if for no other reason than the potential to learn something.
What is My Comfort Zone?
The absolute first step in this technique for improvement is to figure out where you comfort zone is. For a lot of the people the knee-jerk answer is in whatever language they are currently using. This is not a bad answer but it will most likely lead to a shallow experience. For example, my obvious comfort zone is in PHP development. To step outside my comfort zone, then, I'd merely need to read through someone else's project written in Python or even Ruby and then pat myself on the back for each concept or technique I can extract from such projects. However, doing so doesn't really answer the question what is my comfort zone and doesn't force me to get outside of it.
My real comfort zone could be working with a relational database or even using scripting languages. I could easily follow the noSQL revolution for a sample project or work on a project written in, e.g., .NET using C#. These technologies are so different from my normal tools that I'd be forced to think about problems and solutions in a different light.
Different Technologies, Different Approaches
Working with differing technologies is not guaranteed to help at all. In fact the experience can be extremely frustrating because you will come across innovative solutions that simply won't be possible in your "home" environment because of language or technology barriers. This is natural and should be expected. If this weren't the case we wouldn't have such a deep pool of languages to chose from.
Instead of focusing on the negative, keep digging into the project. Given enough time you should be able to find a few concepts from the new environment that you can either adopt or at least adapt into projects in your preferred languages and tools. In my case I inherited a few projects that are written in .NET. Instead of immediately grumbling about how awful it is to work in that environment, I've embraced it and gone on some bug hunting/fixing expeditions through the code. As a result I've learned quite a bit about how another developer uses a different technology to accomplish some of the same tasks.
Read Other Developers' Code
This leads me to the final benefit of stepping out of your personal playground: you get to read other developer's code. I don't remember where but I recently read a comment in which the author stated that it's silly for a developer to assume he or she can continue to expand his or her knowledge without reading peer code. He or she went on to say that this is like a person deciding to become an author without ever reading other works.
At first I resisted the notion that an author must read other's work before becoming a talented author. After all, it's quite possible that a person can be naturally gifted. However, the underlying meaning became apparent. We all must learn ways that work and some ways that do not. Without seeing a variety of approaches there will be no growth. This does not mean that you must immediately adopt what you read. You should look for similarities to your own work as much as you find differences.
A Real-life Example
Like a lot of other web developers I've always held contempt for JavaScript. It's a language that was once far outside my comfort zone. However, the Dojo framework came along and converted me. Using and reading through the code led me to appreciate all the nuances I once despised about the JavaScript language. I listened to talks on the language's history, I learned some of the advanced techniques and concepts presented by Douglas Crockford, John Resig, et cetera, and then I read the specs for upcoming versions of PHP. I had unknowingly adopted some of the functional programming techniques and concepts prevalent in JavaScript and now I could apply them to my PHP projects.
Stepping out of my comfort zone was one of the most influential decisions I've made. I hope you will take some time to do the same.
Stubbing an Interface
One of the topics I talk about frequently is coding to an interface. There is an abundance of evidence why one might choose to do so but today I'd like to talk about one benefit in terms of unit testing—another topic I love to talk about. To illustrate the benefit I will use an example of having the data source coded against the interface thereby allowing the consumer of the data source to ignore the specifics behind how data is stored, e.g., in a relational database or in a key-value store, and concentrate solely on requesting the appropriate data. Before I get to the code, however, I think it's important that I define some of the scope of this post.
Goals of a Unit Test Suite
Other than exercising a substantial portion of your code a unit test suite must be capable of running quickly lest it be a burden on development. Having a burdensome, slow-running test suite will only encourage developers to ignore it and only run it when absolutely necessary. This, in turn, will make maintenance of the test suite more difficult and probably result in fragile or poor tests which ultimately will leave a bad taste in the developer's mouth about unit testing, and we wouldn't want that, now would we?
One sure-fire way to decrease the speed of a test suite is to make it rely on real-world data sources such as your relational database or even a web service. Imagine a test suite for an application that relies on Twitter's availability. I can assure you that I would not want to run that test suite. The fail whale is annoying enough on the web. But I digress. Being able to emulate a data storage source is paramount to the speed of a unit test. Stubbing is one technique employed to do such a thing.
A Data Source Interface
A basic data source needs the implement the standard CRUD methods and can be defined as follows:
interface ICrudDataSource
{
public function create(array $data);
public function read($id);
public function update($id, array $data);
public function delete($id);
}
If this were a real interface there would be PHPDoc blocks to indicate the behavior of the methods and parameter types, but for the sake of the example I omit them. Now we can implement this interface in our database table class and define the behavior for each of the methods in terms of how our chosen rdbms will understand them, e.g., create() is an INSERT statement, read() is a SELECT statement, so on and so forth.
We can then use this class in our unit tests and it will actually test that the database vendor correctly implemented the INSERT, SELECT, UPDATE, and DELETE statements and that we are calling them appropriately. While I do think the latter should be tested when we look at the database abstraction implementation, the former is not necessary and will only slow down tests that use the data source code but don't directly depend on it being a database, for example. In such a case a simple array-based storage mechanism will work.
Array-based Stub for Unit Testing
Thus, we implement the ICrudDataSource interface in a stub that is used for our unit tests that need to interact with the data storage but necessarily need to interact with the specific data storage used in production.
class UserDataStorageStub implements ICrudDataSource
{
protected $_store;
public function __construct()
{
$this->_store = array();
}
public function create(array $data)
{
if (!$this->exists($data['id'])) {
$this->_store[$data['id']] = $data;
return true;
}
return false;
}
public function read($id)
{
if ($this->exists($id)) {
return $this->_store[$id];
}
return false;
}
public function update($id, array $data)
{
if (!$this->exists($id)) {
return false;
}
$this->_store[$id] = $data;
}
public function delete($id)
{
unset($this->_store[$id]);
}
private function exists($id)
{
return array_key_exists($id, $this->_store);
}
}
With this array-based implementation the unit tests will be fast regardless of the availability of the actual data source or the load on that data source. This stub, or a stub that extends this one, can also be easily pre-populated with test data to allow for testing with fixed data (although this can lead to very brittle tests if done lackadaisically).
Maintainable Unit Tests with PHPUnit Revised
One Step Back
In my previous post I chose an example that did not illustrate my point. In this post I will address the issues with previous post, specifically those brought to light by Matthew Weier O'Phinney in his comment.
His first point is that changing the method signature on a constructor should and will break any client code, thus modifications will be necessary to the tests. I agree with this wholeheartedly and my choice of using a constructor was obviously flawed. Let's just chalk this one up to not having had my coffee as I typed that post…
His second point deals with using the tools available to you within the framework. Specifically he mentions the use of the setUp() method which executes prior to each test case and is normally used to establish required state, set up fixtures, etc. I already committed to the constructor example so I carried it through this segment of the code. It was, again, a bad decision.
So this post will serve as a replacement for the old post to better illustrate my point.
Maintainable Tests
Great unit testing is a learned skill. Unfortunately the motivation developers have for dabbling in writing unit tests for an application usually comes from someone else, e.g., his or her boss, a fellow team member, etc. This leads to writing tests just to have tests which, in my opinion at least, is no better than not having tests in the first place.
A non-maintainable code base is know to be a problem. The corollary, a non-maintainable test suite is a huge problem, is also true. If you cannot maintain your test suite, what is the point? Any time a unit test reaches beyond a single or violates the DRY principle a development team could find that the test suite is verging on a non-maintainable state.
Testing Units More Than Once
If a requirement or business rule is tested more than once in the test suite, as is the case in the following example, any changes become difficult to manage. In this example the business requirement that changed is that all cars start with five gallons of gas before they arrive at your showroom. Thus a brand new car, i.e., the default constructed car, will not have an empty gas tank and should be able to start—unless something else is wrong, but I am a programmer not a mechanic.
class CarTest extends PHPUnit_Framework_TestCase
{
protected $_car;
public function setUp()
{
$this->_car = new Car();
}
public function testGasolinePresentBeforeStarting()
{
$this->setExpectedException('GasTankEmptyException');
$this->_car->start();
}
public function testCarStartsAfterFillup()
{
$fuelVessel = $this->getMock(
'GasolineContainer',
array('dispense')
);
//set expectation on fuelContainer to give 10
//gallons of fuel when dispense is called
try {
$this->_car->start();
$this->fail(
'Empty gas tank but no exception caught'
);
} catch(GasTankEmptyException $e) {
$this->_car->addGasoline($fuelVessel);
}
$this->_car->start();
$this->assertTrue($this->_car->isRunning());
}
}
This change is not necessarily a regression because code that used the Car object before should still work as intended the cars will just start from scratch with some gasoline. Since we changed this requirement both tests in the above code will fail. The first will fail because we assumed a brand new car object would have no gasoline in the tank. The same for the second test. The code repeats itself, and so does my explanation.
Avoiding the duplication in the second test by immediately filling up the car with gasoline solves the problem of having to dig through the unit test and makes the second test case a true unit test. As it stands above, it tests two units: 1) that a car with an empty tank throws a specific exception and 2) that after adding gasoline to the car it will start.
Using the Tools Correctly
I knew about setUp() and tearDown() but I was trying too hard to come up with a fancy solution to my problem. The problem, as it should have been explained previously, occurs when a test case has groups of tests that require different configurations of the UUT. For example, sticking with our Car class, we have several tests that require a stock instance of Car while a group of others needs a car with a special engine. We can accomplish this as follows:
class CarTest extends PHPUnit_Framework_TestCase
{
protected $_car;
public function setUp()
{
$this->_car = new Car();
}
public function testSomethingWithAStockCar()
{
//work
$this->assert(…);
}
public function testSomethingElseWithStockCar()
{
//same as above
}
public function testCarWithDifferentEngine()
{
//mock/stub of engine: $engine
$this->_car->setEngine($engine);
}
public function testCarWithDifferentEngineAgain()
{
//mock/stub of engine: $engine
$this->_car->setEngine($engine);
}
}
Assuming I abstract the creation of the mock engine into a helper method, I still have a lot of duplicated code and I still have to call the setEngine() method in each test. That's absurd. One solution is to create a new test case for the different configuration; I'll call it CarWithEngineBTest.
class CarWithEngineBTest extends PHPUnit_Framework_TestCase
{
protected $_car;
public function setUp()
{
$engine = $this->getMock('EngineB');
//…configure mock object…
$this->_car = new Car();
$this->_car->setEngine($engine);
}
//test cases that need this configuration
}
And remove these specific tests from the previous test case. This probably isn't the only way to solve this issue, but it does seem better than my previous factory method "solution".
The example still isn't perfect, but I couldn't come up with a better one. You are more than welcomed to weigh in.
Maintainable Unit Tests with PHPUnit
Great unit testing is a learned skill. Unfortunately the motivation developers have for dabbling in writing unit tests for an application usually comes from someone else, e.g., his or her boss, a fellow team member, etc. This leads to writing tests just to have tests which, in my opinion at least, is no better than not having tests in the first place.
A non-maintainable code base is know to be a problem. The corollary, a non-maintainable test suite is a huge problem, is also true. If you cannot maintain your test suite, what is the point?
Looking at an example, I frequently see test cases that have the UUT's constructor called at the beginning of each test. This works fine up until the requirements change and the constructor now requires an instance of IDataSource or an array instead of a string. Now each test must be touched, i.e., development time devoted to each test function, to implement this change.
class CarTest extends PHPUnit_Framework_TestCase
{
public function testStartFailsIfGasTankEmpty()
{
$car = new Car();
$this->setExpectedException('GasTankEmptyException');
$car->start();
}
public function testSomethingElse()
{
$car = new Car();
//assert something
}
//so on and so forth
}
Instead, if a developer uses a factory method within the test suite to return a default instance of the UUT, the change to the tests is only necessary in cases where the initialized state of the instance must change via the constructor, as follows.
class CarTest extends PHPUnit_Framework_TestCase
{
public function testStartFailsIfGasTankEmpty()
{
$car = $this->Factory_Car();
//…snip…
}
//…snip…
private function Factory_Car()
{
return new Car();
}
}
Okay, that works for a while. But what if in ninety-five percent of the tests you need to use the "default constructor"—in quotes because PHP classes can only have one constructor—but the other five percent need to pass in some configuration options. You have two choices:
- create a new factory method for each group of, e.g., Factory_Car and Factory_SuperFastCar, or
- allow the existing factory method to take arguments and pass them on to the constructor.
Which you choose depends on the unique situation of your test case. For this example I'll assume there are ten tests. Seven of the tests use a default Car object while the other three each need a unique Car instance, thus each passes a configuration argument to the constructor. To handle this situation the factory method will make use of the PHP functions func_get_args() and call_user_func_array().
public function Factory_Car()
{
$car = new Car();
if (func_num_args() > 0) {
$args = func_get_args();
call_user_func_array(array($car, '__construct'), $args);
}
return $car;
}
This factory method allows each test to retrieve a properly-configured instance—where configuration is done via the constructor—of the Car class with which to work without sacrificing maintainability. One caveat: the three test methods in the example will need to be modified if the constructor's signature changes, but, last time I checked, three is less than ten.
Excepting with the SPL
In my opinion proper use of error handling within an application is the mark of seasoned, professional developer. It is far too often that I see code that explodes on every error or, even worse, does not consider that errors will occur during runtime.
One area that I like to make use of exceptions is unexpected input, especially in terms of function/method arguments. A lot of times the standard practice seems to be to return FALSE for invalid input, but the boolean value false should be reserved for boolean indication. Likewise, NULL should have a special connotation, semantically speaking.
In this first example, a repository (or mapper) is attempting to find a user with an id—let's assume it is a database id—that should be an integer. (This code uses the Zend_Db_Table component to abstract queries, etc.)
class UserRepository implements IRepository
{
public function fetchById($id)
{
$data = $this->getDbTable()
->find($id)
->current();
if (null === $data) {
return null;
}
return new User($data->toArray());
}
}
Notice there is no validation performed on the $id parameter which is pushed directly into the database table query. Granted the Zend_Db component will handle this error with minimal pain, i.e., you should be fairly protected against a SQL injection attack, but you know that your database table uses an integer for the ID column thus your application should only respond positively to an integer value for $id.
public function fetchById($id)
{
if (!is_int($id)) {
return false;
}
//…snip
}
My first attempt (above) at validation uses the native is_int() validation function to check if the value supplied for $id is an integer. If it is not the function returns false. There is plenty of precedent behind using the value FALSE as the error state, but it is blatantly not semantic. FALSE is obviously not the user object I asked for, but it does not indicate what went wrong.
Finally, I decide to use the Zend_Validate component—for this trivial example it may be overkill, but it does the job nonetheless—to validate my input parameter for the user's ID property. Also notice that I am now throwing an exception object, specifically an InvalidArgumentException.
public function fetchById($id)
{
$validator = new Zend_Validate_Int();
if (!$validator->isValid($id)) {
throw new InvalidArgumentException(
'User ID must be an integer.
);
}
//…snip
}
The InvalidArgumentException exception is one of many defined in the SPL as an extension of a logic exception. The reason for using such an exception class would be to improve the readability and usefulness of client code. When I use the UserRepository object, my client code will look as follows. When an invalid argument, i.e., a non integer is given to the fetchById() method it is obvious which code path will execute.
try {
$repository = new UserRepository();
$user = $repository->fetchById("jcarouth");
} catch(InvalidArgumentException $e) {
//we passed an invalid argument, i.e., a non-integer
} catch(Exception $e) {
//some other
}
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.
Professional Development with the Zend Framework: Introduction
Table of contents for Professional PHP Development
- Professional Development with the Zend Framework: Introduction
- Professional Development with the Zend Framework: Unit Testing
Talk to a random sample of developers and you'll find a wide range of opinions regarding frameworks. Some people swear by frameworks; some people swear they will never touch a framework. Regardless of your opinion frameworks do serve a valuable purpose: they are tools that are capable and quite effective at developing professional developers.
Frameworks are not unique to any one language. I've watched PHP develop from a scripting language designed to simplify dynamic elements in a personal home page to an "object capable" (snicker) language that people apply object oriented principles with. With the recent rise in available of PHP frameworks, development — professional, "enterprise", or otherwise — using frameworks is becoming more and more prevalent.
Why the Zend Framework?
Personally, I've only worked with a few frameworks and none more so than with the Zend Framework (ZF) which is why I chose it for this series. There are quite a few tutorials out there on how to get started with the framework and I don't intend for this to be a quick and easy "Hello World" tutorial. If you are after something along those lines I'd suggest reading Pádraic Brady's book Zend Framework: Surviving the Deep End. Rather, I'd like to walk through how to use the framework as a learning tool. This will be useful knowledge for even the freshest of beginners but I think it will be especially useful to a developer looking to take his or her framework-driven projects to the next level.
One benefit found in using the Zend Framework comes from its design as a use-at-will framework which means the component library is useful separate from the MVC stack. More concisely, if there is a component of the framework, e.g., the Twitter service wrapper, that you would like to use it is possible to just grab that component and run with it. (I believe the ZF team has worked hard in trying to keep components as decoupled as possible. That said, it is entirely possible that you might have to grab a couple components.)
In this series I will be using the the framework "in all its glory" including the MVC stack. As stated earlier I will assume that you understand the basics on getting your app up and running, but I will have to walk you though some places where I tend to differ from some of the conventions found in the various sources of guidelines for ZF development.
Source Code
To truly benefit from this series you will need to see the progression of the project from early stages to polished and ready for deployment. That wouldn't be difficult except that I don't know at what point you stumbled upon this series. To better facilitate housing each step I will host the code for this series on GitHub. (If you have not yet experimented with Git I — and many others — highly recommend giving it a shot.)
Each installment of this series will be a branch in the Git repository. There should be roughly one branch per installment unless there is something terribly clever or complicated and I'll try to separate it out for you.
You can browse the repository online or clone it. I also encourage you to fork the project and add your commentary. After all giving back to the PHP community is encouraged. The code is licensed under the GPLv3 free software license.
Environment
To follow along with the series you will need to have access to several tools. First and foremost you will need a copy of the Zend Framework. The release at the time of writing is version 1.9.2. I like to use the release branch associated with the current minor version as my development code base. In other words I'll pull from the svn repository branches/release-1.9 instead of the tag for release-1.9.2. This allows my development to receive all bug fixes and improvements for the 1.9.x family.
You will also need a copy of PHPUnit. Sebastian Bergmann maintains steller documentation on installing and using PHPUnit. While not necessary if you just want to gather some new practices or tips on using the framework, PHPUnderControl (PHPUC) is something I'll mention and use throughout this series. Several other tools integrate with PHPUC including PHP Code Sniffer, PHP Documentor, and dbdeploy.
I'll be using PHP version 5.2.9 on my development environment. While PHP 5.3 is the current version it's sadly likely that the adoption rate will be as slow as usual. To take full advantage of PHPUnit and PHPUC, Xdebug will also be used on my development machine(s).
Now, if I haven't lost you, let's move on to some actual developing; well, if you call using mkdir development.
The Directory Structure
At first glance, the recommended Zend Framework directory structure is incredibly complex. I know because the first time I saw my eyes glazed over. But there is a reason for the deep structure and it's masquerading around as our good friend Mr. Separation of Concerns. Take a moment to digest the structure in Figure 1.

Figure 1 — Recommended Zend Framework Structure
Pay no attention to the little cross-hair like symbol on all the directories. That just means that the directory is in the Eclipse build path. In all this is not a bad directory structure. It has a places outside of the document root (which is the public directory, by the way) for tests, configuration files, application cache files, uploaded files, et cetera.
However, in my opinion this structure does not make sense for a production product. For example, when this project is deployed, the tests do not need to go with it. They need to be present on every developer's workstation and the build server, but the final deployment of the product has no reason to keep them around. So, my first change is to separate the tree(s) that compose the production-necessary files from the test and development files. (I'll also cheat and make the change look more impressive by collapsing the expanded directories.)

Figure 2 — Modified "ProDev" structure
As you can see, we
- added a tools directory,
- added the src directory (which now contains all of our application code),
- removed the build sub-directory from the scripts directory, and
- added a directory called database with a sub-directory of deltas to the scripts directory.
The tools directory will house local copies of the various external tool libraries, e.g., PHPUnit, that are needed for the build process. I keep local copies to ensure that no matter where the project is being developed, the environment is consistent. For example, I might have a PHPUnit instance on my development box that is version 3.4 of the project but the build server might be at version 3.3. Having the local project copy — or reference, really — means that I don't have to worry about what version is installed where. I know it will work.
The src directory is where the application code will reside including the reference to the framework. Keeping it contained inside this directory allows the project's development files to remain separate from the application code that will be distributed in final form.
The scripts/build directory is unnecessary as the build script(s) belongs in the project root.
Finally, I added the database and database/deltas directory to the scripts directory to house the script(s) necessary to create and initialize the database for the application — assuming there is one. The deltas part will be confusing to you if you are not familiar with dbdeploy. Essentially this folder will contain a series of sql files prefixed by a number, e.g., 001_create_professionals_table.sql. The three digit number increments by one with each script. I don't yet know what happens after 999_x.sql because I have not made it that far in a project. We'll touch on database and delta scripts later on.
Stub the Necessary Files
The main "hello world" files in a Zend Framework application are no secret. There must be an index.php script, a .htaccess file, a Bootstrap.php file complete with an implementation of Zend_Application_Bootstrap_Bootstrap, so on and so forth. All of these files are included in the Git repository. Clone away.
The interesting bits are the unit test scripts and the build script. For the unit tests — in the tests directory — the scripts you will find in this initial distribution of the ProDevZF application are the phpunit.xml file (a configuration file used by the PHPUnit engine; a bootstrap.php script (used to set up necessary resources to run the scripts); and an AllTests.php (which is a bit strange at this point considering the test suite is empty). Feel free to poke around the contents of those files before the next installment of this series where I will explain them.
The build script is a whole different beast. Welcome to the world of Ant. If you aren't into Ant because you hate Java — or for other more noble reasons — there is a similar tool called Phing. You can probably use Phing instead of Ant but I will not because I'm using Java-based products for my continuous integration. Since build scripts in Ant are covered in great detail on the Internet (the series of tubes you are connected to right now) I will elect to not explain the build script. If you'd like me to explain the whats and whys, leave me a comment and I'll work it into a supplementary post.
Words. So many of them.
At long last this introduction is over. I appreciate your patience as I set the stage for the coming articles. I am hopeful that no other installment in this series is quite as lengthy.
