<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[
Zend_Di is a dependency injector component. It minimizes coupling between groups of classes, makes unit testing much simpler, and provides an easy way to re-configure a package to use custom implementations of components. The architecture of the Zend_Di component is based on the following concepts:
Benefits of using a DI Container:Zend Framework: Zend_Di Component Proposal
Proposed Component Name
Zend_Di
Developer Notes
http://framework.zend.com/wiki/display/ZFDEV/Zend_Di
Proposers
My E-mail Address
Revision
0.1 - 21 November 2007: Initial Proposal.
0.2 - 29 November 2007: Added a real-life example.
0.3 - 30 November 2007: New changes to the API. (wiki revision: 57)Table of Contents
1. Overview
2. References
3. Component Requirements, Constraints, and Acceptance Criteria
- This component will use Reflection.
4. Dependencies on Other Framework Components
- Zend_Config (optional)
- Zend_Exception
- Zend_Loader
5. Theory of Operation
Zend_Di provides generic factory classes that instantiate instances of classes. These instances are then configured by the container, allowing construction logic to be reused on a broader level. For example:
Once we separate configuration from use, we can easily test the Car with different Engines. It's just a matter of re-configuring the package and injecting Zend_Car_Parts_Engine_Gas instead of Zend_Car_Parts_Engine_Fuel.
6. Milestones / Tasks
- Milestone 1: [DONE] Design interface
- Milestone 2: [DONE] Write proposal
- Milestone 3: [IN PROGRESS] Gather feedback and revise design as necessary
- Milestone 4: Review by the Zend team
- Milestone 5: Develop full implementation and unit tests
- Milestone 6: Documentation
- Milestone 7: Future enhancements
7. Class Index
- Zend_Di_Container
- Zend_Di_Exception
- Zend_Di_Container_Manager
- Zend_Di_Container_Exception
- Zend_Di_Container_Storage
- Zend_Di_Container_Storage_Interface
- Zend_Di_Component_Factory
- Zend_Di_Component_Exception
8. Use Cases
Zend_Di handles injections via the constructor or setters methods. In addition, the component allows the user to map out specifications for components and their dependencies in a configuration file and generate the objects based on that specification.
The cases below assume that the following classes have been defined:
Configuration
The configuration is typically set up in a different file. Each package can have its own configuration file, a PHP class or an XML file. The configuration file holds the components specifications and package dependencies.
You can pass an instance of Zend_Config via the constructor, or set a configuration array using the setConfigArray() method.
| UC-01 |
|---|
The two major flavors of Dependency Injection are Setter Injection (injection via setter methods) and Constructor Injection (injection via constructor arguments). Zend_Di provides support for both, and even allows you to mix the two when configuring the one object.
Constructor dependency injection
When a class is loaded, the constructor method is selected by default.
| UC-02 |
|---|
| UC-03 |
|---|
| UC-04 |
|---|
Users can map out specifications for components and their dependencies. So whenever a class is loaded, Zend_Di will inject the dependencies automatically. For example:
| UC-05 |
|---|
Setter dependency injection
| UC-06 |
|---|
Zend_Di injects dependencies using the top-down fashion, starting with the constructor and ending with the setter methods.
| UC-07 |
|---|
Users can map out specifications for a component:
| UC-08 |
|---|
Containers
You can tell Zend_Di what classes to manage by adding them to a container (the order of registration has no significance). Containers can be retrieved using the Zend_Di_Container_Manager::getContainer() method, which returns an instance of Zend_Di_Container_Storage_Interface.
| UC-09 |
|---|
You can register your own container as long as you pass an instance of Zend_Di_Container_Storage_Interface. New containers can be register using the registerContainer() method.
| UC-10 |
|---|
Real-life Example
Zend_Import is a component I use to import SQL, XML and CSV files into the database. I created the Zend_Di component because:
- I needed a way to test Zend_Import with different file formats and using different protocols.
- I wanted to minimize coupling between the components.
- I wanted other developers to know the components Zend_Import was using by looking at the config file or API.
- I wanted to minimize the risk of having hidden dependencies.
The CSV files are retrieved using FTP, and the XML files using HTTP. Because the script is run by a Cron job, I also added logging capabilities to Zend_Import.
The problem I faced when designing Zend_Import was the amount of dependencies the component had: Zend_Import_Protocol, Zend_Db, Zend_Log, Zend_Log_Writer and Zend_Mail (recently added, and not included in this example). So I decided to use the dependency injection pattern to solve this problem.
Zend_Di has been tested with Zend_Import in a staging server. Below is a prototype of the Zend_Import package, and a brief example of how Zend_Import takes full advantage of the DI pattern.
Zend_Import package:
Zend_Import_Dependency class:
The name Dependency.php was given to the file in reference to the DI pattern.
Prototype of the Zend_Import_Format_Csv class:
Usage:
This will output:
9. Class Skeletons
- Zend_Di_Container
- Zend_Di_Component_Factory
- Zend_Di_Container_Manager