From 9f7ed802ea5d661f8f8bac966788c33b8213fc16 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Thu, 15 Apr 2021 13:57:43 +0300 Subject: [PATCH] binder bind instance method --- application/Espo/Core/Binding/Binder.php | 43 ++++++++++--- .../Espo/Core/Binding/ContextualBinder.php | 63 ++++++++++++++++--- .../Core/Binding/BindingContainerTest.php | 56 ++++++++++++++--- 3 files changed, 136 insertions(+), 26 deletions(-) diff --git a/application/Espo/Core/Binding/Binder.php b/application/Espo/Core/Binding/Binder.php index 37961bc20d..5337632591 100644 --- a/application/Espo/Core/Binding/Binder.php +++ b/application/Espo/Core/Binding/Binder.php @@ -48,9 +48,7 @@ class Binder */ public function bindImplementation(string $key, string $implementationClassName): self { - if (!$key || $key[0] === '$') { - throw new LogicException("Can't binding a parameter name globally."); - } + $this->validateBindingKey($key); $this->data->addGlobal( $key, @@ -68,9 +66,7 @@ class Binder */ public function bindService(string $key, string $serviceName): self { - if (!$key || $key[0] === '$') { - throw new LogicException("Can't binding a parameter name globally."); - } + $this->validateBindingKey($key); $this->data->addGlobal( $key, @@ -81,16 +77,14 @@ class Binder } /** - * Bind an interface or parameter name to a callback. + * Bind an interface to a callback. * * @param $key An interface or interface with a parameter name (`Interface $name`). * @param $callback A callback that will resolve a dependency. */ public function bindCallback(string $key, callable $callback): self { - if (!$key || $key[0] === '$') { - throw new LogicException("Can't binding a parameter name globally."); - } + $this->validateBindingKey($key); $this->data->addGlobal( $key, @@ -100,6 +94,24 @@ class Binder return $this; } + /** + * Bind an interface to a specific instance. + * + * @param $key An interface or interface with a parameter name (`Interface $name`). + * @param $instance An instance. + */ + public function bindInstance(string $key, object $instance): self + { + $this->validateBindingKey($key); + + $this->data->addGlobal( + $key, + Binding::createFromValue($instance) + ); + + return $this; + } + /** * Creates a contextual binder. * @@ -109,4 +121,15 @@ class Binder { return new ContextualBinder($this->data, $className); } + + private function validateBindingKey(string $key): void + { + if (!$key) { + throw new LogicException("Bad binding."); + } + + if ($key[0] === '$') { + throw new LogicException("Can't binding a parameter name w/o an interface globally."); + } + } } diff --git a/application/Espo/Core/Binding/ContextualBinder.php b/application/Espo/Core/Binding/ContextualBinder.php index fbfa6b84f8..9cbdfa2c9b 100644 --- a/application/Espo/Core/Binding/ContextualBinder.php +++ b/application/Espo/Core/Binding/ContextualBinder.php @@ -51,9 +51,7 @@ class ContextualBinder */ public function bindImplementation(string $key, string $implementationClassName): self { - if (!$key || $key[0] === '$') { - throw new LogicException("Bad binding."); - } + $this->validateBindingKeyNoParameterName($key); $this->data->addContext( $this->className, @@ -67,14 +65,12 @@ class ContextualBinder /** * Bind an interface to a specific service. * - * @param $key An interface, parameter name (`$name`) or interface with a parameter name (`Interface $name`). + * @param $key An interface or interface with a parameter name (`Interface $name`). * @param $serviceName A service name. */ public function bindService(string $key, string $serviceName): self { - if (!$key || $key[0] === '$') { - throw new LogicException("Bad binding."); - } + $this->validateBindingKeyNoParameterName($key); $this->data->addContext( $this->className, @@ -88,11 +84,13 @@ class ContextualBinder /** * Bind an interface or parameter name to a specific value. * - * @param $key An interface, parameter name (`$name`) or interface with a parameter name (`Interface $name`). + * @param $key Parameter name (`$name`) or interface with a parameter name (`Interface $name`). * @param $value A value of any type. */ public function bindValue(string $key, $value): self { + $this->validateBindingKeyParameterName($key); + $this->data->addContext( $this->className, $key, @@ -102,14 +100,36 @@ class ContextualBinder return $this; } + /** + * Bind an interface to a specific instance. + * + * @param $key An interface or interface with a parameter name (`Interface $name`). + * @param $instance An instance. + */ + public function bindInstance(string $key, object $instance): self + { + $this->validateBindingKeyNoParameterName($key); + + $this->data->addContext( + $this->className, + $key, + Binding::createFromValue($instance) + ); + + return $this; + } + /** * Bind an interface or parameter name to a callback. * - * @param $key An interface, parameter name (`$name`) or interface with a parameter name (`Interface $name`). + * @param $key An interface, parameter name (`$name`) or + * interface with a parameter name (`Interface $name`). * @param $callback A callback that will resolve a dependency. */ public function bindCallback(string $key, callable $callback): self { + $this->validateBinding($key); + $this->data->addContext( $this->className, $key, @@ -118,4 +138,29 @@ class ContextualBinder return $this; } + + private function validateBinding(string $key): void + { + if (!$key) { + throw new LogicException("Bad binding."); + } + } + + private function validateBindingKeyNoParameterName(string $key): void + { + $this->validateBinding($key); + + if ($key[0] === '$') { + throw new LogicException("Can't bind a parameter name w/o an interface."); + } + } + + private function validateBindingKeyParameterName(string $key): void + { + $this->validateBinding($key); + + if (strpos($key, '$') === false) { + throw new LogicException("Can't bind w/o a parameter name."); + } + } } diff --git a/tests/unit/Espo/Core/Binding/BindingContainerTest.php b/tests/unit/Espo/Core/Binding/BindingContainerTest.php index d70c75568e..a293efcbee 100644 --- a/tests/unit/Espo/Core/Binding/BindingContainerTest.php +++ b/tests/unit/Espo/Core/Binding/BindingContainerTest.php @@ -43,9 +43,14 @@ use ReflectionNamedType; class BindingContainerTest extends \PHPUnit\Framework\TestCase { + /** + * @var Binder + */ + private $binder; + protected function setUp() : void { - $this->loader = $this->getMockBuilder(BindingLoader::class)->disableOriginalConstructor()->getMock(); + $this->loader = $this->createMock(BindingLoader::class); $this->data = new BindingData(); @@ -59,7 +64,7 @@ class BindingContainerTest extends \PHPUnit\Framework\TestCase protected function createClassMock(string $className) : ReflectionClass { - $class = $this->getMockBuilder(ReflectionClass::class)->disableOriginalConstructor()->getMock(); + $class = $this->createMock(ReflectionClass::class); $class ->expects($this->any()) @@ -71,11 +76,11 @@ class BindingContainerTest extends \PHPUnit\Framework\TestCase protected function createParamMock(string $name, ?string $className = null) : ReflectionParameter { - $param = $this->getMockBuilder(ReflectionParameter::class)->disableOriginalConstructor()->getMock(); + $param = $this->createMock(ReflectionParameter::class); $class = null; - $type = $this->getMockBuilder(ReflectionNamedType::class)->disableOriginalConstructor()->getMock(); + $type = $this->createMock(ReflectionNamedType::class); if ($className) { $class = $this->createClassMock($className); @@ -114,7 +119,7 @@ class BindingContainerTest extends \PHPUnit\Framework\TestCase return $param; } - protected function createContainer() + protected function createContainer(): BindingContainer { return new BindingContainer($this->loader->load()); } @@ -292,6 +297,44 @@ class BindingContainerTest extends \PHPUnit\Framework\TestCase $this->assertIsCallable($binding->getValue()); } + public function testBindInstance() + { + $className = 'Espo\\Core\\Application'; + + $instance = $this->createMock($className); + + $this->binder->bindInstance($className, $instance); + + $param = $this->createParamMock('test', $className); + + $binding = $this->createContainer()->get(null, $param); + + $this->assertEquals(Binding::VALUE, $binding->getType()); + + $this->assertSame($instance, $binding->getValue()); + } + + public function testContextBindInstance() + { + $className = 'Espo\\Core\\Application'; + + $instance = $this->createMock($className); + + $this->binder + ->for('Espo\\Context') + ->bindInstance($className, $instance); + + $param = $this->createParamMock('test', $className); + + $class = $this->createClassMock('Espo\\Context'); + + $binding = $this->createContainer()->get($class, $param); + + $this->assertEquals(Binding::VALUE, $binding->getType()); + + $this->assertSame($instance, $binding->getValue()); + } + public function testContextGetCallback() { $this->binder @@ -373,7 +416,6 @@ class BindingContainerTest extends \PHPUnit\Framework\TestCase $this->assertEquals('Espo\\ImplTest', $binding->getValue()); } - public function testNoContextClassName() { $this->binder @@ -439,7 +481,7 @@ class BindingContainerTest extends \PHPUnit\Framework\TestCase $this->binder ->for('Espo\\Context') - ->bindValue('Espo\\SomeClass', $instance); + ->bindValue('Espo\\SomeClass $test', $instance); $class = $this->createClassMock('Espo\\Context');