View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFPROP:Proposal Zone Template}

{zone-data:component-name}
Zend_Container
{zone-data}

{zone-data:proposer-list}
[Bradley Holt|~bradley.holt]
{zone-data}

{zone-data:liaison}
TBD
{zone-data}

{zone-data:revision}
1.0 - 28 July 2008: Initial Draft.
{zone-data}

{zone-data:overview}
Zend_Container is a simple dependency injection container that is intended to replace use of Zend_Registry and class-based singletons. This is a proposal for a simpler dependency injection component than [Zend_Di|http://framework.zend.com/wiki/display/ZFPROP/Zend_Di+-+Federico+Cargnelutti] and [Zend_Context|http://framework.zend.com/wiki/display/ZFPROP/Zend_Context+-+Peter+Kovacs]. Note that the word "component" is overloaded in this proposal. This proposal is for a Zend Framework "component" but it also uses the word "component" to refer to an object managed by Zend_Container.

The author of this component is not entirely sure that a dependency injection component is inline with Zend Framework's balance of power and simplicity. However, the author believes that if Zend Framework is to have a dependency injection component that this component should be as simple as possible and designed to solve a limited set of use cases.
{zone-data}

{zone-data:references}
* [PicoContainer|http://www.picocontainer.org/]
* [Inversion of Control Containers and the Dependency Injection pattern|http://martinfowler.com/articles/injection.html]
{zone-data}

{zone-data:requirements}

* This component *will* replace Zend_Registry.
* This component *will* use reflection.
* This component *will not* allow more than one instance of a given type to be registered in a given container.
* This component *will not* act as a factory (except for creating the single instance of a given type).
* This component *will* allow manual component registration or configuration-based component registration.
* This component *will* allow a hierarchy of containers.
* This component *will* allow a container to have zero or one parent container.
* This component *will* allow a container to have zero or more child containers.
* This component *will* allow a child container to access components in its parent container.
* This component *will not* allow a parent container to access components in its child container(s).

{zone-data}

{zone-data:dependencies}
* Zend_Reflection
* Zend_Loader
* Zend_Config (optional)
* Zend_Exception
{zone-data}

{zone-data:operation}
Zend_Container provides a simple dependency injection container in which components may be registered. A component may be registered under its own type or one of its parent types (class or interface). Only one component of a given type may be registered in a given container. This means that two objects of the same type _may_ be registered in the same container if they are registered under different parent types. This also means that two objects of different types may _not_ be registered in the same container if an attempt is made to register them under the same parent type.

Components can be registered manually using calls to the addComponent method or through configuration. Registering components through configuration simply calls the appropriate addComponent methods for you based on the contents of your configuration object. Dependencies to be injected are detected using reflection on added components.

A hierarchy of containers can be created by assigning a parent container upon instantiating a new container. When attempting to get a component of a given type a container will first look in itself and then its parent container (if applicable). The parent container will recurse to its parent container until the component is found or there are no more parent containers. This means that if a component of the same type is registered in a child and a parent container, the component in the child container will be used.
{zone-data}

{zone-data:milestones}
* Milestone 1: Write proposal
* Milestone 2: Design interface
* Milestone 3: Use cases
* Milestone 3: Gather feedback and incorporate design changes
* Milestone 4: Approval of Zend_Reflection
* Milestone 5: Review by the Zend Team
* Milestone 6: Write unit tests
* Milestone 7: Develop full implementation
* Milestone 8: Write API doc
* Milestone 9: Documentation

{zone-data}

{zone-data:class-list}
* Zend_Container
* Zend_Container_Components
* Zend_Container_Exception
* Zend_Container_Types
{zone-data}

{zone-data:use-cases}
||UC-01: Manually wiring dependencies||
{code:php}
class Zoo {

/**
* @var Feline
*/
protected $_feline;

/**
* @var Canine
*/
protected $_canine;

/**
* Sets the Feline for the Zoo to have.
*
* @param Feline $feline
* @return void
*/
public function setFeline(Feline $feline) {
$this->_feline = $feline;
}

/**
* Sets the Canine for the Zoo to have.
*
* @param Canine $canine
* @return void
*/
public function setCanine(Canine $canine) {
$this->_canine = $canine;
}

}

$container = new Zend_Container();
//note the ability to specify class or interface and class
$container->addComponent('Zoo')
->addComponent('Feline', 'Tiger')
->addComponent('Canine', 'Wolf');
$zoo = $container->getComponent('Zoo');
{code}

The above code is the equivalent of (assuming setter injection):

{code:php}
$feline = new Tiger();
$canine = new Wolf();
$zoo = new Zoo();
$zoo->setFeline($feline);
$zoo->setCanine($canine);
{code}

||UC-02: A container with a parent container||
{code:php}
$rootContainer = new Zend_Container();
//passing in the root container so the child is aware of its parent
$childContainer = new Zend_Container($rootContainer);
//note again that we can add either class or class and interface
$rootContainer->addComponent('SomeClass');
$childContainer->addComponent('SomeClass');
$instanceA = $rootContainer->getComponent('SomeClass');
/*
Child containers should have access to their parent's components (but not the other
way round) so if we hadn't added the component specifically to the child container
this next line would have given us instanceA.
*/
$instanceB = $childContainer->getComponent('SomeClass');
echo (int) ($instanceA === $instanceB); // echoes "0" (false)
{code}

{zone-data}

{zone-data:skeletons}
{code:php}

class Zend_Container_Exception extends Zend_Exception {}

class Zend_Container {

/**
* @var Zend_Container
*/
private $_parent;

/**
* @var Zend_Container_Types
*/
private $_types;

/**
* @var Zend_Container_Components
*/
private $_components;

/**
* Instantiates the container.
*
* @param Zend_Container $parent
*/
public function __construct(Zend_Container $parent = null) {}

/**
* Adds the component to the types ArrayObject if given a type. Adds
* the component to the types ArrayObject and components ArrayObject
* if given an already instantiated component.
*
* @param string|Object $typeOrComponent type or component to add
* @param string|Object $implTypeOrComponent optional implementation type or component
* @throws Zend_Container_Exception if the type has already been added
* @return Zend_Container provides a fluent interface
*/
public function addComponent($typeOrComponent, $implTypeOrComponent = null) {}

/**
* Gets a component from the container. First searches the components
* ArrayObject to see if the component has been instantiated. If the
* component is not found in the components ArrayObject then the types
* ArrayObject is searched and the component is instantiated and added
* to the components ArrayObject if found. If not found in either
* components or types then getComponent on the parent container (if
* applicable) is called. If no matching component is found, a
* Zend_Container_Exception is thrown.
*
* @param string $type
* @throws Zend_Container_Exception if no component is found
* @return object the component
*/
public function getComponent($type) {}

}

class Zend_Container_Types extends ArrayObject {}

class Zend_Container_Components extends ArrayObject {}

{code}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>