Skip to content

Commit

Permalink
Merge pull request #3 from intraworlds/add_method_resolve
Browse files Browse the repository at this point in the history
Add method resolve
  • Loading branch information
esler authored Jan 29, 2019
2 parents 845b08d + bd013e5 commit 791839b
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 3 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,23 @@ Our main code will stay the same.
```php
$client = $container->get(Cache\Client::class);
```
Method `resolve` is useful for resolving any dependencies of a _callable_. Especially it's useful for `init` template method. See following example.
```php
abstract class Parent {
function __construct(Dependency $dependency, ServiceContainer $container) {
$this->dependency = $dependency;
$container->resolve([$this, 'init']);
}
}

**TODO** keep going with examples
class Child extends Parent {
function init(AhotherDependency $another) {
// ...
}
}
```

**TODO** keep going with examples

## License
All contents of this package are licensed under the [MIT license].
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
},
"require-dev": {
"phpspec/phpspec": "^5.1",
"phpbench/phpbench": "@dev"
"phpbench/phpbench": "@dev",
"phpunit/phpunit": "^7.5"
},
"autoload": {
"psr-4": {
Expand Down
8 changes: 8 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true">
<testsuites>
<testsuite name="Unit tests">
<directory>tests/</directory>
</testsuite>
</testsuites>
</phpunit>
45 changes: 44 additions & 1 deletion src/ServiceContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class ServiceContainer implements ContainerInterface
/** @var bool */
protected $defaultSingletons = true;

/** @var bool */
protected $eagerwireEnabled = false;

/** @var callable[] */
private $factories = [];

Expand All @@ -32,12 +35,15 @@ class ServiceContainer implements ContainerInterface
* container will resolve any subsequent dependencies with the
* same instance (singleton), FALSE will not save instances
* therefore container returns always fresh instance
* - eagerwire TRUE will always try to resolve optional dependencies, FALSE
* will omit resolution of optional parameters
*
* @param array $options options [autowire => bool, singletons => bool]
*/
public function __construct(array $options=[]) {
$this->autowireEnabled = (bool) ($options['autowire'] ?? true); // autowire by default
$this->defaultSingletons = (bool) ($options['singletons'] ?? false); // don't create singletons by default
$this->eagerwireEnabled = (bool) ($options['eagerwire'] ?? false); // don't resolve optional args
}

/**
Expand Down Expand Up @@ -143,7 +149,7 @@ public function make(string $id)
$instance = ($this->factories[$id] = static::buildSimpleFactory($id))();
} catch (\Throwable $t) {
unset($this->factories[$id]);

// cannot create instance naively for some reason, keep going and try create factory then
if (!$this->autowireEnabled) {
throw new ServiceNotFoundException($id, $t);
Expand All @@ -163,6 +169,43 @@ public function make(string $id)
return $instance;
}

/**
* Resolve dependencies by container, or with given arguments and call given
* callable with them
*
* @param callable $callable a callable to resolve
* @param array $args associative array of optional arguments
*
* @return mixed a result of given callable
*/
public function resolve(callable $callable, array $args=[])
{
if ($callable instanceof \Closure || (is_string($callable) && function_exists($callable))) {
$reflection = new \ReflectionFunction($callable);
} elseif (is_string($callable)) {
$reflection = new \ReflectionMethod($callable);
} elseif (is_object($callable) && ($reflection = new \ReflectionObject($callable))->hasMethod('__invoke')) {
$reflection = $reflection->getMethod('__invoke');
} else {
$reflection = new \ReflectionMethod(...$callable);
}

$params = [];
foreach ($reflection->getParameters() as $param) {
if ($param->isOptional() && !$this->eagerwireEnabled) {
break;
} elseif (array_key_exists($name = $param->getName(), $args)) {
$params[] = $args[$name];
} elseif ($type = $param->getType()) {
$params[] = $this->get((string) $type);
} else {
throw new UnsupportedAutowireParamException($param);
}
}

return $callable(...$params);
}

/**
* Sets given entry as a singleton
*
Expand Down
69 changes: 69 additions & 0 deletions tests/ServiceContainerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

use IW\ServiceContainer;
use PHPUnit\Framework\TestCase;

class ServiceContainerTest extends TestCase
{

public function testResolveFunction() {
$container = new ServiceContainer;

$this->assertInstanceOf('Foo', $container->resolve('foo'));
}

public function testResolveMethod() {
$container = new ServiceContainer;

$foo = $container->get('Foo');

$this->assertInstanceOf('Bar', $container->resolve([$foo, 'bar']));
}

public function testResolveStaticMethod() {
$container = new ServiceContainer;

$this->assertSame('Hello World', $container->resolve('Bar::hello'));
$this->assertSame('Hello World', $container->resolve(['Bar', 'hello']));
}

public function testResolveClosure() {
$container = new ServiceContainer;

$hello = function (string $who) {
return Bar::hello($who);
};

$this->assertSame('Hello Alice', $container->resolve($hello, ['who' => 'Alice']));
}

public function testResolveInvokable() {
$container = new ServiceContainer;

$this->assertInstanceOf('Bar', $container->resolve($container->resolve('foo')));
}
}

function foo(Foo $foo) {
return $foo;
}

class Foo {
public function __construct(Bar $bar) {
$this->_bar = $bar;
}

public function bar(): Bar {
return $this->_bar;
}

public function __invoke() {
return $this->bar();
}
}

class Bar {
public static function hello(string $who='World'): string {
return 'Hello ' . $who;
}
}

0 comments on commit 791839b

Please sign in to comment.