【PHPフレームワークFlow】インターフェースをDIしたときの挙動について

# 初めに
最近PHPのマイナーフレームワークFlowを触っているのですが、インターフェースをDIしたときの挙動について気になることがありました。
いろいろ調べてみたので分かったことをアウトプットします。

# DIしたインターフェースのメソッドを呼び出すとどうなる？

Flowのソースを見てると、以下のようにDIしたインターフェースのメソッドを呼び出すような処理がありました。

```php

class Hoge {

    /**
     * @Flow\Inject
     * @var \Neos\Welcome\Service\Interface\FugaInterface
     */
    protected $fugaInterface;

    public function hogeMethod()
    {
        $this->fugaInterface->fugaMethod()
    }
}
```


最終的に```fugaMethod()```を呼び出していますが、```$fugaInterface```はインターフェースのため実装は書いてありません。
このように、**実装クラスを明示していない場合、どのメソッドが呼び出されるのでしょうか？**


# インターフェースをDIしたときの挙動

[Flowの公式ドキュメント](https://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/ObjectManagement.html#:~:text=All%20PHP%20interfaces%20for%20which%20only%20one%20implementation%20class%20exist%20are%20also%20automatically%20registered%20as%20object%20names%2C%20with%20the%20implementation%20class%20being%20returned%20when%20asked%20for%20an%20instance%20of%20the%20interface.)にはインターフェースをDIしたときの挙動について以下のように書いてあります。

> All PHP interfaces for which only one implementation class exist are also automatically registered as object names, with the implementation class being returned when asked for an instance of the interface.

↓GPTに要約してもらいました。
> この文は、PHPにおいて、単一の実装クラスしか存在しないインターフェースは自動的にオブジェクト名として登録され、そのインターフェースのインスタンスが要求されると、対応する実装クラスが返されると述べています。

つまり、**インターフェースの実装クラスが1つの場合、FWが実装クラスを自動で選択してくれる**というわけですね。


# 試してみた
というわけで、実装クラスが1つの場合と複数ある場合をそれぞれ試してみました。


## パターン1：実装クラスが1つの場合
以下のようなディレクトリ構成にしました。
ControllerでインターフェースをDIしてメソッドを呼び出すシンプルな作りです。

```
Packages/
  ├ Application/
  |    └ Neos.Welcome/
  |         └ Classes/
  |              ├ Controller/
  |              |    └ TestExceptionHandlerController.php（★）
  |              └ Service/
  |                   ├ Impl/
  |                   |    └ TestImpl.php（★）
  |                   └ Interface/
  |                        └ TestInterface.php（★）
  ├ Framework/
  └ Libraries/
```

それぞれのソースは以下になります。
```php:Controller
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;

class DiTestController extends ActionController
{

    /**
     * @Flow\Inject
     * @var \Neos\Flow\Mvc\View\JsonView
     */
    protected $view;

    /**
     * @Flow\Inject
     * @var \Neos\Welcome\Service\Interface\TestInterface
     */
    protected $testInterface;

    /**
     * @return void
     */
    public function interfaceTestAction()
    {
        $this->view->assign('value', array(
            $this->testInterface->testMethod()
        ));
    }
}
```

```php:Interface
<?php
namespace Neos\Welcome\Service\Interface;

interface TestInterface
{

    /**
     * @return void
     */
    public function testMethod();
}
```

```php:Impl
<?php
namespace Neos\Welcome\Service\Impl;

use \Neos\Welcome\Service\Interface\TestInterface;

class TestImpl implements TestInterface
{

    /**
     * @return void
     */
    public function testMethod()
    {
        return "output testImpl !!";
    }
}
```

実際に叩いてみたところ、実装クラスで定義したメソッドが呼び出されていることが確認できました。
```sh
$ curl http://localhost:8081/Neos.Welcome/DiTest/interfaceTest
["output testImpl !!"]
```


## パターン2：実装クラスが複数の場合
以下のようにTestImterfaceを継承するクラスを2つに増やしました。

```
Packages/
  ├ Application/
  |    └ Neos.Welcome/
  |         └ Classes/
  |              ├ Controller/
  |              |    └ TestExceptionHandlerController.php（★）
  |              └ Service/
  |                   ├ Impl/
  |                   |    ├ TestImpl.php（★）
  |                   |    └ TestImpl2.php（★）
  |                   └ Interface/
  |                        └ TestInterface.php（★）
  ├ Framework/
  └ Libraries/
```

キャッシュをクリアしてサーバを立て直したところ、```TestInterface```が見つからないという旨のメッセージがでて失敗しました。
```sh
$ ./flow server:run
The object "Neos\Welcome\Service\Interface\TestInterface" which was specified as a property in the object configuration of object "Neos\Welcome\Controller\DiTestController" (automatically registered class) does not exist.
Check for spelling mistakes and if that dependency is correctly
configured.
```

# 最後に
DIを自動で解決してくれるのは便利ですが、知らないと混乱しそうですね。
（自分が知らないだけで他のFWもこうなのかもしれないですが）
また一つFlowについて知ることができました。
また何か分かったらアウトプットしようと思います。

# 参考
https://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/ObjectManagement.html