Skip to content
Permalink
Browse files

[Inject] Add named parameters to constructor.

  • Loading branch information...
piotrooo committed Jul 23, 2019
1 parent f1a9247 commit 5fb4f45745ebe35335f8659c7af282b02d968697
@@ -7,6 +7,7 @@
namespace Ouzo\Injection\Annotation;
use Ouzo\Injection\InjectorException;
use Ouzo\Utilities\Arrays;
use Ouzo\Utilities\Strings;
use ReflectionClass;
use ReflectionProperty;
@@ -27,7 +28,7 @@ public function getMetadata(ReflectionClass $class, $privatePropertiesOnly = fal
if (Strings::contains($doc, '@Inject')) {
if (preg_match("#@var ([\\\\A-Za-z0-9]*)#s", $doc, $matched)) {
$className = Strings::removePrefix($matched[1], "\\");
$name = $this->extractName($doc);
$name = $this->extractNamed($doc);
$annotations[$property->getName()] = ['name' => $name, 'className' => $className];
} else {
throw new InjectorException('Cannot @Inject dependency. @var is not defined for property $' . $property->getName() . ' in class ' . $class->getName() . '.');
@@ -49,12 +50,15 @@ public function getConstructorMetadata($className)
$doc = $this->getDocCommentFrom($constructor);
if (Strings::contains($doc, '@Inject')) {
$parameters = $constructor->getParameters();
$namedMap = $this->extractNamedMap($parameters, $doc);
foreach ($parameters as $parameter) {
if (!$parameter->getClass()) {
throw new InjectorException("Cannot @Inject by constructor for class $className. All arguments should have types defined.");
}
$name = $this->extractName($doc);
$annotations[$parameter->getName()] = ['name' => $name, 'className' => $parameter->getClass()->getName()];
$parameterName = $parameter->getName();
$name = Arrays::getValue($namedMap, $parameterName, '');
$annotations[$parameterName] = ['name' => $name, 'className' => $parameter->getClass()->getName()];
}
}
}
@@ -75,11 +79,40 @@ protected function getDocCommentFrom($object)
* @param string $doc
* @return string
*/
private function extractName($doc)
private function extractNamed($doc)
{
if (preg_match("#@Named\\(\"([A-Za-z0-9_]*)\"\\)#s", $doc, $matched)) {
return $matched[1];
}
return '';
}
/**
* @param array $parameters
* @param string $doc
* @return array
*/
private function extractNamedMap($parameters, $doc)
{
if (preg_match('/@Named\("([A-Za-z0-9_,=]*)"\)/s', $doc, $matched)) {
$paramsWithNamed = explode(',', $matched[1]);
$paramsWithNamedAsArray = Arrays::map($paramsWithNamed, function ($paramWithName) {
return explode('=', $paramWithName);
});
// Handle case: @Named("some_name")
if (count($paramsWithNamed) == 1 && count($paramsWithNamedAsArray[0]) == 1) {
return [$parameters[0]->getName() => $paramsWithNamed[0]];
}
return Arrays::toMap($paramsWithNamedAsArray, function ($paramToNamedMap) {
return $paramToNamedMap[0];
}, function ($paramToNamedMap) {
return $paramToNamedMap[1];
});
}
return [];
}
}
@@ -0,0 +1,17 @@
<?php
class ClassWithNamedConstructorMultipleDep
{
public $myClass;
public $secondClass;
/**
* @Inject
* @Named("myClass=my_dep,secondClass=my_second_dep")
*/
public function __construct(ClassWithNoDep $myClass, ClassWithPrivateDep $secondClass)
{
$this->myClass = $myClass;
$this->secondClass = $secondClass;
}
}
@@ -0,0 +1,17 @@
<?php
class ClassWithNamedConstructorSingleNamedDep
{
public $myClass;
public $secondClass;
/**
* @Inject
* @Named("secondClass=my_second_dep")
*/
public function __construct(ClassWithPrivateDep $myClass, ClassWithPrivateDep $secondClass)
{
$this->myClass = $myClass;
$this->secondClass = $secondClass;
}
}
@@ -451,6 +451,43 @@ public function shouldInjectThroughFactoryInSingletonScopeAndStillBeAbleToCreate
$this->assertDependencyInjected(ClassFactory::class, $instance2);
}
/**
* @test
*/
public function shouldInjectMultipleNamedDependenciesIntoConstructor()
{
//given
$config = new InjectorConfig();
$injector = new Injector($config);
$config->bind(ClassWithNoDep::class, 'my_dep')->to(SubClassWithNoDep::class);
$config->bind(ClassWithPrivateDep::class, 'my_second_dep')->to(SubClassOfClassWithPrivateDep::class);
//when
$instance = $injector->getInstance(ClassWithNamedConstructorMultipleDep::class);
//then
$this->assertDependencyInjected(SubClassWithNoDep::class, $instance->myClass);
$this->assertDependencyInjected(SubClassOfClassWithPrivateDep::class, $instance->secondClass);
}
/**
* @test
*/
public function shouldInjectOneNamedDependencyIntoDefinedConstructorParameter()
{
//given
$config = new InjectorConfig();
$injector = new Injector($config);
$config->bind(ClassWithPrivateDep::class, 'my_second_dep')->to(SubClassOfClassWithPrivateDep::class);
//when
$instance = $injector->getInstance(ClassWithNamedConstructorSingleNamedDep::class);
//then
$this->assertDependencyInjected(ClassWithPrivateDep::class, $instance->myClass);
$this->assertDependencyInjected(SubClassOfClassWithPrivateDep::class, $instance->secondClass);
}
private function assertDependencyInjected($className, $instance)
{
$this->assertNotNull($instance, 'Dependency was not injected.');

0 comments on commit 5fb4f45

Please sign in to comment.
You can’t perform that action at this time.