--- /dev/null
+<?php
+/**
+ * A binder object for binding an interface to a closure.
+ *
+ * An interface may be bound to a closure. That closure must accept a
+ * Horde_Injector and return an object that satisfies the instance
+ * requirement. For example:
+ *
+ * $injector->bindClosure('database', function($injector) { return new my_mysql(); });
+ *
+ * @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_Closure implements Horde_Injector_Binder
+{
+ /**
+ * TODO
+ */
+ private $_closure;
+
+ /**
+ * Create a new Horde_Injector_Binder_Closure instance.
+ *
+ * @param string $closure The closure to use for creating objects.
+ */
+ public function __construct($closure)
+ {
+ $this->_closure = $closure;
+ }
+
+ /**
+ * TODO
+ */
+ public function equals(Horde_Injector_Binder $otherBinder)
+ {
+ return (($otherBinder instanceof Horde_Injector_Binder_Closure) &&
+ ($otherBinder->getClosure() == $this->_closure));
+ }
+
+ /**
+ * Get the closure that this binder was bound to.
+ *
+ * @return callable The closure this binder is bound to.
+ */
+ public function getClosure()
+ {
+ return $this->_closure;
+ }
+
+ /**
+ * Create instance using a closure
+ *
+ * If the closure depends on a Horde_Injector we want to limit its scope
+ * so it cannot change anything that effects any higher-level scope. A
+ * closure should not have the responsibility of making a higher-level
+ * scope change.
+ * To enforce this we create a new child Horde_Injector. When a
+ * Horde_Injector is requested from a Horde_Injector it will return
+ * itself. This means that the closure will only ever be able to work on
+ * the child Horde_Injector we give it now.
+ *
+ * @param Horde_Injector $injector Injector object.
+ *
+ * @return TODO
+ */
+ public function create(Horde_Injector $injector)
+ {
+
+ $childInjector = $injector->createChildInjector();
+ $closure = $this->_closure;
+ return $closure($childInjector);
+ }
+}
<dir name="Horde">
<dir name="Injector">
<dir name="Binder">
+ <file name="Closure.php" role="php" />
<file name="Factory.php" role="php" />
<file name="Implementation.php" role="php" />
</dir> <!-- /lib/Horde/Injector/Binder -->
<dir name="Horde">
<dir name="Injector">
<dir name="Binder">
+ <file name="ClosureTest.php" role="test" />
<file name="FactoryTest.php" role="test" />
<file name="ImplementationTest.php" role="test" />
</dir> <!-- /test/Horde/Injector/Binder -->
<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/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/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" />
<install as="Horde/Injector/phpunit.xml" name="test/Horde/Injector/phpunit.xml" />
+ <install as="Horde/Injector/Binder/ClosureTest.php" name="test/Horde/Injector/Binder/ClosureTest.php" />
<install as="Horde/Injector/Binder/FactoryTest.php" name="test/Horde/Injector/Binder/FactoryTest.php" />
<install as="Horde/Injector/Binder/ImplementationTest.php" name="test/Horde/Injector/Binder/ImplementationTest.php" />
</filelist>
--- /dev/null
+<?php
+class Horde_Injector_Binder_ClosureTest extends Horde_Test_Case
+{
+ public function testShouldCallClosure()
+ {
+ $childInjector = $this->getMockSkipConstructor('Horde_Injector', array('createInstance', 'getInstance'));
+ $injector = $this->getMockSkipConstructor('Horde_Injector', array('createChildInjector'));
+ $injector->expects($this->once())
+ ->method('createChildInjector')
+ ->with()
+ ->will($this->returnValue($childInjector));
+
+ $closureBinder = new Horde_Injector_Binder_Closure(
+ function (Horde_Injector $injector) { return 'INSTANCE'; }
+ );
+
+ $this->assertEquals('INSTANCE', $closureBinder->create($injector));
+ }
+
+ /**
+ * The closure binder should pass a child injector object to the closure, so that
+ * any configuration that happens in the closure will not bleed into global scope
+ */
+ public function testShouldPassChildInjectorToClosure()
+ {
+ $closure = function (Horde_Injector $injector) { return $injector; };
+
+ $binder = new Horde_Injector_Binder_Closure($closure);
+
+ $injector = new ClosureInjectorMockTestAccess(new Horde_Injector_TopLevel());
+ $injector->TEST_ID = "PARENTINJECTOR";
+
+ // calling create should pass a child injector to the factory
+ $childInjector = $binder->create($injector);
+
+ // now the factory should have a reference to a child injector
+ $this->assertEquals($injector->TEST_ID . "->CHILD", $childInjector->TEST_ID, "Incorrect Injector passed to closure");
+ }
+
+ public function testShouldReturnBindingDetails()
+ {
+ $closure = function (Horde_Injector $injector) {};
+ $closureBinder = new Horde_Injector_Binder_Closure(
+ $closure
+ );
+
+ $this->assertEquals($closure, $closureBinder->getClosure());
+ }
+}
+
+class ClosureInjectorMockTestAccess extends Horde_Injector
+{
+ public function createChildInjector()
+ {
+ $child = new self($this);
+ $child->TEST_ID = $this->TEST_ID . "->CHILD";
+ return $child;
+ }
+}