diff --git a/.travis.yml b/.travis.yml index 9c343aa5..20f0c339 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: php -sudo: false +sudo: required +dist: trusty php: - 5.6 - 7 diff --git a/README.ja.md b/README.ja.md index 5e499197..ca3d89bb 100644 --- a/README.ja.md +++ b/README.ja.md @@ -372,33 +372,78 @@ protected function configure() ## コンストラクタ束縛 -`@Inject`アノテーションのないサードパーティーのクラスに特定の束縛を指定するのに`toConstructor`を使うことができます。クラス名と`Named Binding`を指定して束縛します。 +`@Inject`アノテーションのないサードパーティーのクラスやアノテーションを使いたくない時には`Provider`束縛を使うこともできますが、その場合インスタンスをユーザーコードが作成する事になりAOPが利用できません。 + +この問題は`toConstructor()`束縛で解決できます。インターフェイスにクラスを束縛するのは`to()`と同じですが、`@Named`やセッターメソッドの`@Inject`の指定をアノテートする事なしに指定できます。 -```php -bind(EngineInterface::class)->annotatedWith('na')->to(NaturalAspirationEngine::class); - $this->bind()->annotatedWith('car_name')->toInstance('Eunos Roadster'); $this ->bind(CarInterface::class) ->toConstructor( - Car::class, - 'engine=na,carName=car_name' // varName=BindName,... + Car::class, // $class_name + [ + ['enginne' => 'na'], // $name + ['number' => 'registrtion_number'] + ], + (new InjectionPoints) // $setter_injection + ->addMethod('setWheel', "right") + ->addOptionalMethod('setTurboCharger'), + 'initialize' // $postCosntruct ); } ``` - この例では`Car`クラスでは`EngineInterface $engine, $carName`と二つの引数が必要ですが、それぞれの変数名に`Named binding`束縛を行い依存解決をしています。 +### Parameter + +**class_name** + +クラス名 + +**name** + +名前束縛。配列か文字列で`引数名`と`束縛名の名前`をペアにして指定します。 + +array `[[$parame_name => $binding_name],...]` or string `"param_name=binding_name&..."` + +**setter_injection** + +セッターインジェクションのメソッド名と`束縛名の名前`を指定したインジェクとポイントオブジェクト + +**postCosntruct** + +`@postCosntruct`と同じく全てのインジェクションが終わった後に呼ばれる初期化メソッド名。 + +## PDO Example + +[PDO](http://php.net/manual/ja/pdo.construct.php)クラスの束縛の例です. + +```php +public PDO::__construct ( string $dsn [, string $username [, string $password [, array $options ]]] ) +``` + +```php +protected function configure() +{ + $this->bind(\PDO::class)->toConstructor( + \PDO::class, + [ + ['pdo' => 'pdo_dsn'], + ['username' => 'pdo_username'], + ['password' => 'pdo_password'] + ] + )->in(Scope::SINGLETON); + $this->bind()->annotatedWith('pdo_dsn')->toInstance($dsn); + $this->bind()->annotatedWith('pdo_username')->toInstance($username); + $this->bind()->annotatedWith('pdo_password')->toInstance($password); +} +``` + +PDOのどのインターフェイスがないので`toConstructor()`メソッドの二番目の引数の名前束縛でP束縛しています + ## スコープ デフォルトでは、Rayは毎回新しいインスタンスを生成しますが、これはスコープの設定で変更することができます。 diff --git a/README.md b/README.md index fc4aaa39..676dd5fb 100644 --- a/README.md +++ b/README.md @@ -538,9 +538,9 @@ To address this, Ray.Di has `toConstructor` bindings. class Car implements CarInerface { /** - * @Named("na") + * @Named("engine=na,number=registrtion_number") */ - public function __construct(EngineInterface $engine) + public function __construct(EngineInterface $engine, $numer, $passowrd) { // ... } @@ -577,16 +577,64 @@ protected function configure() $this ->bind(CarInterface::class) ->toConstructor( - Car::class, - 'na', // constructor injection - (new InjectionPoints) - ->addMethod('setWheel', "right") // setter injection - ->addOptionalMethod('setTurboCharger'), // optional setter injection - 'initialize' // @PostCosntruct + Car::class, // $class_name + [ + ['enginne' => 'na'], // $name + ['number' => 'registrtion_number'] + ], + (new InjectionPoints) // $setter_injection + ->addMethod('setWheel', "right") + ->addOptionalMethod('setTurboCharger'), + 'initialize' // $postCosntruct ); } ``` -Ray.Di will invoke that constructor and setter method to satisfy the binding and invoke in `initialize` method after all dependencies are injected. +### Parameter + +**class_name** + +Class Name + +**name** + +Parameter name binding. + +array `[[$parame_name => $binding_name],...]` or string `"param_name=binding_name&..."` + +**setter_injection** + +Setter Injection + +**postCosntruct** + +Ray.Di will invoke that constructor and setter method to satisfy the binding and invoke in `$postCosntruct` method after all dependencies are injected. + +## PDO Example + +Here is the example for the native [PDO](http://php.net/manual/ja/pdo.construct.php) class. + +```php +public PDO::__construct ( string $dsn [, string $username [, string $password [, array $options ]]] ) +``` + +```php +protected function configure() +{ + $this->bind(\PDO::class)->toConstructor( + \PDO::class, + [ + ['pdo' => 'pdo_dsn'], + ['username' => 'pdo_username'], + ['password' => 'pdo_password'] + ] + )->in(Scope::SINGLETON); + $this->bind()->annotatedWith('pdo_dsn')->toInstance($dsn); + $this->bind()->annotatedWith('pdo_username')->toInstance($username); + $this->bind()->annotatedWith('pdo_password')->toInstance($password); +} +``` + +Since no argument of PDO has a type, it binds with the `Name Binding` of the second argument of the `toConstructor()` method. ## Scopes ## diff --git a/src/Bind.php b/src/Bind.php index 14d7d814..58d5ddd0 100644 --- a/src/Bind.php +++ b/src/Bind.php @@ -105,7 +105,7 @@ public function to($class) /** * @param string $class class name - * @param string $name varName=bindName,... + * @param string | array $name "varName=bindName,..." or [[varName=>bindName],...] * @param InjectionPoints $injectionPoints injection points * @param null $postConstruct method name of initialization after all dependencies are injected * @@ -113,6 +113,9 @@ public function to($class) */ public function toConstructor($class, $name, InjectionPoints $injectionPoints = null, $postConstruct = null) { + if (is_array($name)) { + $name = $this->getStringName($name); + } $this->untarget = null; $postConstruct = $postConstruct ? new \ReflectionMethod($class, $postConstruct) : null; $this->bound = (new DependencyFactory)->newToConstructor(new \ReflectionClass($class), $name, $injectionPoints, $postConstruct); @@ -200,4 +203,26 @@ private function hasNotRegistered($interface) return $hasNotRegistered; } + + /** + * Return string + * + * input: [['varA' => 'nameA'], ['varB' => 'nameB']] + * output: "varA=nameA,varB=nameB" + * + * @param array $name + * + * @return string + */ + private function getStringName(array $name) + { + $names = array_reduce(array_keys($name), function ($carry, $key) use ($name) { + $carry[] .= $key . '=' . $name[$key]; + + return $carry; + }, []); + $string = implode(',', $names); + + return $string; + } } diff --git a/tests/BindTest.php b/tests/BindTest.php index 8200af36..f6c0cb72 100644 --- a/tests/BindTest.php +++ b/tests/BindTest.php @@ -67,12 +67,23 @@ public function testUntargetedBindSingleton() $this->assertSame(spl_object_hash($dependency1), spl_object_hash($dependency2)); } - public function testToConstructor() + public function nameProvider() + { + return [ + ['tmpDir=tmp_dir,leg=left'], + [['tmpDir' => 'tmp_dir','leg' => 'left']] + ]; + } + + /** + * @dataProvider nameProvider + */ + public function testToConstructor($name) { $container = new Container; $container->add((new Bind($container, ''))->annotatedWith('tmp_dir')->toInstance('/tmp')); $container->add((new Bind($container, FakeLegInterface::class))->annotatedWith('left')->to(FakeLeftLeg::class)); - $container->add((new Bind($container, FakeRobotInterface::class))->toConstructor(FakeToConstructorRobot::class, 'tmpDir=tmp_dir,leg=left')); + $container->add((new Bind($container, FakeRobotInterface::class))->toConstructor(FakeToConstructorRobot::class, $name)); $instance = $container->getInstance(FakeRobotInterface::class, Name::ANY); /* @var $instance FakeToConstructorRobot */ $this->assertInstanceOf(FakeLeftLeg::class, $instance->leg);