diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e461ee4..81ab19e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ ## 1.3.0 October 14, 2024 +- Enh #352: Support parameters name binding (@xepozz) - Enh #353: Add shortcut for tag reference #333 (@xepozz) - Enh #356: Improve usage `NotFoundException` for cases with definitions (@vjik) - Enh #364: Minor refactoring to improve performance of container (@samdark) diff --git a/README.md b/README.md index 4cfdc10e..c508a9eb 100644 --- a/README.md +++ b/README.md @@ -385,6 +385,39 @@ $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 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 parameter 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 ### Getting tagged services You can get tagged services from the container in the following way: diff --git a/composer.json b/composer.json index 7026d3bc..b51dc5c7 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "php": "8.1 - 8.5", "ext-mbstring": "*", "psr/container": "^1.1 || ^2.0", - "yiisoft/definitions": "^3.0", + "yiisoft/definitions": "dev-parameter-name-binding", "yiisoft/friendly-exception": "^1.1.0" }, "require-dev": { 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 d11b7684..4f6b86cf 100644 --- a/tests/Unit/ContainerTest.php +++ b/tests/Unit/ContainerTest.php @@ -21,6 +21,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; @@ -1966,6 +1967,44 @@ public function testInvalidTags(string $message, array $tags): void 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()); public static function dataNotFoundExceptionMessageWithDefinitions(): array { return [