From 309100b57a4abd533eca7cbc4c5613a5bf8a3940 Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Tue, 13 Feb 2024 23:01:25 +0700 Subject: [PATCH 1/4] Support parameters name binding --- tests/Support/ArgumentNameBinding.php | 14 ++++++++++++++ tests/Unit/ContainerTest.php | 17 ++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/Support/ArgumentNameBinding.php 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 @@ +withDefinitions([ @@ -1972,4 +1973,18 @@ public function testInvalidTags(string $message, array $tags): void $this->expectExceptionMessageMatches($message); new Container($config); } + + public function testArgumentNameBinding() + { + $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); + } } From 2729552e6b3045d38ab2eae9b51212c10bcb4c3f Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Tue, 13 Feb 2024 23:02:09 +0700 Subject: [PATCH 2/4] Use temp branch --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e9055b5c..b86e8abc 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", From c0b1c00f27a31146cde3a58a72c2117f548ca1eb Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Thu, 15 Feb 2024 23:27:13 +0700 Subject: [PATCH 3/4] Add tests --- ...nConstructorFirstTypeInParamResolvable.php | 5 +++ tests/Unit/ContainerTest.php | 32 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/Support/UnionTypeInConstructorFirstTypeInParamResolvable.php b/tests/Support/UnionTypeInConstructorFirstTypeInParamResolvable.php index 73db39e1..9406b7a0 100644 --- a/tests/Support/UnionTypeInConstructorFirstTypeInParamResolvable.php +++ b/tests/Support/UnionTypeInConstructorFirstTypeInParamResolvable.php @@ -9,4 +9,9 @@ final class UnionTypeInConstructorFirstTypeInParamResolvable public function __construct(private EngineMarkOne|EngineInterface $engine) { } + + public function getEngine(): EngineMarkOne|EngineInterface + { + return $this->engine; + } } diff --git a/tests/Unit/ContainerTest.php b/tests/Unit/ContainerTest.php index e60a5663..682c4e54 100644 --- a/tests/Unit/ContainerTest.php +++ b/tests/Unit/ContainerTest.php @@ -1974,12 +1974,12 @@ public function testInvalidTags(string $message, array $tags): void new Container($config); } - public function testArgumentNameBinding() + public function testArgumentNameBindingTyped(): void { $config = ContainerConfig::create() ->withDefinitions([ - EngineInterface::class . '$markOne' => EngineMarkOne::class, - EngineInterface::class . '$markTwo' => EngineMarkTwo::class, + EngineInterface::class . ' $markOne' => EngineMarkOne::class, + EngineInterface::class . ' $markTwo' => EngineMarkTwo::class, ]); $container = new Container($config); @@ -1987,4 +1987,30 @@ public function testArgumentNameBinding() $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()); + } } From 30f25218a32014c0b2db34a40fd892b9cfca0b5d Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Fri, 23 Feb 2024 14:41:06 +0700 Subject: [PATCH 4/4] Add docs --- CHANGELOG.md | 2 +- README.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11cac6d..fb57b39c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 1.2.2 under development -- no changes in this release. +- Enh #352: Support parameters name binding (@xepozz) ## 1.2.1 December 23, 2022 diff --git a/README.md b/README.md index 0e8a3665..c1deb36b 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