Archive for the ‘Software Architecture’ Category
An Alternative to Zend_Controller
Zend Framework is very flexible and one of its strengths is that it allows developers to implement their own components. The Zend_Controller component, for example, is very powerful. Of course, it’s not my intention to replace it, but to offer an alternative that decreases the number of decisions a developer needs to make when developing an application.
Meet Zf_Controller. The Zf_Controller component has the following goals:
- Abstract complexity: Try to reduce the level of details so the developer can focus on a few concepts at a time.
- Emphasize Convention over Configuration: Emphasize CoC, meaning that the user only needs to specify unconventional aspects of the application.
- Maintain backwards compatibility: Allow the developer to replace Zf_Controller with Zend_Controller in case the application grows in size or complexity.
- Improve performance: Load less classes, execute less code.
- Remove circular references: Avoid circular references.
- Remove Singleton classes: Avoid implementing the Singleton pattern.
- Research: Learn more about the framework, what it does, how it works.
Project Structure
Zend_Controller allows you to use the project structure that best suits your needs. On the other hand, Zf_Controller is more rigid, it only allows you to use the standard project structure:
project/
app/
config/
controllers/
ErrorController.php
IndexController.php
views/
layouts/
scripts/
error/
index/
index.phtml
domain/
Model/
Example.php
lib/
Zend/
Zf/
Zf_Controller classes:
Zf/
Controller/
Action/
Helper/
Layout.php
Action.php
Front.php
Bootstrap File
Zend_Controller:
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../app'));
set_include_path(APPLICATION_PATH . '/../lib'
. PATH_SEPARATOR . get_include_path());
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$routes = include_once APPLICATION_PATH . '/config/routes.php';
$router->addRoutes($routes);
$layout = Zend_Layout::startMvc();
$layout->setLayoutPath(APPLICATION_PATH . '/views/layouts');
$frontController->dispatch();
Zf_Controller (no Router):
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../app'));
set_include_path(APPLICATION_PATH . '/../lib'
. PATH_SEPARATOR . APPLICATION_PATH . '/../domain);
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();
$frontController = new Zf_Controller_Front();
$frontController->setLayoutPath(APPLICATION_PATH . '/views/layouts');
$frontController->dispatch();
Action Controller
The default action controller and the default action are named “index”:
class IndexController extends Zf_Controller_Action
{
public function indexAction()
{}
}
Error Controller
Zend_Controller:
class ErrorController extends Zend_Controller_Action
{
// Action used by Zend_Controller
public function errorAction()
{
$error = $this->_getParam('error_handler');
echo $error->exception->getMessage();
}
}
Zf_Controller:
class ErrorController extends Zf_Controller_Action
{
// Action used by Zf_Controller
public function indexAction($e)
{
echo $e->getMessage();
}
}
Rendering a View Script
Zf_Controller does not use the ViewRenderer helper class. To make the code more readable and testable, you need to call the render() method and return a value:
class IndexController extends Zf_Controller_Action
{
public function indexAction()
{
$view = $this->initView();
$view->message = 'Hello';
// Renders views/scripts/index/index.phtml
return $this->render();
}
public function testAction()
{
$view = $this->initView();
$view->message = 'Goodbye';
// Renders views/scripts/index/index.phtml
return $this->render('index');
}}
index.phtml file:
<p><?php echo $this->message ?></p>
Using a Layout
By setting the path to your layouts in the Bootstrap file, you automatically enable the default layout “layout.phtml”:
$frontController = new Zf_Controller_Front(); $frontController->setLayoutPath(APPLICATION_PATH . '/views/layouts');
To disable the layout:
class IndexController extends Zf_Controller_Action
{
protected $_isLayoutEnabled = false;
}
To use a different layout:
class IndexController extends Zf_Controller_Action
{
protected $_layoutScript = 'main.phtml';
}
main.phtml file:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <?php echo $this->headTitle() ?> <?php echo $this->headScript() ?> <?php echo $this->headStyle() ?> </head> <body> <div id="content"><?php echo $this->layout()->content ?></div> </body> </html>
Source Code:
http://svn.fedecarg.com/repo/Zf/Controller/
Implementing your own Front Controller in Zend Framework
There’s no doubt that the additional complexity of implementing the default Front Controller in ZF results in a number of benefits. The most important ones are flexibility and extensibility. The Front Controller implementation takes into consideration the future growth of your application and its design reduces the level of effort required to extend it. A good example of this is the plugin architecture.
But, what if after evaluating the requirements of your system, you determine that there’s not sufficient complexity to implement the default Front Controller? What if you don’t need all that flexibility, a URL mapper, a ViewRendered plugin or an ActionStack helper. Maybe all you need is just a couple of controllers, and that’s it. It’s clear that the standard Front Controller does provide more options and meets every possible use case requirement, but at the cost of complexity and a lot of classes.
So, what to do? Do you choose a different tool for the job, or replace the default Front Controller with your own implementation? How hard can it be? Is it really that difficult to replace the most important component of the Zend Framework and maintain backwards compatibility?
You are about to find out.
In this series of posts, I’ll try to cover a few things I’ve learned about implementing my own Front Controller in Zend Framework. Most of the ideas and code are based on my previous posts:
- Zend Framework: The Cost of Flexibility is Complexity
- Zend Framework Automatic Dependency Tracking
- Zend Framework Controller: 22% Drop in Responsiveness
- Refactoring the Front Controller of the Zend Framework
- Improving the performance of Zend_Controller
- Zend Framework Architecture
Will simplicity finally meet power?
Domain-Driven Design: Sample Application
Last updated: 15 Feb, 2010
Part 1: Domain-Driven Design and MVC Architectures
Part 2: Domain-Driven Design: Data Access Strategies
Part 3: Domain-Driven Design: The Repository
Some of the Domain-driven design concepts explained above are applied in this sample application.
Directory Structure
app/
config/
controllers/
UserController.php
domain/
entities/
User.php
UserProfile.php
repositories/
UserRepository.php
views/
lib/
public/
The domain layer should be well separated from the other layers and it should have few dependencies on the framework you are using.
User Entity
The User and UserProfile objects have a one-to-one relationship and form an Aggregate. An Aggregate is as a collection of related objects that have references between each other. Within an Aggregate there’s always an Aggregate Root (parent Entity), in this case User:
class User
{
private $id;
private $name;
/* @var UserProfile */
private $profile;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function setProfile(UserProfile $profile)
{
$this->profile = $profile;
}
public function getProfile()
{
return $this->profile;
}
}
class UserProfile
{
private $id;
public function __construct($id)
{
$this->id = $id;
}
}
Users Collection
A collection is simply an object that groups multiple elements into a single unit.
class Users
{
private $elements = array();
public function __construct(array $users)
{
foreach ($users as $user) {
if (!($user instanceof User)) {
throw new Exception();
}
$this->elements[] = $user;
}
}
public function toArray()
{
return $this->elements;
}
}
User DAO
The UserDAO class allows data access mechanisms to change independently of the code that uses the data:
interface UserDao
{
public function find($id);
public function findAll();
}
class UserDatabaseDao implements UserDao
{
public function find($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource()
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user')->where('id = ?', $id);
return $db->fetchRow($query);
}
public function findAll()
{
...
return $db->fetchAll($query);
}
}
interface UserProfileDao
{
public function find($id);
public function findByUserId($id);
}
class UserProfileDatabaseDao implements UserProfileDao
{
public function find($id)
{
...
}
public function findByUserId($id)
{
$dataSource = Zf_Orm_Manager::getInstance()->getDataSource()
$db = $dataSource->getConnection('slave');
$query = $db->select()->from('user_profile')->where('user_id = ?', $id);
return $db->fetchRow($query);
}
}
User Repository
A Repository is basically a collection of Aggregate Roots. Collections are used to store, retrieve and manipulate Entities and Value objects, however, object management is beyond the scope of this post. The Repository object can inject dependencies on demand, making the instantiation process inexpensive.
class UserRepository
{
/* @var UserDatabaseDao */
private $userDao;
/* @var UserProfileDatabaseDao */
private $userProfileDao;
public function __construct()
{
$this->userDao = new UserDatabaseDao();
$this->userProfileDao = new UserProfileDatabaseDao();
}
public function find($id)
{
$row = $this->userDao->find($id);
$user = new User($row['id'], $row['name']);
$row = $this->userProfileDao->findByUserId($id);
if (isset($row['id'])) {
$profile = new UserProfile($row['id']);
$user->setProfile($profile);
}
return $user;
}
public function findAll()
{
$users = array();
$rows = $this->userDao->findAll();
foreach ($rows as $row) {
$users[] = new User($row['id'], $row['name']);
}
return new Users($users);
}
}
Usage:
$repository = new UserRepository(); $user = $repository->find(1); $profile = $user->getProfile(); $users = $repository->findAll();
Source Code
http://svn.fedecarg.com/repo/Zf/Orm
Links
If you’re interested in learning more about Domain-driven design, I recommend the following articles:
Domain Driven Design and Development In Practice
Domain-Driven Design in an Evolving Architecture
Domain-Driven Design: The Repository
Part 2: Domain-Driven Design: Data Access Strategies
The Ubiquitous Language
The ubiquitous language is the foundation of Domain-driven design. The concept is simple, developers and domain experts share a common language that both understand. This language is set in business terminology, not technical terminology. This ubiquitous language allows the technical team become part of the business.
The Repository
Repositories play an important part in DDD, they speak the language of the domain and act as mediators between the domain and data mapping layers. They provide a common language to all team members by translating technical terminology into business terminology.
In a nutshell, a Repository:
- Is not a data access layer
- Provides a higher level of data manipulation
- Is persistence ignorance
- Is a collection of aggregate roots
- Offers a mechanism to manage entities
Data Access Objects
In the absence of an ORM framework, the Data Access Object (DAO) handles the impedance mismatch that a relational database has with object-oriented techniques. In DDD, you inject Repositories, not DAO’s in domain entities. For example, imagine you have an entity named User that needs to access the database to retrieve the User details:
class User
{
private $id;
private $name;
public function __construct(UserDao $dao, $id)
{
$row = $dao->find($id);
$this->setId($row['id']);
$this->setName($row['name']);
}
public function setId($id) {}
public function getId() {}
public function setName($name) {}
public function getName() {}
}
The User DAO class will look something like this:
interface UserDao
{
public function fetchRow($id);
}
class UserDatabaseDaoImpl implements UserDao
{
public function fetchRow($id)
{
...
$query = $db->select();
$query->from('user');
$query->where('id = ?', $id);
return $db->fetchRow($query);
}
}
$dao = new UserDatabaseDaoImpl();
$user = new User($dao, 1);
$userId = $user->getId();
But, what about separation of concerns? DAO’s are related to persistence, and persistence is infrastructure, not domain. The main problem with the example above is that we have lots of different concerns polluting the domain. According to DDD, an object should be distilled until nothing remains that does not relate to its meaning or support its role in interactions. And that’s exactly the problem the Repository pattern tries to solve.
Injecting Repositories
Lets create a UserRepository class to isolate the domain object from details of the UserDatabaseDaoImpl class:
class User
{
private $id;
private $name;
public function __construct(UserRepository $repository, $id)
{
$row = $repository->find($id);
$this->setId($row['id']);
$this->setName($row['name']);
}
public function setId($id) {}
public function getId() {}
public function setName($name) {}
public function getName() {}
}
It’s the responsibility of the UserRepository to work with all necessary DAO’s and provide all data access services to the domain model in the language which the domain understands.
interface UserRepository
{
public function find($id);
}
class UserRepositoryImpl implements UserRepository
{
private $databaseDao;
public function setDatabaseDao(UserDao $dao) {}
public function getDatabaseDao() {}
public function find($id)
{
return $this->getDatabaseDao()->find($id);
}
}
$userRepository = new UserRepositoryImpl();
$userRepository->setDatabaseDao(new UserDatabaseDaoImpl());
$user = new User($userRepository, 1);
$userId = $user->getId();
$userName = $user->getName();
The main difference between the Repository and the DAO is that the DAO is at a lower level of abstraction and doesn’t speak the ubiquitous language of the domain.
So, what’s next?
The process of creating an entity is complex, because an entity always has relationship with other objects in your domain model. When creating an entity, we have to initialize its relationships as well. Therefore, it’s a good practice to delegate this task to another object.
We’ve seen how to write a persistence-ignorant domain model. Next, I’ll explain how to automate the creation and injection of dependencies.
Domain-Driven Design: Data Access Strategies
Part 1: Domain-Driven Design and MVC Architectures
The Domain Model
Here are some of the features a Domain-driven design framework should support:
- A domain model that is independent and decoupled from the application.
- A reusable library that can be used in many different domain-specific applications.
- Dependency Injection in order to inject Repositories and Services into Domain Objects.
- Integration with unit testing frameworks, such as PHPUnit.
- Good integration with other frameworks, such as Zend, Symfony, Doctrine, etc.
The Zend Framework, for example, is part of the infrastructure layer and acts as a supporting library for all the other layers. However, the domain layer should be well isolated from the other layers of the application and should not be dependent on the framework you are using.
Data Access Strategies
When accessing data from a data source, you have to decide how your application will communicate with a data source. Each data access strategy has its own advantages and disadvantages. Here are some design patterns that support DDD:
Generic DAO’s
Data Access Objects (DAO’s) and Repositories play an important role in DDD. The goal of a DAO is to abstract and encapsulate all access to the data and provide an interface. The DAO always connects, reads and saves data to a data source. From the applications point of view, it makes no difference when it accesses a database, XML file or Web service:
$user = new UserDbDao(); $row = $user->findUserById(456); echo $row->name; $user = new UserXmlDao(); $row = $user->findUserById(456); echo $row->name;
Table Data Gateway
An object that acts as a Gateway to a database table. One instance handles all the rows in the table. The Zend_Db_Table solution is an implementation of the Table Data Gateway pattern:
$user = new User(); $rows = $user->find(456); $row = $rows->current(); echo $row->name;
More info: Table Data Gateway
Row Data Gateway
An object that acts as a Gateway to a single record in a data source. There is one instance per row. Zend_Db_Table_Row is an implementation of the Row Data Gateway pattern:
$user = new User();
$row = $user->fetchRow($user->select()->where('user_id = ?', 1));
echo $row->name;
More info: Row Data Gateway
Active Record
An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data. The Mad_Model component is an ORM layer that follows the Active Record pattern where tables map to classes, rows map to objects, and columns to object attributes:
$user = new User(); $userFinder = new UserFinder(); $row = $userFinder->find(456);
More info: Active Record
Data Mapper
A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself:
$user = new User(); $userMapper = new UserMapper(); $row = $userMapper->findByEmail($email);
More info: Data Mapper
Data Transfer Objects
A Data Transfer Object (DTO) in Domain-driven design is not the same as a Value Object. A DTO is often used in combination with a DAO to encapsulate data. It allows you to reduce communication effort when dealing with a lot of small data entities. For example:
class UserData
{
public function setId($id) {}
public function getId() {}
public function setFirstName($firstName) {}
public function getFirstName() {}
public function setLastName($lastName) {}
public function getLastName() {}
}
$user = new UserData();
$user->setFirstName('Jim');
$user->setLastName('Morrison');
$userDao = new UserDbDao();
$userDao->insert($user);
A DTO is a simple container for a set of aggregated data. It should contain no business logic and limit its behaviour to activities such as internal consistency checking and basic validation. Be careful not to make the DTO depend on any new classes as a result of implementing these methods.
Domain-Driven Design and MVC Architectures
According to Eric Evans, Domain-driven design (DDD) is not a technology or a methodology. It’s a different way of thinking about how to organize your applications and structure your code. This way of thinking complements very well the popular MVC architecture. The domain model provides a structural view of the system. Most of the time, applications don’t change, what changes is the domain. MVC, however, doesn’t really tell you how your model should be structured. That’s why some frameworks don’t force you to use a specific model structure, instead, they let your model evolve as your knowledge and expertise grows.
Domain-driven design separates the model layer “M” of MVC into an application, domain and infrastructure layer. The infrastructure layer is used to retrieve and store data. The domain layer is where the business knowledge or expertise is. The application layer is responsible for coordinating the infrastructure and domain layers to make a useful application. Typically, it would use the infrastructure to obtain the data, consult the domain to see what should be done, and then use the infrastructure again to achieve the results. Srini Penchikala explains this in more detail here: “Domain Driven Design and Development In Practice“.
Object-oriented programming is the most important element in the domain implementation. Domain objects are designed using classes and interfaces, and take advantage of OOP concepts like inheritance, encapsulation, and polymorphism. Most of the domain elements are true objects with both State (attributes) and Behaviour (methods or operations that act on the state). Entities and Value Objects in DDD are classic examples of OOP concepts since they have both state and behaviour.
Terminology used by Domain-driven design
- Entity: An object which has a distinct identity. For example, a User entity has a distinct identity with an ID.
- Value Object: An object which has no distinct identity, like numbers and dates. For example, if two Users have the same date of birth, you can have multiple copies of an object that represents the date 16 Jan 1982.
- Factory: Used to create Entities. For example, you can use a Factory to create a Profile entity from a User entity.
- Repository: Used to store, retrieve and delete domain objects from different storage implementations. For example, you can use a Repository to store the Profile your Factory created.
- Service: When an operation does not conceptually belong to any object.
The purpose of this post was to provide a brief introduction to Domain-driven design.
Part 2: Domain-Driven Design: Data Access Strategies
Links
If you’re interested in learning more about Domain-driven design or Model-driven design, I recommend the following articles:
Zend Framework: The Cost of Flexibility is Complexity
The Zend Framework is a very flexible system, in fact, it can be used to build practically anything. But, the problem with using a flexible system is that flexibility costs. And the cost of flexibility is complexity.
Martin Fowler wrote:
Every time you put extra stuff into your code to make it more flexible, you are usually adding more complexity. If your guess about the flexibility needs of your software is correct, then you are ahead of the game. You’ve gained. But if you get it wrong, you’ve only added complexity that makes it more difficult to change your software.
Don’t assume that just because you’re using an object-oriented framework you are writing reusable and maintainable code. You are just structuring your spaghetti code. As Paul Graham put it: “Object-oriented programming offers a sustainable way to write spaghetti code”. The main problem with flexibility is that most developers give up trying to understand. I don’t blame them, no one likes dealing with complexity. However, flexibility is sometimes required and generally always desirable. But, sometimes we forget that frameworks exist to help us solve or address complex issues, not to fix bad design decisions for us. So if your code is difficult to understand or unit test, then you are probably doing something wrong.
Just because you can, doesn’t mean you should
What’s the best way to render a view script in the Zend Framework? Think, don’t give up yet. Is it enabling or disabling the ViewRenderer action helper? Remember, the framework allows you to shape the Zend_Controller_Action class to your application’s needs.
The manual clearly states that:
By default, the front controller enables the ViewRenderer action helper. This helper takes care of injecting the view object into the controller, as well as automatically rendering views.
However, no one stops you from doing it manually:
Zend_Controller_Action provides a rudimentary and flexible mechanism for view integration. Two methods accomplish this, initView() and render(); the former method lazy-loads the $view public property, and the latter renders a view based on the current requested action, using the directory hierarchy to determine the script path.
Developer A
class IndexController extends Zend_Controller_Action
{
public function searchAction()
{
$query = $this->_getParam('q', null);
if (null === $query) {
$this->view->error = 'Invalid query';
return;
}
$this->view->query = $query;
if (is_numeric($query)) {
$this->setUserDetailsById($query);
} else {
$this->setUserDetailsByName($query);
}
}
public function setUserDetailsByName($name)
{
$row = $this->findUserByName($name);
$this->view->userId = $row['id'];
$this->view->userName = $row['name'];
}
public function setUserDetailsById($id)
{
$row = $this->findUserById($id);
$this->view->userId = $row['id'];
$this->view->userName = $row['name'];
}
}
Developer B
class IndexController extends Zend_Controller_Action
{
protected $_viewParams = array(
'error' => null,
'query' => null,
'userId' => null,
'userName' => null,
);
public function init()
{
$this->_helper->removeHelper('viewRenderer');
}
public function searchAction()
{
$query = $this->_getParam('q', null);
if (null === $query) {
$this->_viewParams['error'] = 'Invalid query';
$this->renderView('search');
return false;
}
return $this->displayUserDetails($query);
}
public function displayUserDetails($query)
{
if (is_numeric($query)) {
$row = $this->getUserDetailsById($query);
} else {
$row = $this->getUserDetailsByName($query);
}
if (!$row) {
$this->_viewParams['error'] = 'User not found';
$this->renderView('search');
return false;
}
foreach ($this->_viewParams as $key => $value) {
if (!array_key_exists($key, $row) {
continue;
}
$this->_viewParams[$key] = $row[$key];
}
$this->_viewParams['query'] = $query;
$this->renderView('search');
return true;
}
public function renderView($template)
{
$view = $this->initView();
foreach ($this->_viewParams as $key => $value) {
$view->$key = $value;
}
$this->render($template);
}
public function getUserDetailsByName($name)
{
return $this->findUserByName($name);
}
public function getUserDetailsById($id)
{
return $this->findUserById($id);
}
}
Same problem, different solutions.
Now, guess who is doing test-driven development, has all his code unit tested, puts commonly used code into libraries, abstracts out common patterns of behaviour, writes his code using the top-down fashion and uses the principle of “least astonishment”.
Developer A or B?
Conclusion
Many requirements lie in the future and are unknowable at the time our application is designed and built. To avoid burdensome maintenance costs developers must therefore rely on a system’s ability to change gracefully its flexibility. The combination of flexibility and smart design can reduce the maintenance burden. It’s up to us, the developers, to change our thinking when using a flexible framework. Like I said before, frameworks exist to help us solve complex issues, not to make decisions for us.
Four Great InfoQ Presentations
Hope you like these recommendations and if you know of any other good tech-related video, then please let me know.
1. Developing Expertise: Herding Racehorses, Racing Sheep
One of my favourites. In this presentation Dave Thomas (The Pragmatic Programmer) talks about expanding people’s expertise in their domains of interest by not treating them uniformly as they had the same amount of knowledge and level of experience.
2. Real World Web Services
Another good presentation. In this one Scott Davis provides a pragmatic, down-to-earth introduction to Web services as used in the real world by public sites, including SOAP-based and REST examples.
3. CouchDB and me
This presentation is different, and that’s why I like it so much. Damien Katz shares his experiences and reminds people how difficult but at the same time gratifying is to be an open source developer. He talks about the history of CouchDB development from a very personal point of view. His inspirations for CouchDB and why he decided to move my wife and kids to a cheaper place and live off savings to build this thing.
4. Yahoo! Communities Architectures
In this presentation, Ian Flint tries to explain the infrastructure and architecture employed by Yahoo! to keep going a multitude of servers running of different platforms and offering different services. Very interesting!
Testing Zend Framework Controllers
Post updated on: Oct 29, 2009
Testing a Web application is a complex task, because a Web application is made of several layers of logic. Unit testing a Zend Framework controller can be very difficult, specially for those who are not familiar with the Zend Framework.
You can test your action controllers using Zend_Test and/or PHPUnit. Zend_Test allows you to simulate requests, insert test data, inspect your application’s output and generally verify your code is doing what it should be doing. It’s up to you to decide which one to use. Even though this choice can be confusing, you can use both. If you’re just getting started with testing, using Zend_Test will probably get you started faster.
The PHPUnit framework will probably feel very familiar to developers coming from Java. It’s inspired by Java’s JUnit, so you’ll feel at home with this method if you’ve used JUnit or any test framework inspired by JUnit.
Of course, no one stops you from using both systems side-by-side (even in the same app). In the end, most projects will eventually end up using both. Each shines in different circumstances.
Using PHPUnit
First, you need to create the directory structure:
application/
config/
controllers/
ExampleController.php
models/
views/
library/
Custom/
Zend/
public/
tests/
application/
controllers/
ExampleControllerTest.php
ControllerTestCase.php
library/
log/
coverage/
bootstrap.php
phpunit.xml
A test suite needs some environmental information, and this information is usually found in the bootstrap.php file. The biggest difference between this file and the one you use in you application, is that the Front Controller doesn’t dispatch the Request Object:
tests/bootstrap.php
error_reporting( E_ALL | E_STRICT );
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
date_default_timezone_set('Europe/London');
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
define('APPLICATION_ENV', 'loc');
define('LIBRARY_PATH', realpath(dirname(__FILE__) . '/../library'));
define('TESTS_PATH', realpath(dirname(__FILE__)));
$_SERVER['SERVER_NAME'] = 'http://localhost';
$includePaths = array(LIBRARY_PATH, get_include_path());
set_include_path(implode(PATH_SEPARATOR, $includePaths));
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();
Zend_Session::$_unitTestEnabled = true;
Zend_Session::start();
tests/phpunit.xml
<phpunit bootstrap="./bootstrap.php" colors="false">
<testsuite name="ApplicationTestSuite">
<directory>./application/</directory>
<directory>./library/</directory>
</testsuite>
<filter>
<whitelist>
<directory suffix=".php">../application</directory>
<directory suffix=".php">../library/Custom</directory>
<exclude>
<directory suffix=".phtml">../application/views</directory>
<file>../application/Bootstrap.php</file>
</exclude>
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="./log/coverage" charset="UTF-8"
yui="true" highlight="false" lowUpperBound="35" highLowerBound="70"/>
</logging>
</phpunit>
Note: If you are using a version of PHPUnit >= 3.4, you need to add the ”testsuites” tag.
tests/application/ControllerTestCase.php
class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
public $application;
public function setUp()
{
$this->application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/config/settings.ini'
);
$this->bootstrap = array($this, 'bootstrap');
parent::setUp();
}
public function tearDown()
{
Zend_Controller_Front::getInstance()->resetInstance();
$this->resetRequest();
$this->resetResponse();
$this->request->setPost(array());
$this->request->setQuery(array());
}
public function bootstrap()
{
$this->application->bootstrap();
}
}
If you want the Front Controller to throw all the exceptions, you have no other choice than to overwrite the dispatch method and pass a boolean TRUE to the throwExceptions() method. For example:
class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
...
public function dispatch($url = null)
{
// redirector should not exit
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$redirector->setExit(false);
// json helper should not exit
$json = Zend_Controller_Action_HelperBroker::getStaticHelper('json');
$json->suppressExit = true;
$request = $this->getRequest();
if (null !== $url) {
$request->setRequestUri($url);
}
$request->setPathInfo(null);
$this->getFrontController()
->setRequest($request)
->setResponse($this->getResponse())
->throwExceptions(true)
->returnResponse(false);
$this->getFrontController()->dispatch();
}
...
}
Writing unit tests
Time to create our first test suite. A test suite is a class inherited from PHPUnit_Framework_TestCase containing test methods, identified by a leading “test” in the method name.
tests/application/controllers/ExampleControllerTest.php
<?php
require_once TESTS_PATH . '/application/ControllerTestCase.php';
require_once APPLICATION_PATH . '/controllers/ExampleController.php';
class ExampleControllerTest extends ControllerTestCase
{
public function testDefaultShouldInvokeIndexAction()
{
$this->dispatch('/');
$this->assertController('index');
$this->assertAction('index');
}
public function testViewObjectContainsStringProperty()
{
$this->dispatch('/');
$controller = new ExampleController(
$this->request,
$this->response,
$this->request->getParams()
);
$controller->indexAction();
$this->assertTrue(isset($controller->view->string));
}
}
To create an instance of the Action Controller, you need to dispatch the request and pass the Request/Response objects and parameters to the Action Controller.
tests/application/controllers/ErrorControllerTest.php
require_once TESTS_PATH . '/app/ControllerTestCase.php';
require_once APPLICATION_PATH . '/controllers/ErrorController.php';
class ErrorControllerTest extends ControllerTestCase
{
public function testExceptionIsAnInstanceOfZendControllerException()
{
$e = new stdClass();
$e->type = Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER;
$e->exception = new Zend_Controller_Exception('Invalid controller');
$e->request = $this->request;
$this->request->setParam('error_handler', $e);
$this->request->setParam('controller', 'error');
$this->request->setParam('action', 'error');
$controller = new ErrorController(
$this->request,
$this->response,
$this->request->getParams()
);
$controller->errorAction();
$isInstanceOf = $controller->view->exception instanceof Zend_Controller_Exception;
$this->assertTrue($isInstanceOf);
}
}
Running tests
$ cd /path/to/tests $ phpunit PHPUnit 3.3.8 by Sebastian Bergmann. ... Time: 0 seconds OK (3 tests, 4 assertions)
If there are test failures, you’ll see full details about which tests failed. Optionally, you can use Phing and Hudson to automate this task.
Related post:
Testing Zend Framework Action Controllers With Mocks
Setting up continuous integration for PHP using Hudson and Phing
Memcached consistent hashing mechanism
If you are using the Memcache functions through a PECL extension, you can set global runtime configuration options by specifying the values within your php.ini file. One of them is memcache.hash_strategy. This option sets the hashing mechanism used to select and specifies which hash strategy to use: Standard (default) or Consistent.
It’s recommended that you set to Consistent to allow servers to be added or removed from the pool without causing the keys to be remapped to other servers. When set to standard, an older strategy is used that potentially uses different servers for storage.
With PHP, the connections to the memcached instances are kept open as long as the PHP and associated Apache instance remain running. When adding a removing servers from the list in a running instance, the connections will be shared, but the script will only select among the instances explicitly configured within the script.
So, to ensure that changes to the server list within a script do not cause problems, make sure to use the consistent hashing mechanism.