Contents
型宣言の基本概念とサポートタイプ
PHP における 型宣言 は、変数・引数・戻り値が期待した型であることをコンパイル時または実行時に保証します。型が合わない場合は TypeError が投げられ、バグの早期発見とコードの可読性向上につながります。本章では PHP が提供する全ての型を一覧化し、実務での利用シーンを具体例とともに示します。
スカラー型・ユニオン型・Intersection 型
スカラー型は int, float, string, bool の 4 種類です。
PHP 8.0 からは ユニオン型(int|float)が、PHP 8.2 からは Intersection 型 が利用可能になりました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// ユニオン型の例:数値系ならどちらでも受け付ける function formatNumber(int|float $value): string { return number_format($value, 2); } /* Intersection 型の正しい書き方(PHP 8.2+) * JsonSerializable と Countable の両方を実装したオブジェクトだけを受け取ります。 */ interface JsonSerializable {} interface Countable {} function process(JsonSerializable&Countable $obj): void { // ここでは $obj が both interfaces を満たすことが保証されます echo json_encode($obj); } |
ポイント:Intersection 型は
&(アンパサンド)で結合し、型ヒントの位置に直接記述します。ユニオン型と同様に|ではなく&を使う点に注意してください。
mixed・never・リテラル型(true, false, null)
- mixed は「どんな型でも受け付ける」ことを明示し、意図的に型チェックを緩めたいときに使用します。
- never は関数が決して正常終了しない(例外や
exit)ことを表現し、静的解析で到達不能コードを検出できます。 - リテラル型 は特定のリテラル値だけを許容します(
true,false,null)。PHP 8.2 で導入されました。
|
1 2 3 4 5 6 7 8 9 |
function alwaysThrows(): never { throw new RuntimeException('この関数は決して戻らない'); } /* リテラル型の例:返り値が true または false のみ */ function isEnabled(bool $flag): true|false { return $flag; } |
declare(strict_types=1) の意味と TypeError 発生タイミング
declare(strict_types=1) をファイル冒頭に記述すると、スカラー型の暗黙的な型変換が無効化されます。strict モードと non‑strict モードで同じコードを実行したときの挙動差を把握し、どのケースで TypeError が発生するかを具体例で確認します。
strict モードと非 strict モードの違い
以下の 2 ファイルは同一関数 add() を定義していますが、strict 宣言の有無だけが異なります。
|
1 2 3 4 5 6 7 8 9 |
// file_strict.php declare(strict_types=1); function add(int $a, int $b): int { return $a + $b; } echo add(2, '3'); // ← TypeError が発生 |
|
1 2 3 4 5 6 7 |
// file_loose.php(strict 宣言なし) function add(int $a, int $b): int { return $a + $b; } echo add(2, '3'); // ← 文字列が自動的に整数へキャストされ、5 が出力 |
TypeError がスローされる代表的ケース
| ケース | strict 時の結果 |
|---|---|
スカラー引数に文字列数値 ('10') を渡す |
TypeError |
float → int の暗黙変換 |
TypeError |
nullable でないパラメータへ null が渡された |
TypeError |
| 戻り値が宣言型と不一致 | TypeError |
declare(strict_types=1)の詳細は公式マニュアルの「型宣言」をご参照ください。
関数・メソッド・プロパティへの型宣言ベストプラクティス
関数やメソッドだけでなく、クラスプロパティにも型を付与できるようになったことで、オブジェクト全体の型安全性が向上します。ここでは実務ですぐに活用できる指針とコード例を示します。
パラメータ・戻り値型宣言の基本方針
- nullable は
?を付けて明示し、デフォルト引数は必ず nullable と合わせます。 - 複数の型が許容できる場合はユニオン型(例:
int|float)を使用し、可読性を保ちつつ柔軟性も確保します。 - 戻り値がオブジェクトになるケースではインターフェイスで抽象化し、実装クラスの差し替えを容易にします。
|
1 2 3 4 5 6 7 8 9 10 11 |
class OrderProcessor { /** * @param array<int, array{price: float}> $items 商品情報配列 * @param float|null $discount 割引率(0〜1 の小数)または null */ public function calculateTotal(array $items, ?float $discount = null): float { $sum = array_reduce($items, fn(float $c, array $i) => $c + $i['price'], 0.0); return $discount !== null ? $sum * (1 - $discount) : $sum; } } |
プロパティ型宣言と readonly の活用(PHP 8.1+)
- 必須プロパティ はコンストラクタで初期化し、未初期化状態を防ぎます。
readonly修飾子は「代入後に変更不可」な不変オブジェクトを作りやすくし、バグの温床になるミューテーションを排除します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class User { public readonly int $id; // 生成時に一度だけ設定可能 public string $name; private ?DateTimeImmutable $lastLogin = null; public function __construct(int $id, string $name) { $this->id = $id; $this->name = $name; } public function setLastLogin(DateTimeImmutable $dt): void { $this->lastLogin = $dt; // nullable プロパティへの代入例 } } |
プロパティ型の正式仕様は公式マニュアル「型宣言」に掲載されています。
PHP 8.3 の新機能:クラス定数への型宣言
PHP 8.3 からは クラス定数 にも型を付与できるようになり、IDE 補完や静的解析の精度が大幅に向上しました。ここでは構文・メリット・実装例を詳しく解説します。
基本構文と使用例
|
1 2 3 4 5 |
class Pagination { public const int PER_PAGE = 20; // 整数型定数 public const bool ENABLE_CACHE = true; // 真偽値型定数 } |
- 定数に型を付けるだけで、型が合わない代入は ParseError としてコンパイル時に検出されます。
- 型付き定数は
ReflectionClassConstant::hasType()やgetType()でも取得可能です。
従来(PHP ≤8.2)との比較表
| 項目 | PHP ≤8.2 (型なし) | PHP 8.3 (型あり) |
|---|---|---|
| 構文 | const PER_PAGE = 20; |
public const int PER_PAGE = 20; |
| 型安全性 | なし(任意のスカラーへ変更可) | 定数に割り当てられる値は宣言型と一致必須 |
| 静的解析・IDE 補完 | 制限あり | 完全対応、リテラル型も認識 |
リテラル型・enum 型との併用例
|
1 2 3 4 5 6 7 8 9 10 11 12 |
enum Status: string { case Draft = 'draft'; case Published = 'published'; } class Article { public const Status DEFAULT_STATUS = Status::Draft; // enum 型定数 /* nullable な文字列リテラル型定数の正しい記法(PHP 8.3) */ public const ?string NULLABLE_CONST = null; } |
?を付けた型指定により nullable リテラル型定数を安全に宣言できます。- 以前は
public const null NULLABLE_CONST = null;のような構文がエラーになるため、上記の書き方が推奨されます。
クラス定数への型宣言に関する詳細は公式マニュアル「型宣言」をご参照ください。
また、Qiita の実装例(執筆者は PHP コミュニティで高評価を得ている開発者)では具体的なコードとベストプラクティスが紹介されていますので、導入時の参考に最適です【Qiita 記事】。
実務での落とし穴と安全な導入ステップ
型宣言は品質向上に有効ですが、レガシーコードや外部ライブラリとの相性を見誤ると逆効果になることがあります。ここでは典型的な問題点と段階的な移行手順をまとめました。
外部ライブラリとの互換性チェックポイント
- Composer パッケージ が PHP 8.3 未対応の場合は
composer.jsonのplatform設定でバージョン上限を明示し、必要に応じて代替パッケージへ切り替えます。 - ライブラリ内部で
declare(strict_types=0)が書かれていても、呼び出し側が strict モードであれば影響はありません。ただし、型ヒントの不一致はTypeErrorを引き起こす点に注意してください。
動的参照・リフレクション時の注意
|
1 2 3 4 5 6 7 8 9 10 11 |
$constName = 'PER_PAGE'; $value = (new ReflectionClass(Pagination::class)) ->getConstant($constName); // 20 が取得できる // PHP 8.3 の新メソッドで型情報も取得可能 $rc = new ReflectionClassConstant(Pagination::class, $constName); if ($rc->hasType()) { $type = $rc->getType(); // ReflectionNamedType インスタンス echo "定数の型は {$type} です。"; } |
- 動的に定数名を組み立てる場合は
ReflectionClassConstant::hasType()で型チェックし、期待外れの型が混入しないようガードします。
段階的導入手順(実務向けロードマップ)
- PHP バージョンを統一 –
composer.jsonのplatform: {"php": "8.3"}を設定し、CI でバージョンチェック。 - テストカバレッジの測定 – カバレッジが低い箇所は先にリファクタリングしてから型宣言を追加。
- スカラー・ユニオン型から着手 –
int|floatなどシンプルなユニオン型で全関数・メソッドのパラメータと戻り値に型付与。 - プロパティと readonly の導入 – 不変オブジェクトを中心に
readonlyを適用し、ミューテーションバグを削減。 - クラス定数への型宣言 – 変更が安全な定数(設定値・ステータスコード等)から順次型付与。IDE の補完が効くことを確認。
- CI/CD に静的解析ツールを組み込む –
phpstanやpsalmを実行し、潜在的な型エラーをプルリクエスト時に検出。
公式マニュアルと Qiita 記事は、実装例・注意点が豊富で導入時の必読リソースです【PHP マニュアル】【Qiita 記事】。
まとめ
- 型宣言 は
TypeErrorによる安全性確保とコード可読性向上の両輪です。 declare(strict_types=1)により暗黙的なスカラー変換が無効化され、意図しない型揺らぎを防げます。- PHP 8.3 からは クラス定数 へも型付与でき、IDE 補完・静的解析の精度が格段に上がります。
- 実務で導入する際は外部ライブラリとの互換性とリフレクション使用時の型取得を意識し、段階的な移行計画を策定すれば既存コードベースでも安全にアップグレードできます。
これらのポイントを踏まえて、プロジェクト全体に型宣言を浸透させ、保守性と信頼性の高い PHP アプリケーションを構築してください。
参考リンク
-
PHP 公式マニュアル – 型宣言
https://www.php.net/manual/ja/language.types.declarations.php -
Qiita 記事 – PHP8.3 がリリースされたので新機能全部やる(執筆者:ranakualu、コミュニティで高評価)
https://qiita.com/rana_kualu/items/f03efb9810c3ac5b2ab6