From 8fed4555b5db757ec32052f15f5bb32f6ec40034 Mon Sep 17 00:00:00 2001 From: Chuck Hagenbuch Date: Sun, 14 Mar 2010 14:53:42 -0400 Subject: [PATCH] Add closure Binder --- .../Injector/lib/Horde/Injector/Binder/Closure.php | 76 ++++++++++++++++++++++ framework/Injector/package.xml | 4 ++ .../test/Horde/Injector/Binder/ClosureTest.php | 59 +++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 framework/Injector/lib/Horde/Injector/Binder/Closure.php create mode 100644 framework/Injector/test/Horde/Injector/Binder/ClosureTest.php diff --git a/framework/Injector/lib/Horde/Injector/Binder/Closure.php b/framework/Injector/lib/Horde/Injector/Binder/Closure.php new file mode 100644 index 000000000..29fa10d6b --- /dev/null +++ b/framework/Injector/lib/Horde/Injector/Binder/Closure.php @@ -0,0 +1,76 @@ +bindClosure('database', function($injector) { return new my_mysql(); }); + * + * @author Bob Mckee + * @author James Pepin + * @author Chuck Hagenbuch + * @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); + } +} diff --git a/framework/Injector/package.xml b/framework/Injector/package.xml index 06337f492..7b77ebc29 100644 --- a/framework/Injector/package.xml +++ b/framework/Injector/package.xml @@ -59,6 +59,7 @@ + @@ -74,6 +75,7 @@ + @@ -104,12 +106,14 @@ + + diff --git a/framework/Injector/test/Horde/Injector/Binder/ClosureTest.php b/framework/Injector/test/Horde/Injector/Binder/ClosureTest.php new file mode 100644 index 000000000..5b7164493 --- /dev/null +++ b/framework/Injector/test/Horde/Injector/Binder/ClosureTest.php @@ -0,0 +1,59 @@ +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; + } +} -- 2.11.0