はじめに
PHP 8.2.0から追加された readonly class 機能を使ってみます。
「コードの堅牢性・安全性を高めたい」という場合に有効だと思います。
この記事では、PHPのクラスのドキュメントを参考に、以下のようなケースで readonly class の活用例を紹介します。
- APIのリクエストデータを作成する
※本記事では下記のバージョンを参考にしています。
PHP 8.2.0
PHPの readonly class って何?
PHP 8.2.0から導入された readonly class(読み取り専用クラス)は、クラス内のすべてのプロパティを readonly として宣言し、イミュータブル(不変)なクラスとして設定する機能です。
メリット
- イミュータブル(不変。初期化後のプロパティの変更ができない)ため、初期化後に誤ってプロパティを変更することを防ぐ
- 「このクラスは不変である」という設計意図を明確に伝えることができる
- プロパティに型を指定するため型安全になる
-
readonlyを指定したクラスの子クラスではreadonlyを指定した場合にのみ継承できる。その制約により派生クラスによる意図しない動作を防ぐ
デメリット
- 初期化後にプロパティを変更できないため、柔軟性がない
- 型を指定していないプロパティや、
staticプロパティに対してはreadonlyを指定できない
PHPでの使い方
APIのリクエストデータ(DTO)を作成する
// CreateUserRequest.php
readonly class CreateUserRequest
{
// すべてのプロパティは readonly となり、初期化後は変更できない
public function __construct(
public int $id,
public string $name,
public string $email,
) {}
/**
* リクエストデータから新しいインスタンスを生成する
*/
public static function fromRequest(array $data): self
{
return new self(
id: $data['id'],
name: $data['name'],
email: $data['email'],
);
}
}
// 実行例
$requestData = [
'id' => 1,
'name' => 'Fish Suzuki',
'email' => 'fish_suzuki@example.com',
];
// DTOの生成
try {
// 初期化。初期化後は変更できない
$requestDto = CreateUserRequest::fromRequest($requestData);
// OK: 読み取り専用としてアクセス
echo "ユーザー名: " . $requestDto->name;
// NG: 処理途中で値を変更しようとすると例外が発生する
$requestDto->companyId = 20;
} catch (\InvalidArgumentException $e) {
echo "Error: " . $e->getMessage();
}
まとめ
PHP 8.2.0から追加された readonly class を使うことで、以下のようなメリットがあると感じます。
- 初期化後に誤ってプロパティを変更することを防ぐため、堅牢性・安全性が高まる
- 設計意図を伝えやすい
- 型安全
設計の意図を伝えやすく、かつ安全性を高められる機能のため、今後も活用していきたいと思います。