Permalink
Browse files

[[DI] Implemented injection through factory class.

  • Loading branch information...
bbankowski committed Nov 8, 2018
1 parent abd1ab9 commit dfa6c01f72a9d4b20fb57da818e52b54b399d1d0
View
@@ -10,6 +10,7 @@ Enhancements:
* [ORM] Implemented upsert functionality (Model::createOrUpdate).
* [DI] Added constructor injection for arguments with types defined (issue #265).
* [DI] Implemented injection of private fields for parent class.
* [DI] Implemented injection through factory class.
* [Utilities] Added equalsIgnoreCase to Functions and FluentFunctions (issue #263).
* [Utilities] Fixed Clock to support DST changes when adding hours, minutes or seconds.
@@ -3,6 +3,7 @@
* Copyright (c) Ouzo contributors, http://ouzoframework.org
* This file is made available under the MIT License (view the LICENSE file for more information).
*/
namespace Ouzo\Injection;
class Binder
@@ -17,6 +18,8 @@ class Binder
private $instance;
/** @var string */
private $name;
/** @var string */
private $factoryClassName;
/**
* @param string $className
@@ -48,6 +51,16 @@ public function to($boundClassName)
return $this;
}
/**
* @param string $factoryClassName
* @return $this
*/
public function throughFactory($factoryClassName)
{
$this->factoryClassName = $factoryClassName;
return $this;
}
/**
* @return string
*/
@@ -97,4 +110,12 @@ public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function getFactoryClassName()
{
return $this->factoryClassName;
}
}
@@ -29,7 +29,7 @@ public function __construct(InjectorConfig $config, $injector)
* @param string $name
* @return Binder
*/
public function getBinder($className, $name)
public function getBinder($className, $name = '')
{
if ($className == Injector::class) {
return $this->injectorBinder;
@@ -0,0 +1,8 @@
<?php
namespace Ouzo\Injection;
interface Factory
{
public function create();
}
@@ -29,7 +29,7 @@ public function __construct(InjectorConfig $config = null, AnnotationMetadataPro
$this->injectorConfig = $config ?: new InjectorConfig();
$this->bindings = new Bindings($this->injectorConfig, $this);
$this->factory = new InstanceFactory($this->bindings, $provider ?: new DocCommentExtractor());
$this->repository = new InstanceRepository();
$this->repository = new InstanceRepository($this->bindings);
}
/** @return InjectorConfig */
@@ -3,6 +3,7 @@
* Copyright (c) Ouzo contributors, http://ouzoframework.org
* This file is made available under the MIT License (view the LICENSE file for more information).
*/
namespace Ouzo\Injection;
use BadMethodCallException;
@@ -12,18 +13,36 @@ class InstanceRepository
/** @var object[] */
private $instances = [];
/** @var Bindings */
private $bindings;
/**
* @param Bindings $bindings
*/
public function __construct(Bindings $bindings)
{
$this->bindings = $bindings;
}
/**
* @param InstanceFactory $factory
* @param Binder $binder
* @return object
* @throws BadMethodCallException
* @throws InjectorException
*/
public function getInstance(InstanceFactory $factory, Binder $binder)
{
$instance = $binder->getInstance();
if ($instance) {
return $instance;
}
$factoryClassName = $binder->getFactoryClassName();
if ($factoryClassName) {
return $this->createInstanceThroughFactory($factory, $factoryClassName);
}
$className = $binder->getBoundClassName() ?: $binder->getClassName();
$scope = $binder->getScope();
if ($scope == Scope::SINGLETON) {
@@ -49,4 +68,20 @@ public function singletonInstance(InstanceFactory $factory, $className)
$this->instances[$className] = $instance;
return $instance;
}
/**
* @param InstanceFactory $factory
* @param $factoryClassName
* @return mixed
* @throws InjectorException
*/
private function createInstanceThroughFactory(InstanceFactory $factory, $factoryClassName)
{
if (!in_array(Factory::class, class_implements($factoryClassName))) {
throw new InjectorException("Factory class $factoryClassName does not implemented \Ouzo\Injection\Factory interface.");
}
$factoryBinder = $this->bindings->getBinder($factoryClassName);
$factoryObject = $this->getInstance($factory, $factoryBinder);
return $factoryObject->create();
}
}
@@ -36,6 +36,13 @@ $config = new InjectorConfig();
$config->bind('\MyClass')->toInstance(new MyClass());
```
Binding through factory class (`Factory` interface has to be implemented):
```php
$config = new InjectorConfig();
$config->bind('\MyClass')->throughFactory('\MyClassFactory');
```
Auto-wiring dependencies (with @Inject):
```php
@@ -0,0 +1,10 @@
<?php
/*
* Copyright (c) Ouzo contributors, http://ouzoframework.org
* This file is made available under the MIT License (view the LICENSE file for more information).
*/
class ClassCreatedByFactory
{
}
@@ -0,0 +1,16 @@
<?php
/*
* Copyright (c) Ouzo contributors, http://ouzoframework.org
* This file is made available under the MIT License (view the LICENSE file for more information).
*/
use Ouzo\Injection\Factory;
class ClassFactory implements Factory
{
public function create()
{
return new ClassCreatedByFactory();
}
}
@@ -0,0 +1,14 @@
<?php
/*
* Copyright (c) Ouzo contributors, http://ouzoframework.org
* This file is made available under the MIT License (view the LICENSE file for more information).
*/
class ClassWithNamedThroughDep
{
/**
* @Inject @Named("through_dep")
* @var \ClassWithNoDep
*/
public $myClass;
}
@@ -0,0 +1,14 @@
<?php
/*
* Copyright (c) Ouzo contributors, http://ouzoframework.org
* This file is made available under the MIT License (view the LICENSE file for more information).
*/
class ClassWithThroughDep
{
/**
* @Inject
* @var \ClassWithNoDep
*/
public $myClass;
}
@@ -357,6 +357,59 @@ public function shouldInjectParentPrivateField()
$this->assertDependencyInjected(ClassWithNoDep::class, $instance->getMyClass());
}
/**
* @test
*/
public function shouldInjectThroughFactoryDependency()
{
//given
$config = new InjectorConfig();
$injector = new Injector($config);
$config->bind(ClassWithNoDep::class)->throughFactory(ClassFactory::class);
//when
$instance = $injector->getInstance(ClassWithThroughDep::class);
//then
$this->assertInstanceOf(ClassWithThroughDep::class, $instance);
$this->assertDependencyInjected(ClassCreatedByFactory::class, $instance->myClass);
}
/**
* @test
*/
public function shouldInjectThroughFactoryDependencyWhenInterfaceIsNotImplemented()
{
//given
$config = new InjectorConfig();
$injector = new Injector($config);
$config->bind(ClassWithNoDep::class)->throughFactory(ClassWithNoDep::class);
//when
CatchException::when($injector)->getInstance(ClassWithThroughDep::class);
//then
CatchException::assertThat()->isInstanceOf(InjectorException::class);
}
/**
* @test
*/
public function shouldInjectNamedThroughFactoryDependency()
{
//given
$config = new InjectorConfig();
$injector = new Injector($config);
$config->bind(ClassWithNoDep::class, 'through_dep')->throughFactory(ClassFactory::class);
//when
$instance = $injector->getInstance(ClassWithNamedThroughDep::class);
//then
$this->assertInstanceOf(ClassWithNamedThroughDep::class, $instance);
$this->assertDependencyInjected(ClassCreatedByFactory::class, $instance->myClass);
}
private function assertDependencyInjected($className, $instance)
{
$this->assertNotNull($instance, 'Dependency was not injected.');

0 comments on commit dfa6c01

Please sign in to comment.