diff --git a/CHANGELOG.md b/CHANGELOG.md index 76121f14..6437d2ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 1.2.2 under development +- Enh #352: Support parameters name binding (@xepozz) - Enh #353: Add shortcut for tag reference #333 (@xepozz) ## 1.2.1 December 23, 2022 diff --git a/README.md b/README.md index e2a2f0fc..7811d7d4 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,41 @@ $config = ContainerConfig::create() $container = new Container($config); ``` +## Name binding + +Name binding is a way to bind a name to a definition. It is used to resolve a definition not by its class name but by a name. + +Set a definitions with a specific name. It may be typed or untyped reference like: +1. `'$serviceName' => $definition` +2. `Service::class . ' $serviceName' => $definition` + +```php +return [ + '$fileCache' => FileCache::class, // implements CacheInterface + '$redisCache' => RedisCache::class, // implements CacheInterface + CacheInterface::class . ' $memCache' => MemCache::class, // also implements CacheInterface +] +``` + +So now you can resolve a definition by its name: + +```php +class MyService +{ + public function __construct( + CacheInterface $memCache, // typed reference + $fileCache, // untyped reference + CacheInterface $redisCache, // typed reference to untyped definition + ) { + // ... + } +} +``` + +```php +$container->get(MyService::class); // returns an instance of MyService +``` + ## Resetting services state Despite stateful services isn't a great practice, these are often inevitable. When you build long-running diff --git a/composer.json b/composer.json index 34a41b87..5d230319 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "php": "^8.0", "ext-mbstring": "*", "psr/container": "^1.1|^2.0", - "yiisoft/definitions": "^3.0" + "yiisoft/definitions": "dev-parameter-name-binding" }, "require-dev": { "league/container": "^4.2", diff --git a/tests/Support/ArgumentNameBinding.php b/tests/Support/ArgumentNameBinding.php new file mode 100644 index 00000000..f46b1641 --- /dev/null +++ b/tests/Support/ArgumentNameBinding.php @@ -0,0 +1,14 @@ +engine; + } } diff --git a/tests/Unit/ContainerTest.php b/tests/Unit/ContainerTest.php index 7637fbfb..682c4e54 100644 --- a/tests/Unit/ContainerTest.php +++ b/tests/Unit/ContainerTest.php @@ -19,6 +19,7 @@ use Yiisoft\Di\StateResetter; use Yiisoft\Di\ServiceProviderInterface; use Yiisoft\Di\Tests\Support\A; +use Yiisoft\Di\Tests\Support\ArgumentNameBinding; use Yiisoft\Di\Tests\Support\B; use Yiisoft\Di\Tests\Support\Car; use Yiisoft\Di\Tests\Support\CarFactory; @@ -145,7 +146,7 @@ public function dataHas(): array /** * @dataProvider dataHas */ - public function testHas(bool $expected, $id): void + public function testHas(bool $expected, string $id): void { $config = ContainerConfig::create() ->withDefinitions([ @@ -1972,4 +1973,44 @@ public function testInvalidTags(string $message, array $tags): void $this->expectExceptionMessageMatches($message); new Container($config); } + + public function testArgumentNameBindingTyped(): void + { + $config = ContainerConfig::create() + ->withDefinitions([ + EngineInterface::class . ' $markOne' => EngineMarkOne::class, + EngineInterface::class . ' $markTwo' => EngineMarkTwo::class, + ]); + $container = new Container($config); + + $class = $container->get(ArgumentNameBinding::class); + $this->assertInstanceOf(EngineMarkOne::class, $class->markOne); + $this->assertInstanceOf(EngineMarkTwo::class, $class->markTwo); + } + + public function testArgumentNameBindingUntyped(): void + { + $config = ContainerConfig::create() + ->withDefinitions([ + '$markOne' => EngineMarkOne::class, + '$markTwo' => EngineMarkTwo::class, + ]); + $container = new Container($config); + + $class = $container->get(ArgumentNameBinding::class); + $this->assertInstanceOf(EngineMarkOne::class, $class->markOne); + $this->assertInstanceOf(EngineMarkTwo::class, $class->markTwo); + } + + public function testArgumentNameBindingUnionTypes(): void + { + $config = ContainerConfig::create() + ->withDefinitions([ + '$engine' => EngineMarkOne::class, + ]); + $container = new Container($config); + + $class = $container->get(UnionTypeInConstructorFirstTypeInParamResolvable::class); + $this->assertInstanceOf(EngineMarkOne::class, $class->getEngine()); + } }