Skip to content

Commit

Permalink
Merge 71e368d into 26adbf3
Browse files Browse the repository at this point in the history
  • Loading branch information
jdreesen committed Sep 22, 2015
2 parents 26adbf3 + 71e368d commit f21f62c
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 7 deletions.
1 change: 1 addition & 0 deletions change-log.md
Expand Up @@ -5,6 +5,7 @@
Improvements:

- [#272](https://github.com/PHP-DI/PHP-DI/issues/272): Support 'Class::method' syntax for callables
- [#333](https://github.com/PHP-DI/PHP-DI/pull/333): Allow injection of any container object as factory parameter via type hinting

Bugfixes:

Expand Down
14 changes: 8 additions & 6 deletions doc/php-definitions.md
Expand Up @@ -109,17 +109,18 @@ return [
];
```

The only parameter of a factory is the container (which can be used to retrieve other entries). You are encouraged to type-hint against the interface `Interop\Container\ContainerInterface` instead of the implementation `DI\Container`: that can be necessary in scenarios where you are using multiple containers (for example if using the PHP-DI + Symfony integration).
Instances of other classes can be injected through parameters via type-hinting (as long as they are registered within the container or autowiring is enabled).

The container, as seen above, can be injected and then used to retrieve other entries, like values, that can't be automatically injected via type -hinting. You are encouraged to type-hint against the interface `Interop\Container\ContainerInterface` instead of the implementation `DI\Container` in this case: that can be necessary in scenarios where you are using multiple containers (for example if using the PHP-DI + Symfony integration).

You can also use a factory class - as an example, let's assume you have a simple factory class like this:

```php
class FooFactory
{
// note: $container can be omitted if not needed
public function create($container)
public function create(Bar $bar)
{
return new Foo();
return new Foo($bar);
}
}
```
Expand All @@ -135,7 +136,7 @@ return [

But the factory will be created on every request (`new FooFactory`) even if not used. Additionally with this method it's harder to pass dependencies in the factory.

The recommended solution is let the container create the factory:
The recommended solution is to let the container create the factory:

```php
return [
Expand All @@ -155,7 +156,8 @@ This configuration is equivalent to the following code:

```php
$factory = $container->get(FooFactory::class);
return $factory->create();
$bar = $container->get(Bar::class);
return $factory->create($bar);
```

Please note:
Expand Down
1 change: 1 addition & 0 deletions src/DI/Container.php
Expand Up @@ -88,6 +88,7 @@ public function __construct(
$this->singletonEntries['DI\Container'] = $this;
$this->singletonEntries['DI\FactoryInterface'] = $this;
$this->singletonEntries['DI\InvokerInterface'] = $this;
$this->singletonEntries['Interop\Container\ContainerInterface'] = $this->wrapperContainer;
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/DI/Definition/Resolver/FactoryResolver.php
Expand Up @@ -7,8 +7,11 @@
use DI\Definition\FactoryDefinition;
use Interop\Container\ContainerInterface;
use Invoker\Exception\NotCallableException;
use Invoker\Exception\NotEnoughParametersException;
use Invoker\Invoker;
use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
use Invoker\ParameterResolver\NumericArrayResolver;
use Invoker\ParameterResolver\ResolverChain;

/**
* Resolves a factory definition to a value.
Expand Down Expand Up @@ -51,7 +54,12 @@ public function __construct(ContainerInterface $container)
public function resolve(Definition $definition, array $parameters = [])
{
if (! $this->invoker) {
$this->invoker = new Invoker(new NumericArrayResolver, $this->container);
$parameterResolver = new ResolverChain([
new TypeHintContainerResolver($this->container),
new NumericArrayResolver,
]);

$this->invoker = new Invoker($parameterResolver, $this->container);
}

try {
Expand All @@ -62,6 +70,12 @@ public function resolve(Definition $definition, array $parameters = [])
$definition->getName(),
$e->getMessage()
));
} catch (NotEnoughParametersException $e) {
throw new DefinitionException(sprintf(
'Entry "%s" cannot be resolved: %s',
$definition->getName(),
$e->getMessage()
));
}
}

Expand Down
41 changes: 41 additions & 0 deletions tests/IntegrationTest/Definitions/FactoryDefinitionTest.php
Expand Up @@ -3,6 +3,7 @@
namespace DI\Test\IntegrationTest\Definitions;

use DI\ContainerBuilder;
use Interop\Container\ContainerInterface;

/**
* Test factory definitions.
Expand Down Expand Up @@ -88,6 +89,46 @@ public function test_arbitrary_named_invokable_container_object_as_factory()
$this->assertSame('bar', $factory);
}

public function test_container_gets_injected_as_first_argument_without_typehint()
{
$container = $this->createContainer([
'factory' => function ($c) {
return $c;
},
]);

$factory = $container->get('factory');

$this->assertInstanceOf('Interop\Container\ContainerInterface', $factory);
}

public function test_arbitrary_object_gets_injected_via_typehint()
{
$container = $this->createContainer([
'factory' => function (\stdClass $stdClass) {
return $stdClass;
},
]);

$factory = $container->get('factory');

$this->assertInstanceOf('stdClass', $factory);
}

public function test_container_gets_injected_in_arbitrary_position_via_typehint()
{
$container = $this->createContainer([
'factory' => function (\stdClass $stdClass, ContainerInterface $c) {
return [$stdClass, $c];
},
]);

$factory = $container->get('factory');

$this->assertInstanceOf('stdClass', $factory[0]);
$this->assertInstanceOf('Interop\Container\ContainerInterface', $factory[1]);
}

/**
* @expectedException \DI\Definition\Exception\DefinitionException
* @expectedExceptionMessage Entry "foo" cannot be resolved: factory "Hello World" is neither a callable nor a valid container entry
Expand Down

0 comments on commit f21f62c

Please sign in to comment.