Using a Dependency Injection Container with Zend_Application
Much has been written on Dependency Injection lately, mostly by Padraic Brady (1, 2) and Fabien Potencier (1, 2, 3, 4, 5). My subjective feeling tells me there are now more PHP DI containers out there than CMS or ORMs implemented in PHP, including two written by myself (an overengineered and a useful one). Its an awesome pattern if used on a larger scale and can (re-) wire a complex business application according to a clients needs without having to change much of the domain code. It aims at a complete seperation of object instantiation and dependency tracking from the business logic.
Beginning with version 1.8 Zend Framework is able to integrate any of these DI containers into its Zend_Application component easily. The Application component intializes a set of common resources and pushes them into the MVC stack as additional Front Controller parameters. Technically a Zend_Registry instance holds all these resources with their respective resource names as keys. The resources are accessible inside the Front Controller and the Action Controller classes. Assume the resource 'Db' is initialized in your application, you can access it with:
$front = Zend_Controller_Front::getInstance();
$container = $front->getParam('bootstrap')->getContainer();
$db = $container->db;
// or:
class FooController extends Zend_Controller_Action {
public function barAction()
{
$container = $this->getInvokeArg('bootstrap')->getContainer();
$db = $container->db;
}
}
This is pretty much dependency injection already, but the default registry approach suffers from two serious problem: New instances can only be added to the Container by implementing new Zend_Application resources and these instances cannot be lazy loaded. All resources used by Zend_Application are always loaded on every request. But the application container implementation was developed with Dependency Injection in mind and is not tied to the use of Zend_Registry. Only three magic methods are required by any container that wants to be Zend_Application compliant: __get(), __set() and __isset(). Each instantiated resource is pushed via __set() into the container. If required by another resource, __isset() is used to check wheater a resource with the given name exists inside the container and __get() is used to retrieve the instances from the container.
Some month ago I extended Yadif, a lightweight PicoContainer-like DI container for PHP written by Thomas McKelvey, with several features that make it a very powerful DI container in my opinion. It has a detailed documentation and examples on the GitHub Page if you want to check it out. I extended Yadif to be Zend_Application compliant in a way that the application-wide Zend_Application resources can be used as dependencies for objects that are lazy-loaded from the container. Skipping the tech-talk, here is an example. First we have to replace the default Container with Yadif:
$objects = new Zend_Config_Xml(APPLICATION_PATH."/config/objects.xml");
$container = new Yadif_Container($objects);
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/config/application.xml'
);
// Set Yadif as Container
$application->getBootstrap()->setContainer($container);
$application->bootstrap()
->run();
Assume you run Zend_Application with a "Db" and a "Cache" resource. These resources are loaded on every request. The objects configured in Yadif however are not instantiated until they are requested for the first time from the container. We can merge these two worlds and make use of the Application resources inside the Yadif Configuration "objects.xml", which looks like:
<?xml version="1.0" ?>
<objects>
<myModel>
<class>MyModel</class>
<arguments arg1="db" arg2="cache" />
</myModel>
</objects>
Instantiating a myModel class inside the Action Controller uses the the Database and Cache resources as constructor arguments:
class FooController extends Zend_Controller_Action {
public function barAction()
{
$container = $this->getInvokeArg('bootstrap')->getContainer();
$myModel = $container->myModel;
}
}
Given the possibilites by the Yadif_Container you are now empowered to use Dependency Injection for all your application objects and make use of Zend_Applications resource system. Furthermore any other dependency injection container, simple or complex, can also be integrated easily.
Category: PHP,
Comments (11)
Tags for this Article
Comments (11)
But you could also extend ZEnd_Controller_Action and write a generic init() or constructor that checks via reflection which services should be loaded. User: beberlei - Date: 2009/06/16 23:43
Had a minor issue though, it seems to check that the classes exists when you load the configuration. Which happens before you bootstrap the autoloader, so the classes aren't found and it throws an exception. I've forked it and removed that check and it seems fine. I don't know if there is another workaround for this I've missed? User: brenton alker - Date: 2009/06/17 03:46
Interesting post, thanks.
User: federico - Date: 2009/06/17 21:11
what we are doing in our apps is to create an actionHelper that will test with the "property_exists" method if a field need to be set in the Controller :) and then i just need to declare public variable in my controller and initialize my action helper :) User: thesorrow - Date: 2009/06/18 10:40
That is unfortunate as in the early days I actually had high hopes so will a DI container help? I'm not convinced; few people who actually know what they're doing will get great use out of it but for many it'll be "just another feature" that will be abused :(
Roll on the day when PHP has it's own DI container at the internal level... User: leslie - Date: 2009/06/20 09:22
$myModel1 = $container-myModel;
$myModel2 = $container-myModel;
do I get the same instance of myModel back? User: simon - Date: 2009/07/29 21:01
I keep getting class not found warnings as the autoloader has not been initialized before the objects are added to the container, which checks for them, am I doing something wrong? User: keith - Date: 2009/09/08 17:09
Post A Comment
Back To The Top


Unfortunately creating a new dispatcher means duplicating a lot of code. User: avi block - Date: 2009/06/16 22:36