Also add examples for the annotated setters and closure binders.
</p>
-<p>Implementation binders also allow the calling of optional setter injection methods. Providing the method parameters here is done the same way as its done in the constructor, using reflection.</p>
-
-<p>
-<pre><code><span style="color:#0000BB">$injector</span><span style="color: #007700">-></span><span style="color: #0000BB">bindImplementation</span><span style="color: #007700">(</span><span style="color: #DD0000">'DataSourceInteface'</span><span style="color: #007700">, </span><span style="color: #DD0000">'DataSourceX'</span><span style="color: #007700">)
- -></span><span style="color: #0000BB">bindSetter</span><span style="color: #007700">(</span><span style="color: #DD0000">'setLogger'</span><span style="color: #007700">);
-
-class </span><span style="color: #0000BB">DataSourceX </span><span style="color: #007700">{
- public function </span><span style="color: #0000BB">__construct</span><span style="color: #007700">(</span><span style="color: #0000BB">DependencyY $dependencyY</span><span style="color: #007700">) {
- ...
- }
-
- public function </span><span style="color: #0000BB">setLogger</span><span style="color: #007700">(</span><span style="color: #0000BB">Logger $logger</span><span style="color: #007700">) {
- ...
- }
-}
-</span></span></code></pre>
-
-</p>
-
<h5 id="toc21"> Choosing a binder</h5>
<p>Use a factory binder if:</p>
--- /dev/null
+<?php
+/**
+ * Demonstrates how to use the annotated setters binder with Horde_Injector.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Injector
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php BSD
+ * @link http://pear.horde.org/index.php?package=Injector
+ */
+
+require 'Horde/Autoloader.php';
+
+class Worker
+{
+ public $helper;
+
+ /**
+ * @inject
+ */
+ public function setHelper(Helper $h)
+ {
+ $this->helper = $h;
+ }
+}
+
+class Helper
+{
+ public function __toString()
+ {
+ return 'helper';
+ }
+}
+
+$a = new Horde_Injector(new Horde_Injector_TopLevel());
+$b = $a->getInstance('Worker');
+echo "$b->helper\n";
* @link http://pear.horde.org/index.php?package=Injector
*/
-require_once 'Horde/Autoloader.php';
+require 'Horde/Autoloader.php';
/**
* A dummy binder.
--- /dev/null
+<?php
+/**
+ * Demonstrates how to use the closure binder with Horde_Injector.
+ *
+ * PHP version 5.3+
+ *
+ * @category Horde
+ * @package Injector
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php BSD
+ * @link http://pear.horde.org/index.php?package=Injector
+ */
+
+if (version_compare(PHP_VERSION, '5.3', 'lt')) {
+ echo "PHP 5.3+ is required for the closure binder\n";
+ exit(1);
+}
+
+require 'Horde/Autoloader.php';
+
+class ClosureCreated
+{
+ public function __construct($msg)
+ {
+ $this->msg = $msg;
+ }
+ public function __toString()
+ {
+ return 'Foo: ' . $this->msg;
+ }
+}
+
+$closure = function(Horde_Injector $i) {
+ return new ClosureCreated('created by closure');
+};
+$binder = new Horde_Injector_Binder_Closure($closure);
+
+$a = new Horde_Injector(new Horde_Injector_TopLevel());
+$a->bindClosure('CC', $closure);
+
+$b = $a->getInstance('CC');
+echo "$b\n";
* @link http://pear.horde.org/index.php?package=Injector
*/
-require_once 'Horde/Autoloader.php';
+require 'Horde/Autoloader.php';
class Greet
{
<?php
/**
- * Demonstrates how to use the default factory binder with Horde_Injector.
+ * Demonstrates how to use the default implementation binder with Horde_Injector.
*
* PHP version 5
*
* @link http://pear.horde.org/index.php?package=Injector
*/
-require_once 'Horde/Autoloader.php';
+require 'Horde/Autoloader.php';
interface Person
{
* @link http://pear.horde.org/index.php?package=Injector
*/
-require_once 'Horde/Autoloader.php';
+require 'Horde/Autoloader.php';
$a = new Horde_Injector(new Horde_Injector_TopLevel());
$a->setInstance('a', 'a');
--- /dev/null
+<?php
+/**
+ * This is a binder that finds methods marked with @inject and calls them with
+ * their dependencies. It must be stacked on another binder that actually
+ * creates the instance.
+ *
+ * @author Bob Mckee <bmckee@bywires.com>
+ * @author James Pepin <james@jamespepin.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @category Horde
+ * @package Horde_Injector
+ */
+class Horde_Injector_Binder_AnnotatedSetters implements Horde_Injector_Binder
+{
+ /**
+ * @var Horde_Injector_Binder
+ */
+ private $_binder;
+
+ /**
+ * @var Horde_Injector_DependencyFinder
+ */
+ private $_dependencyFinder;
+
+ private $_setters = array();
+
+ /**
+ *
+ */
+ public function __construct(Horde_Injector_Binder $binder, Horde_Injector_DependencyFinder $dependencyFinder)
+ {
+ $this->_binder = $binder;
+ $this->_dependencyFinder = $dependencyFinder;
+ }
+
+ /**
+ * TODO
+ */
+ public function equals(Horde_Injector_Binder $otherBinder)
+ {
+ return false;
+ }
+
+ /**
+ * TODO
+ */
+ public function create(Horde_Injector $injector)
+ {
+ $instance = $this->_binder->create($injector);
+
+ $reflectionClass = new ReflectionClass(get_class($instance));
+ $this->_bindAnnotatedSetters($reflectionClass);
+ $this->_callSetters($injector, $instance);
+
+ return $instance;
+ }
+
+ /**
+ */
+ private function _bindAnnotatedSetters(ReflectionClass $reflectionClass)
+ {
+ foreach ($this->_findAnnotatedSetters($reflectionClass) as $setter) {
+ $this->_setters[] = $setter;
+ }
+ }
+
+ /**
+ * Find all public methods in $reflectionClass that are annotated with
+ * @inject.
+ *
+ * @param ReflectionClass $reflectionClass
+ *
+ * @return array
+ */
+ private function _findAnnotatedSetters(ReflectionClass $reflectionClass)
+ {
+ $setters = array();
+ foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
+ $docBlock = $reflectionMethod->getDocComment();
+ if ($docBlock) {
+ if (strpos($docBlock, '@inject') !== false) {
+ $setters[] = $reflectionMethod->name;
+ }
+ }
+ }
+
+ return $setters;
+ }
+
+ /**
+ * TODO
+ */
+ protected function _callSetters(Horde_Injector $injector, $instance)
+ {
+ foreach ($this->_setters as $setter) {
+ $reflectionMethod = new ReflectionMethod($instance, $setter);
+ $reflectionMethod->invokeArgs(
+ $instance,
+ $this->_dependencyFinder->getMethodDependencies($injector, $reflectionMethod)
+ );
+ }
+ }
+}
*/
public function create(Horde_Injector $injector)
{
-
$childInjector = $injector->createChildInjector();
$closure = $this->_closure;
return $closure($childInjector);
/**
* TODO
*/
- protected $_implementation;
+ private $_implementation;
/**
- * TODO
+ * @var Horde_Injector_DependencyFinder
*/
- protected $_setters;
+ private $_dependencyFinder;
/**
* TODO
*/
- public function __construct($implementation)
+ public function __construct($implementation, Horde_Injector_DependencyFinder $dependencyFinder)
{
$this->_implementation = $implementation;
- $this->_setters = array();
+ $this->_dependencyFinder = $dependencyFinder;
}
/**
/**
* TODO
*/
- public function bindSetter($method)
- {
- $this->_setters[] = $method;
- return $this;
- }
-
- /**
- * TODO
- */
public function equals(Horde_Injector_Binder $otherBinder)
{
return (($otherBinder instanceof Horde_Injector_Binder_Implementation) &&
{
$reflectionClass = new ReflectionClass($this->_implementation);
$this->_validateImplementation($reflectionClass);
- $instance = $this->_getInstance($injector, $reflectionClass);
- $this->_callSetters($injector, $instance);
- return $instance;
+ return $this->_getInstance($injector, $reflectionClass);
}
/**
protected function _getInstance(Horde_Injector $injector, ReflectionClass $class)
{
return $class->getConstructor()
- ? $class->newInstanceArgs($this->_getMethodDependencies($injector, $class->getConstructor()))
+ ? $class->newInstanceArgs($this->_dependencyFinder->getMethodDependencies($injector, $class->getConstructor()))
: $class->newInstance();
}
-
- /**
- * TODO
- */
- protected function _callSetters(Horde_Injector $injector, $instance)
- {
- foreach ($this->_setters as $setter) {
- $reflectionMethod = new ReflectionMethod($instance, $setter);
- $reflectionMethod->invokeArgs(
- $instance,
- $this->_getMethodDependencies($injector, $reflectionMethod)
- );
- }
- }
-
- /**
- */
- protected function _getMethodDependencies(Horde_Injector $injector, ReflectionMethod $method)
- {
- $dependencies = array();
-
- try {
- foreach ($method->getParameters() as $parameter) {
- $dependencies[] = $this->_getParameterDependency($injector, $parameter);
- }
- } catch (Horde_Injector_Exception $e) {
- throw new Horde_Injector_Exception("$method has unfulfilled dependencies ($parameter)", 0, $e);
- }
-
- return $dependencies;
- }
-
- /**
- */
- protected function _getParameterDependency(Horde_Injector $injector, ReflectionParameter $parameter)
- {
- if ($parameter->getClass()) {
- return $injector->getInstance($parameter->getClass()->getName());
- } elseif ($parameter->isOptional()) {
- return $parameter->getDefaultValue();
- }
-
- throw new Horde_Injector_Exception("Untyped parameter \$" . $parameter->getName() . "can't be fulfilled");
- }
}
+++ /dev/null
-<?php
-/**
- * TODO
- *
- * @author Bob Mckee <bmckee@bywires.com>
- * @author James Pepin <james@jamespepin.com>
- * @category Horde
- * @package Horde_Injector
- */
-class Horde_Injector_Binder_ImplementationWithSetters extends Horde_Injector_Binder_Implementation
-{
- /**
- * TODO
- */
- public function create(Horde_Injector $injector)
- {
- $reflectionClass = new ReflectionClass($this->_implementation);
- $this->_validateImplementation($reflectionClass);
- $instance = $this->_getInstance($injector, $reflectionClass);
- $this->_bindAnnotatedSetters($reflectionClass);
- $this->_callSetters($injector, $instance);
- return $instance;
- }
-
- /**
- */
- private function _bindAnnotatedSetters(ReflectionClass $reflectionClass)
- {
- foreach ($this->_findSetters($reflectionClass) as $setter) {
- $this->bindSetter($setter);
- }
- }
-
- /**
- * Find all public methods in $reflectionClass that are annotated with
- * @inject.
- *
- * @param ReflectionClass $reflectionClass
- *
- * @return array
- */
- private function _findSetters(ReflectionClass $reflectionClass)
- {
- $setters = array();
- foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
- $docBlock = $reflectionMethod->getDocComment();
- if ($docBlock) {
- if (strpos($docBlock, '@inject') !== false) {
- $setters[] = $reflectionMethod->name;
- }
- }
- }
-
- return $setters;
- }
-}
--- /dev/null
+<?php
+/**
+ * This is a simple class that uses reflection to figure out the dependencies of
+ * a method and attempts to return them using the Injector instance.
+ *
+ * @author Bob Mckee <bmckee@bywires.com>
+ * @author James Pepin <james@jamespepin.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @category Horde
+ * @package Horde_Injector
+ */
+class Horde_Injector_DependencyFinder
+{
+ /**
+ */
+ public function getMethodDependencies(Horde_Injector $injector, ReflectionMethod $method)
+ {
+ $dependencies = array();
+
+ try {
+ foreach ($method->getParameters() as $parameter) {
+ $dependencies[] = $this->getParameterDependency($injector, $parameter);
+ }
+ } catch (Horde_Injector_Exception $e) {
+ throw new Horde_Injector_Exception("$method has unfulfilled dependencies ($parameter)", 0, $e);
+ }
+
+ return $dependencies;
+ }
+
+ /**
+ */
+ public function getParameterDependency(Horde_Injector $injector, ReflectionParameter $parameter)
+ {
+ if ($parameter->getClass()) {
+ return $injector->getInstance($parameter->getClass()->getName());
+ } elseif ($parameter->isOptional()) {
+ return $parameter->getDefaultValue();
+ }
+
+ throw new Horde_Injector_Exception("Untyped parameter \$" . $parameter->getName() . "can't be fulfilled");
+ }
+}
*/
public function getBinder($interface)
{
- return new Horde_Injector_Binder_ImplementationWithSetters($interface, $interface);
+ $dependencyFinder = new Horde_Injector_DependencyFinder();
+ $implementationBinder = new Horde_Injector_Binder_Implementation($interface, $dependencyFinder);
+ return new Horde_Injector_Binder_AnnotatedSetters($implementationBinder, $dependencyFinder);
}
/**
<dir name="examples">
<dir name="Horde">
<dir name="Injector">
+ <file name="annotatedsetters.php" role="php" />
<file name="binder.php" role="php" />
+ <file name="closure.php" role="php" />
<file name="factory.php" role="php" />
<file name="implementation.php" role="php" />
<file name="setget.php" role="php" />
<dir name="Horde">
<dir name="Injector">
<dir name="Binder">
+ <file name="AnnotatedSetters.php" role="php" />
<file name="Closure.php" role="php" />
<file name="Factory.php" role="php" />
<file name="Implementation.php" role="php" />
- <file name="ImplementationWithSetters.php" role="php" />
</dir> <!-- /lib/Horde/Injector/Binder -->
<file name="Binder.php" role="php" />
+ <file name="DependencyFinder.php" role="php" />
<file name="Exception.php" role="php" />
<file name="Scope.php" role="php" />
<file name="TopLevel.php" role="php" />
<install as="Horde/Injector/Readme.html" name="doc/Horde/Injector/Readme.html" />
<install as="Horde/Injector.php" name="lib/Horde/Injector.php" />
<install as="Horde/Injector/Binder.php" name="lib/Horde/Injector/Binder.php" />
+ <install as="Horde/Injector/DependencyFinder.php" name="lib/Horde/Injector/DependencyFinder.php" />
<install as="Horde/Injector/Exception.php" name="lib/Horde/Injector/Exception.php" />
<install as="Horde/Injector/Scope.php" name="lib/Horde/Injector/Scope.php" />
<install as="Horde/Injector/TopLevel.php" name="lib/Horde/Injector/TopLevel.php" />
+ <install as="Horde/Injector/Binder/AnnotatedSetters.php" name="lib/Horde/Injector/Binder/AnnotatedSetters.php" />
<install as="Horde/Injector/Binder/Closure.php" name="lib/Horde/Injector/Binder/Closure.php" />
<install as="Horde/Injector/Binder/Factory.php" name="lib/Horde/Injector/Binder/Factory.php" />
<install as="Horde/Injector/Binder/Implementation.php" name="lib/Horde/Injector/Binder/Implementation.php" />
- <install as="Horde/Injector/Binder/ImplementationWithSetters.php" name="lib/Horde/Injector/Binder/ImplementationWithSetters.php" />
<install as="Horde/Injector/AllTests.php" name="test/Horde/Injector/AllTests.php" />
<install as="Horde/Injector/BinderTest.php" name="test/Horde/Injector/BinderTest.php" />
<install as="Horde/Injector/InjectorTest.php" name="test/Horde/Injector/InjectorTest.php" />
--- /dev/null
+<?php
+class Horde_Injector_Binder_AnnotatedSettersTest extends Horde_Test_Case
+{
+ public function testShouldCallAnnotatedSetters()
+ {
+ $instance = new Horde_Injector_Binder_AnnotatedSettersTest__TypedSetterDependency();
+ $binder = new Horde_Injector_Binder_AnnotatedSettersTest__EmptyBinder($instance);
+ $df = new Horde_Injector_DependencyFinder();
+ $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+ $annotatedSettersBinder = new Horde_Injector_Binder_AnnotatedSetters($binder, $df);
+
+ $this->assertNull($instance->dep);
+ $newInstance = $annotatedSettersBinder->create($injector);
+ $this->assertType('Horde_Injector_Binder_AnnotatedSettersTest__NoDependencies', $newInstance->dep);
+ }
+}
+
+/**
+ * Used by preceeding tests!!!
+ */
+
+class Horde_Injector_Binder_AnnotatedSettersTest__EmptyBinder implements Horde_Injector_Binder
+{
+ public $instance;
+ public function __construct($instance)
+ {
+ $this->instance = $instance;
+ }
+
+ public function create(Horde_Injector $injector)
+ {
+ return $this->instance;
+ }
+
+ public function equals(Horde_Injector_Binder $otherBinder)
+ {
+ return false;
+ }
+}
+
+class Horde_Injector_Binder_AnnotatedSettersTest__NoDependencies
+{
+}
+
+class Horde_Injector_Binder_AnnotatedSettersTest__TypedSetterDependency
+{
+ public $dep;
+
+ /**
+ * @inject
+ */
+ public function setDep(Horde_Injector_Binder_AnnotatedSettersTest__NoDependencies $dep)
+ {
+ $this->dep = $dep;
+ }
+}
<?php
class Horde_Injector_Binder_ImplementationTest extends Horde_Test_Case
{
+ public function setUp()
+ {
+ $this->df = new Horde_Injector_DependencyFinder();
+ }
+
public function testShouldReturnBindingDetails()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'IMPLEMENTATION'
+ 'IMPLEMENTATION',
+ $this->df
);
$this->assertEquals('IMPLEMENTATION', $implBinder->getImplementation());
public function testShouldCreateInstanceOfClassWithNoDependencies()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__NoDependencies'
+ 'Horde_Injector_Binder_ImplementationTest__NoDependencies',
+ $this->df
);
$this->assertType(
public function testShouldCreateInstanceOfClassWithTypedDependencies()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__TypedDependency'
+ 'Horde_Injector_Binder_ImplementationTest__TypedDependency',
+ $this->df
);
$createdInstance = $implBinder->create($this->_getInjectorReturnsNoDependencyObject());
public function testShouldThrowExceptionWhenTryingToCreateInstanceOfClassWithUntypedDependencies()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__UntypedDependency'
+ 'Horde_Injector_Binder_ImplementationTest__UntypedDependency',
+ $this->df
);
$implBinder->create($this->_getInjectorNeverCallMock());
public function testShouldUseDefaultValuesFromUntypedOptionalParameters()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__UntypedOptionalDependency'
+ 'Horde_Injector_Binder_ImplementationTest__UntypedOptionalDependency',
+ $this->df
);
$createdInstance = $implBinder->create($this->_getInjectorNeverCallMock());
public function testShouldThrowExceptionIfRequestedClassIsNotDefined()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'CLASS_DOES_NOT_EXIST'
+ 'CLASS_DOES_NOT_EXIST',
+ $this->df
);
$implBinder->create($this->_getInjectorNeverCallMock());
/**
* @expectedException Horde_Injector_Exception
*/
- public function testShouldThrowExcpetionIfImplementationIsAnInterface()
+ public function testShouldThrowExceptionIfImplementationIsAnInterface()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__Interface'
+ 'Horde_Injector_Binder_ImplementationTest__Interface',
+ $this->df
);
$implBinder->create($this->_getInjectorNeverCallMock());
/**
* @expectedException Horde_Injector_Exception
*/
- public function testShouldThrowExcpetionIfImplementationIsAnAbstractClass()
+ public function testShouldThrowExceptionIfImplementationIsAnAbstractClass()
{
$implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__AbstractClass'
+ 'Horde_Injector_Binder_ImplementationTest__AbstractClass',
+ $this->df
);
$implBinder->create($this->_getInjectorNeverCallMock());
}
- public function testShouldCallSetterMethodsWithNoDependenciesIfRequested()
- {
- $implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__SetterNoDependencies'
- );
- $implBinder->bindSetter('setDependency');
-
- $instance = $implBinder->create($this->_getInjectorNeverCallMock());
-
- $this->assertType(
- 'Horde_Injector_Binder_ImplementationTest__SetterNoDependencies',
- $instance
- );
-
- $this->assertEquals('CALLED', $instance->setterDep);
- }
-
- public function testShouldCallSetterMethodsWithDependenciesIfRequested()
- {
- $implBinder = new Horde_Injector_Binder_Implementation(
- 'Horde_Injector_Binder_ImplementationTest__SetterHasDependencies'
- );
- $implBinder->bindSetter('setDependency');
-
- $instance = $implBinder->create($this->_getInjectorReturnsNoDependencyObject());
-
- $this->assertType(
- 'Horde_Injector_Binder_ImplementationTest__SetterHasDependencies',
- $instance
- );
-
- $this->assertType(
- 'Horde_Injector_Binder_ImplementationTest__NoDependencies',
- $instance->setterDep
- );
- }
-
private function _getInjectorNeverCallMock()
{
$injector = $this->getMockSkipConstructor('Horde_Injector', array('getInstance'));
*/
public function binderIsEqualProvider()
{
+ $df = new Horde_Injector_DependencyFinder();
return array(
array(
- new Horde_Injector_Binder_Implementation('foobar'),
+ new Horde_Injector_Binder_Implementation('foobar', $df),
new Horde_Injector_Binder_Factory('factory', 'method'),
false, "Implementation_Binder should not equal Factory binder"
),
array(
- new Horde_Injector_Binder_Implementation('foobar'),
- new Horde_Injector_Binder_Implementation('foobar'),
+ new Horde_Injector_Binder_Implementation('foobar', $df),
+ new Horde_Injector_Binder_Implementation('foobar', $df),
true, "Implementation Binders both reference concrete class foobar"
),
array(
- new Horde_Injector_Binder_Implementation('foobar'),
- new Horde_Injector_Binder_Implementation('otherimpl'),
+ new Horde_Injector_Binder_Implementation('foobar', $df),
+ new Horde_Injector_Binder_Implementation('otherimpl', $df),
false, "Implementation Binders do not have same implementation set"
),
array(
new Horde_Injector_Binder_Factory('factory', 'method'),
- new Horde_Injector_Binder_Implementation('foobar'),
+ new Horde_Injector_Binder_Implementation('foobar', $df),
false, "Implementation_Binder should not equal Factory binder"
),
array(
{
// we need to set a class for an instance on the parent
$injector = new Horde_Injector(new Horde_Injector_TopLevel());
- $injector->addBinder('FooBarInterface', new Horde_Injector_Binder_Implementation('StdClass'));
+ $df = new Horde_Injector_DependencyFinder();
+ $injector->addBinder('FooBarInterface', new Horde_Injector_Binder_Implementation('StdClass', $df));
// getInstance will save $returnedObject and return it again later when FooBarInterface is requested
$returnedObject = $injector->getInstance('FooBarInterface');
$childInjector = $injector->createChildInjector();
// add same binding again to child
- $childInjector->addBinder('FooBarInterface', new Horde_Injector_Binder_Implementation('StdClass'));
+ $childInjector->addBinder('FooBarInterface', new Horde_Injector_Binder_Implementation('StdClass', $df));
$this->assertSame($returnedObject, $childInjector->getInstance('FooBarInterface'),
"Child should have returned object reference from parent because added binder was identical to the parent binder");