👀

コンストラクタのプロモーションとは

2023/01/22に公開

概要

PHP 8.0以降に提供されているconstructメソッドの省略記法

https://www.php.net/manual/ja/language.oop5.decon.php

PHP 8.0.0 以降では、コンストラクタの引数を 対応するオブジェクトのプロパティに昇格させることができます。 コンストラクタの引数をプロパティに代入し、それ以外の操作を行わないことはよくあることです。 コンストラクタのプロモーションは、こういった場合の短縮記法を提供します。 「コンストラクタを引数と一緒に使う」の例は、次のように書き直すことが出来ます。(※具体例省略)

Laravelコントローラーでの使用例

  • PHP v8.2.1
  • Laravel v9.48.0
php artisan make:controller UserController

従来の書き方

app/Http/Controllers/UserController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Log\LogManager;
use Psr\Log\LoggerInterface;

class UserController extends Controller
{
    /** @var LogManager */
    private $logger; // プロパティを定義する

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    /**
     * Undocumented function
     *
     * @return void
     */
    public function index(): void
    {
        dd($this->logger);
    }
}

省略記法

app/Http/Controllers/UserController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Log\LogManager;
use Psr\Log\LoggerInterface;

class UserController extends Controller
{
    /**
     * @param LoggerInterface $logger
     */
    public function __construct(private LoggerInterface $logger)
    {
        // プロパティへの注入を書かない
    }

    /**
     * Undocumented function
     *
     * @return void
     */
    public function index(): void
    {
        dd($this->logger);
    }
}

実行してみる

routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index']);


localhost/users

補足

コンストラクタの引数に、アクセス権の修飾子が含まれている場合、 PHP はそれをオブジェクトのプロパティ、かつコンストラクタの引数であると解釈します。 そして、その引数の値をプロパティに代入します。 コンストラクタの本体は空にすることもできますし、 他の文を含めることも出来ます。 引数の値が対応するプロパティに代入された後、 追加の文が実行されます。

全ての引数をプロパティに昇格させる必要はありません。 昇格させる引数と、させない引数を混ぜることもできます。 プロパティに昇格した引数は、コンストラクタの呼び出しコードになんの影響も与えません。

と、ありますがプロパティ宣言をして同時にコンストラクタのプロモーションを使おうとすると再宣言不可ということでFatalErrorが投げられます。

省略記法への個人的な感想

IDE操作でプロパティから実装クラスにジャンプすることが多いため、Interfaceの実装クラスを注入する場合はわざわざ実装クラスを検索するのが面倒な気がする。
※IDEで解決できるのであれば今後知りたいポイントです。

コンストラクタで注入するクラスが少ないかつ実装が自明なものだったり、プリミティブ型しか使わないケースは良いと思うが積極的に使うことは少なそうな印象。

こういうケースで使ってるよ!というお話があればぜひ教えてください!

Discussion