Skip to content

Commit

Permalink
Merge pull request #54 from lucatume/defunctl/master
Browse files Browse the repository at this point in the history
Wrap #53
  • Loading branch information
lucatume authored Mar 17, 2023
2 parents b51304f + c7ed358 commit a813edc
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 7 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ to [Semantic Versioning](http://semver.org/).

## [unreleased] Unreleased

### Fixed

- Allow bound interfaces to properly resolve their concrete class when the concrete class has contextual bindings (thanks @defunctl). e.g. the following will now work properly:

```php
$container = new \lucatume\DI52\Container();
$container->bind(SomeInterface::class, SomeClass::class);
$container->when(Someclass:class)
->needs('$num')
->give(20);

// This now works, properly resolving SomeClass::class.
$instance = $container->get(SomeInterface::class);
```

## [3.3.0] 2023-02-28;

### Breaking change
Expand Down
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ $container->bind(DbCache::class, function($container){
});

/*
* but when an implementation of the `CacheInterface` is requested by
* But when an implementation of the `CacheInterface` is requested by
* `TransactionManager`, then it should be given an instance of `Array Cache`.
*/
$container->when(TransactionManager::class)
Expand All @@ -562,9 +562,18 @@ $container->when(TransactionManager::class)
* We can also bind primitives where the container doesn't know how to auto-wire
* them.
*/
$container->when(PaginationManager::class)
->needs('$per_page')
->give(25);
$container->when(MysqlOrm:class)
->needs('$dbUrl')
->give('mysql://user:password@127.0.0.1:3306/app');

/*
* When primitives are bound to a class the container will correctly resolve them when building the class
* bound to an interface.
*/
$container->bind(ORMInterface::class, MysqlOrm::class);

// The `ORMInterface` will be resolved an instance of the `MysqlOrm` class, with the `$dbUrl` argument set correctly.
$orm = $container->get(ORMInterface::class);
```

## Binding decorator chains
Expand Down
20 changes: 20 additions & 0 deletions src/Builders/ClassBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ class ClassBuilder implements BuilderInterface, ReinitializableBuilderInterface
*/
protected $resolver;

/**
* Whether the $className is an implementation of $id
* and $id is an interface.
*
* @var bool
*/
protected $isInterface = false;

/**
* ClassBuilder constructor.
*
Expand All @@ -73,6 +81,13 @@ public function __construct($id, Resolver $resolver, $className, array $afterBui
"nothing is bound to the '{$className}' id and it's not an existing or instantiable class."
);
}

$interfaces = class_implements($className);

if ($interfaces && isset($interfaces[$id])) {
$this->isInterface = true;
}

$this->id = $id;
$this->className = $className;
$this->afterBuildMethods = $afterBuildMethods;
Expand Down Expand Up @@ -194,10 +209,15 @@ protected function resolveParameter(Parameter $parameter)

if ($paramClass) {
$parameterImplementation = $this->resolver->whenNeedsGive($this->id, $paramClass);
} elseif ($this->isInterface) {
$name = $parameter->getName();
// If an interface was requested, resolve the underlying concrete class instead.
$parameterImplementation = $this->resolver->whenNeedsGive($this->className, "\$$name");
} else {
$name = $parameter->getName();
$parameterImplementation = $this->resolver->whenNeedsGive($this->id, "\$$name");
}

try {
return $parameterImplementation instanceof BuilderInterface ?
$parameterImplementation->build()
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/ContextualBindingContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,41 @@ public function it_should_resolve_primitive_contextual_bindings_in_a_PHP53_class
$this->assertNull($instance->optional());
}

/**
* @test
*/
public function it_should_resolve_primitive_contextual_bindings_in_a_php7_class_when_its_bound_interface_is_resolved(
)
{
$container = new Container();

$container->bind(Test5Interface::class, Primitive53ConstructorClass::class);
$container->when(Primitive53ConstructorClass::class)
->needs('$num')
->give(15);

$container->when(Primitive53ConstructorClass::class)
->needs('$hello')
->give(function () {
return 'World';
});

$container->when(Primitive53ConstructorClass::class)
->needs('$list')
->give([
'one',
'two',
]);

$instance = $container->get(Test5Interface::class);

$this->assertSame(15, $instance->num());
$this->assertInstanceOf(Concrete53Dependency::class, $instance->dependency());
$this->assertSame('World', $instance->hello());
$this->assertSame(['one', 'two'], $instance->getList());
$this->assertNull($instance->optional());
}

/**
* @test
*/
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/PHP7ContextualBindingContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,41 @@ public function it_should_resolve_primitive_contextual_bindings_in_a_php7_class(
$this->assertNull($instance->optional());
}

/**
* @test
*/
public function it_should_resolve_primitive_contextual_bindings_in_a_php7_class_when_its_bound_interface_is_resolved()
{
$container = new Container();

$container->bind( Test7Interface::class, Primitive7ConstructorClass::class );

$container->when(Primitive7ConstructorClass::class)
->needs('$num')
->give(15);

$container->when(Primitive7ConstructorClass::class)
->needs('$hello')
->give(function () {
return 'World';
});

$container->when(Primitive7ConstructorClass::class)
->needs('$list')
->give([
'one',
'two',
]);

$instance = $container->get(Test7Interface::class);

$this->assertSame(15, $instance->num());
$this->assertInstanceOf(Concrete7Dependency::class, $instance->dependency());
$this->assertSame('World', $instance->hello());
$this->assertSame(['one', 'two'], $instance->list());
$this->assertNull($instance->optional());
}

/**
* @test
*/
Expand Down
34 changes: 34 additions & 0 deletions tests/unit/PHP8ContextualBindingContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,40 @@ public function it_should_resolve_primitive_contextual_bindings_in_a_php80_class
$this->assertNull($instance->optional());
}

/**
* @test
*/
public function it_should_resolve_primitive_contextual_bindings_in_a_php80_class_when_its_bound_interface_is_resolved()
{
$container = new Container();

$container->bind(Test8Interface::class, Primitive8ConstructorClass::class);
$container->when(Primitive8ConstructorClass::class)
->needs('$num')
->give(20);

$container->when(Primitive8ConstructorClass::class)
->needs('$hello')
->give(function () {
return 'World';
});

$container->when(Primitive8ConstructorClass::class)
->needs('$list')
->give([
'one',
'two',
]);

$instance = $container->get(Test8Interface::class);

$this->assertSame(20, $instance->num());
$this->assertInstanceOf(Concrete8Dependency::class, $instance->dependency());
$this->assertSame('World', $instance->hello());
$this->assertSame(['one', 'two'], $instance->list());
$this->assertNull($instance->optional());
}

/**
* @test
*/
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/data/test-contextual-classes-php.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
* PHP5.3+ contextual binding test classes.
*/

interface Test5Interface
{

}

class Concrete53Dependency
{

}

class Primitive53ConstructorClass
class Primitive53ConstructorClass implements Test5Interface
{
/**
* @var int
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/data/test-contextual-classes-php7.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
* PHP7+ contextual binding test classes.
*/

interface Test7Interface
{

}

class Concrete7Dependency
{

}

class Primitive7ConstructorClass
class Primitive7ConstructorClass implements Test7Interface
{

/**
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/data/test-contextual-classes-php8.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
* PHP8+ contextual binding test classes.
*/

interface Test8Interface
{

}

final class Concrete8Dependency
{

}

final class Primitive8ConstructorClass
final class Primitive8ConstructorClass implements Test8Interface
{

public function __construct(
Expand Down

0 comments on commit a813edc

Please sign in to comment.