はじめに
Laravelにはサービスコンテナと呼ばれるクラスの依存関係を管理し、依存注入を実行するための強力な仕組みが備わっています。
この仕組みを使うことによってクラスの見通しが良くなったり、テストが書きやすくなったりするという利点がありますが、注入の仕方によって負荷が違ってくるので記事にしたいと思います。
動作確認環境
- Windows10 Home
- Laravel8
- PHP8.0
計測結果
番号 | 内容 | 結果 |
---|---|---|
1 | クラスを生成する | 0.69594383239746 ms |
2 | makeでクラスを生成する | 86.152076721191 ms |
3 | コンストラクトインジェクションでクラスを生成する | 222.5170135498 ms |
サンプル
sample1.php
public function sample1()
{
for ($i = 0; $i <= 10000; $i++) {
new HogeApplication();
}
}
sample2.php
public function sample2()
{
for ($i = 0; $i <= 10000; $i++) {
app()->make(HogeApplication::class);
}
}
sample3.php
public function sample3()
{
for ($i = 0; $i <= 10000; $i++) {
app()->make(HogeApplication::class);
}
}
class HogeApplication
{
public function __construct(protected Hoge2Application $hoge2Application)
{
}
}
番号 | 内容 | 結果 |
---|---|---|
4 | コンストラクトインジェクションでサービスプロバイダ経由で依存注入 | 174.88718032837 ms |
5 | 使用タイミングでサービスプロバイダ経由で依存注入 | 40.194988250732 ms |
6 | 外でサービスプロバイダ経由で依存解決したものを引数で渡す | 40.879011154175 ms |
※5については呼び出す度に依存解決処理が行われるため、一度呼び出したものは変数に保持しておくなどの対応が必要
sample4.php
public function sample4()
{
for ($i = 0; $i <= 10000; $i++) {
app()->make(HogeApplication::class);
}
}
class HogeApplication
{
public function __construct(
protected TesterInterface $testerInterface
) {
}
}
sample5.php
public function sample5()
{
for ($i = 0; $i <= 10000; $i++) {
$hogeApplication = new HogeApplication();
$hogeApplication->hoge();
}
}
class HogeApplication
{
public function hoge()
{
app()->make(TesterInterface::class);
}
}
sample6.php
public function sample6()
{
for ($i = 0; $i <= 10000; $i++) {
$test = app()->make(TesterInterface::class);
$hogeApplication = new HogeApplication($test);
}
}
class HogeApplication
{
public function __construct(protected $tester)
{
}
}
問題点
- コンストラクトインジェクションはapp()での依存解決するよりも格段に処理が重いです
- コンストラクトインジェクションの場合、依存解決必要のない場合であっても該当のクラスを生成したタイミングで依存解決の処理がかかってしまうことになります
まとめ
1つの依存解決程度では然程問題になることはないと思いますが、依存関係が複雑になってくると必要ないタイミングで依存解決処理が入ってしまいボトルネックとなるといったことがあるので、用法に気をつけて依存解決するようにしましょう。
この結果を見る限りでは、5番を上手く利用するのがベターなように思います。
おまけ
sample.php
// 以下は同じです。
app()->make(HogeService::class);
app()->makeWith(HogeService::class);
// 以下は同じです。
app()->make(HogeService::class, ['id' => 1]);
app()->makeWith(HogeService::class, ['id' => 1]);
公式ドキュメントを読むと違いがあるように書かれていますが、中の処理はまったく同じです。
引数を渡す場合であってもmakeを使えるので、makeWithと使い分ける必要はないでしょう。