PHP 8.4で追加されたプロパティフック・非対称可視性・新配列関数などをLaravelの実コードで解説。DTOやEloquentモデルへの実践的な適用例とアップグレード時の注意点を中級者向けに紹介します。
「PHP 8.4って結局、何が嬉しいの?」と思っていませんか?
メジャーバージョンアップのたびに感じる、あの微妙な感覚。「新機能が追加された」とリリースノートを読んでも、「で、自分の日常的なLaravel開発で何がどう変わるの?」がピンとこない──そんな経験はないでしょうか。
PHP 8.4は2024年11月にリリースされた最新の安定版です。8.3までの流れを受け継ぎながらも、言語レベルで書き方が変わる機能がいくつか追加されており、特にLaravelのEloquentモデル・DTO・サービスクラスとの相性が良い変更が目立ちます。
この記事では「実際のコードでどう変わるか」を軸に、PHP 8.4の新機能をBefore / Afterの形式で具体的に解説します。技術選定者の方には各機能の「なぜ使うべきか」もあわせて整理していますので、チームへの導入判断の材料にしてください。
PHP 8.4がLaravel開発者にとって重要な理由
PHP 8.xシリーズはパフォーマンス改善と型安全性の強化を軸に進化してきました。8.4では特にオブジェクト指向の表現力が大きく向上しています。
LaravelはEloquentモデル・DTO・フォームリクエスト・サービスクラスといった構造を多用するフレームワークです。これらの構造に対してPHP 8.4の新機能がピンポイントで効いてくるため、「書くコードの量が減り、意図がコードに直接現れる」という変化が期待できます。
アーキテクチャの観点からも、DTOや値オブジェクトをより厳密に表現できるようになることで、チームでのコードレビューや新規メンバーのオンボーディングがスムーズになります。
① プロパティフック(Property Hooks)
何が変わったか
PHP 8.4では、プロパティに get / set フックを直接定義できるようになりました。従来、プロパティへのアクセス制御や変換処理はゲッター・セッターメソッドで実装していましたが、その記述がプロパティ定義と一体化します。
class UserDto
{
private string $email;
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): void
{
$this->email = strtolower(trim($email));
}
}
$user = new UserDto();
$user->setEmail(' [email protected] ');
echo $user->getEmail(); // [email protected]
Laravelでの使いどころ
フォームリクエストから生成するDTOや、APIレスポンス用の値オブジェクトで特に効果を発揮します。「このプロパティはセット時に正規化する」という意図がクラス定義から直接読み取れるため、コードの自己文書化が進みます。
// フォームリクエスト → DTO の変換例
class CreateProductDto
{
public string $name {
set(string $value) {
$this->name = mb_trim($value);
}
}
public int $priceCents {
get {
return $this->priceCents;
}
set(int $value) {
if ($value < 0) {
throw new InvalidArgumentException('Price must be non-negative.');
}
$this->priceCents = $value;
}
}
}
Eloquentモデルでは引き続き $casts や Attribute::make() が主流ですが、モデル外のDTOレイヤーにプロパティフックを使うと、バリデーションロジックの分散を防げます。
② 非対称可視性(Asymmetric Visibility)
何が変わったか
「外から読めるが、外から書き換えられない」という制約をプロパティ単位で表現できるようになりました。public private(set) のように、読み取りと書き込みで異なる可視性を設定できます。
class Order
{
private OrderStatus $status;
public function getStatus(): OrderStatus
{
return $this->status;
}
public function transition(OrderStatus $new): void
{
// ステータス遷移ロジック
$this->status = $new;
}
}
Laravelでの使いどころ
DDDやクリーンアーキテクチャを採用しているチームで特に有効です。Eloquentモデルをラップするドメインエンティティや、ステートマシンパターンを使った注文・申請フローの実装で、「状態の変更は必ず専用メソッド経由」という設計意図をコードで強制できます。
// 申請ドメインエンティティの例
class ApplicationEntity
{
public private(set) string $applicantId;
public private(set) ApplicationStatus $status;
public private(set) \DateTimeImmutable $createdAt;
public function __construct(string $applicantId)
{
$this->applicantId = $applicantId;
$this->status = ApplicationStatus::Draft;
$this->createdAt = new \DateTimeImmutable();
}
public function submit(): void
{
if ($this->status !== ApplicationStatus::Draft) {
throw new DomainException('Only draft applications can be submitted.');
}
$this->status = ApplicationStatus::Submitted;
}
}
ゲッターメソッドの乱立を防ぎながら、イミュータブルに近い設計を維持できる点が実務上のメリットです。
③ new without parentheses
何が変わったか
new ClassName() のカッコを省略したまま、即座にメソッドチェーンを呼べるようになりました。地味に見えて、日常的な記述が随所でスッキリします。
$result = (new QueryBuilder())->select('*')->from('users')->get();
// またはこう書いていた
$builder = new QueryBuilder();
$result = $builder->select('*')->from('users')->get();
Laravelでの使いどころ
サービスクラスのファクトリメソッドや、テストコードでのオブジェクト生成が読みやすくなります。Laravelのテストで (new FooService())->handle() のようなパターンを書いていた箇所が、余分なカッコなしで書けます。
// Featureテストでの利用例
$response = new HttpClient()
->withToken($this->token)
->post('/api/orders', $payload);
// DTOのファクトリでも自然に使える
$dto = new CreateOrderDto()->fromRequest($request);
④ 新しい配列関数(array_find / array_find_key / array_any / array_all)
何が変わったか
コールバックベースで配列を検索・検証する関数が4つ追加されました。これまで array_filter + reset の組み合わせや array_reduce で書いていたコードが簡潔になります。
// 条件を満たす最初の要素を取得
$first = reset(array_filter($items, fn($item) => $item->isActive())) ?: null;
// 全要素が条件を満たすか検証
$allValid = count(array_filter($items, fn($item) => $item->price > 0)) === count($items);
// いずれかが条件を満たすか検証
$hasExpired = count(array_filter($items, fn($item) => $item->isExpired())) > 0;
Laravelでの使いどころ
LaravelではCollectionが充実しているため、Eloquentの結果に対しては ->first() や ->every() を使うことが多いです。ただし、CollectionではなくプレーンなPHP配列を扱うサービスクラスやユーティリティ、外部APIのレスポンスを直接処理する処理では、これらの関数が素直に使えて便利です。
// 外部APIレスポンスの処理(配列のまま扱う場合)
$apiItems = json_decode($response->body(), true);
$activeItem = array_find($apiItems, fn($item) => $item['status'] === 'active');
$allShipped = array_all($apiItems, fn($item) => $item['shipped'] === true);
⑤ #[\Deprecated] 属性
何が変わったか
PHP 8.4では公式の #[\Deprecated] 属性が追加され、関数・メソッド・クラス定数に非推奨マークをコードレベルで付与できるようになりました。
class OrderService
{
#[\Deprecated(
message: 'Use processOrderV2() instead.',
since: '2.0.0'
)]
public function processOrder(array $data): void
{
// 旧実装
}
public function processOrderV2(CreateOrderDto $dto): OrderResult
{
// 新実装
}
}
Laravelでの使いどころ
チームで開発するプロダクトでAPIを段階的に移行する際、旧メソッドを即削除せずに #[\Deprecated] でマークしておくことで、IDEが警告を出し、呼び出し箇所を追いやすくなります。リファクタリングの進捗管理にも使えます。
⑥ 遅延オブジェクト(Lazy Objects)
何が変わったか
PHP 8.4では ReflectionClass::newLazyGhost() / newLazyProxy() を使って、初期化コストの高いオブジェクトの生成を実際に使われる瞬間まで遅延できるようになりました。
$reflector = new ReflectionClass(HeavyService::class);
$lazyService = $reflector->newLazyGhost(function (HeavyService $instance) {
// 実際に使われるまでここは呼ばれない
$instance->__construct(
config('services.heavy.endpoint'),
config('services.heavy.api_key')
);
});
// $lazyService を渡しておき、実際にメソッドを呼んだ瞬間に初期化される
Laravelでの使いどころ
Laravelのサービスコンテナは既にレイジーバインディングの仕組みを持っているため、アプリケーションコードで直接使う機会は限定的です。ただし、フレームワークやパッケージ開発の文脈、あるいはDIコンテナをカスタム実装している場合には強力なツールになります。将来的にLaravel本体がこの機能を内部で活用する可能性もあるため、概念として押さえておく価値はあります。
⑦ 新しいHTML5対応DOM(Dom\HTMLDocument)
何が変わったか
Dom\HTMLDocument クラスが追加され、HTML5の仕様に準拠したDOMパースが可能になりました。従来の DOMDocument はHTML5の仕様との乖離があり、モダンなHTMLの処理で問題が生じることがありました。
$doc = new DOMDocument();
@$doc->loadHTML($html); // HTML5タグで警告が出ることがあった
$xpath = new DOMXPath($doc);
$nodes = $xpath->query('//main');
Laravelでの使いどころ
スクレイピング処理・メール本文のHTMLパース・外部コンテンツの変換処理を実装しているサービスクラスで役立ちます。querySelector が使えるようになったことで、XPathを書かなくて済むケースが増えます。
アップグレード時によくある失敗パターンと注意点
PHP 8.4へのアップグレードは魅力的ですが、現場での移行には以下の点で失敗しやすいので注意が必要です。
❶ 拡張・パッケージの対応状況を確認せずに上げてしまう
PHP 8.4で非推奨になった内部関数・挙動の変更が、依存パッケージに影響することがあります。composer outdated と composer why-not php:8.4 を事前に実行し、各パッケージのPHP 8.4対応状況を必ず確認してから本番環境に適用してください。
❷ 開発環境だけ上げて本番はそのまま
Local・Staging・Productionのバージョンが揃っていない状態でコードをデプロイすると、プロパティフックや非対称可視性の構文がそのまま本番でパースエラーになります。CI/CDパイプラインにPHPバージョンの一致確認を組み込むのが安全です。
❸ #[\Deprecated] を使ったら即削除したと思い込む
属性を付けても実際のコードは削除されていません。次のメジャーリリースで本当に削除するまでのスケジュールをチケットに記録し、属性の since パラメータで管理する運用ルールを決めておきましょう。
❹ プロパティフックと readonly の併用に注意
readonly プロパティとプロパティフックの併用には制約があります。特に set フックのある readonly プロパティの扱いは、公式ドキュメントで動作を確認してから設計してください。意図しない動作を避けるため、チーム内でコーディング規約を統一することを勧めます。
PHP 8.4 新機能 活用マップ
| 機能 | Eloquentモデル | DTO/値オブジェクト | サービスクラス | テストコード |
|---|---|---|---|---|
| プロパティフック | ||||
| 非対称可視性 | ||||
| new without parentheses | ||||
| 新配列関数 | ||||
| #[Deprecated]属性 | ||||
| 遅延オブジェクト | ||||
| Dom\HTMLDocument |
アップグレード前チェックリスト
この技術、御社の業務にも活かせます
業務システム開発
Laravelを使った業務システムの構築・改修を20年以上の経験でサポートします
※ 通常1営業日以内にご返信します
まとめ:PHP 8.4は「書き方を変える」アップデート
PHP 8.4の新機能は、パフォーマンスの劇的な改善というよりも、コードの表現力と設計の明確さを高める変更が中心です。
プロパティフックと非対称可視性は、DTOや値オブジェクトの設計に直接影響します。新配列関数はコレクション外でのデータ処理をシンプルにし、new without parentheses は日常的な記述の摩擦を下げます。どれも「大きな機能」ではないものの、積み重なることでコードベース全体の読みやすさに効いてきます。
弊社Fivenine Designでも、自社プロダクトの新規コードをPHP 8.4で書き始めており、特にDTOレイヤーでのプロパティフックと非対称可視性の組み合わせが実務上便利であることを実感しています。
既存プロジェクトへの適用は、依存パッケージの対応状況を確認しながら段階的に進めるのが現実的です。まずは新規モジュールやリファクタリング対象のクラスから試してみてください。
PHP 8.4への移行戦略や、Laravelアプリケーションの設計についてお悩みの方は、お気軽にご相談ください。 Laravel製の自社サービスを開発・運用してきた経験をもとに、プロジェクトの状況に合ったアドバイスを無料でお伝えしています。