コードがクラッシュする理由:「未初期化(Uninitialized)」状態
PHP 7.4で型付きプロパティが導入されましたが、PHP 8ではその扱いがより厳格になりました。通常の型指定のないプロパティは、デフォルトで常にnullになります。しかし、型付きプロパティは、明示的に値を代入するまで**uninitialized(未初期化)**と呼ばれる「ゾンビ」状態で存在します。初期化前に読み取ろうとすると、PHPは単なる警告ではなく、致命的なエラー(Fatal Error)を出してスクリプトを停止させます。
Fatal error: Uncaught Error: Typed property Foo::$bar must not be accessed before initialization
このエラーは通常、PHP 8への移行中や、Nullableなプロパティ(?stringなど)が自動的にnullに初期化されると思い込んでいる場合に発生します。実際には自動では初期化されません。データの扱いには意図的である必要があります。
エラーが発生するコードの例
<?php
class UserProfile {
public string $username;
public int $age;
}
$profile = new UserProfile();
echo $profile->username; // ❌ ここでFatal Errorが発生します
?>
エラーの修正方法
クラスの構造に応じて、いくつかの異なる方法でこの問題に対処できます。
1. 最もクリーンな修正方法:コンストラクタのプロパティプロモーション
モダンなPHPでは、プロパティの定義と初期化を一度に行うことができます。PHP 8.0以降、コンストラクタのプロパティプロモーション(Constructor Property Promotion)を使用することで、すべてのオブジェクトが有効なデータで開始されることを保証できます。簡潔でボイラープレートを排除できます。
<?php
class UserProfile {
// 1行で定義と代入を行う
public function __construct(
public string $username = "Guest",
public int $age = 0
) {}
}
$profile = new UserProfile();
echo $profile->username; // 正常に動作: "Guest" を出力
?>
2. 素早い解決策:インラインでのデフォルト値設定
コンストラクタ経由で値を渡す必要がない場合は、クラス定義の中で直接デフォルト値を代入するだけです。これにより、プロパティは即座に危険な「未初期化」状態から脱却します。
<?php
class AppConfig {
public string $theme = 'light';
public bool $enabled = false;
}
$config = new AppConfig();
echo $config->theme; // 正常に動作: "light" を出力
?>
3. Nullableに関する注意点
型にクエスチョンマークを付ける(?stringなど)ことでNullableになりますが、それだけで自動的にデフォルト値がnullになるわけではありません。明示的に設定する必要があります。これは、レガシーな7.x系のコードをリファクタリングする際によくある落とし穴です。
<?php
class Document {
// ❌ すぐにアクセスすると依然としてクラッシュします
// public ?string $title;
// ✅ 安全:nullで初期化済み
public ?string $title = null;
}
$doc = new Document();
var_dump($doc->title); // 正常に動作: NULL を出力
?>
4. isset()による「ジャストインタイム」チェック
データベースから後で取得する必要がある場合など、すぐに値を提供できないことがあります。このような場合は isset() を使用します。PHPでは、未初期化のプロパティに対して isset() は false を返すため、必要なときだけデータをロード(遅延読み込み)することが可能です。
<?php
class LazyLoader {
public string $data;
public function getData(): string {
if (!isset($this->data)) {
$this->data = $this->fetchFromApi(); // 遅延読み込みの具体例
}
return $this->data;
}
private function fetchFromApi(): string {
return "Critical Data Loaded";
}
}
?>
ソリューションのテスト
ターミナルで簡単なチェックを行い、クラッシュが解消されたことを確認します。スクリプトが Uncaught Error を出さずに終了すれば、プロパティの状態管理は成功です。
# スクリプトを実行
php your_script.php
# 成功時の出力例:
string(5) "Guest"
より良いPHPコードのためのプロのヒント
- **Nullに対して厳格になる:** `null` がビジネスロジックにおいて実際に意味を持つ場合のみ、プロパティをNullableにします。ユーザーに名前が*必須*である場合は、コンストラクタで強制的に設定するようにします。
- **チェックを自動化する:** レベル6以上のPHPStanを使用しましょう。プロジェクト全体をスキャンし、保存する前にプロパティの初期化を忘れている箇所を正確に指摘してくれます。
- **マッパーに注意する:** DoctrineなどのORMを使用している場合は、データベースのスキーマがクラスの型と一致していることを確認してください。Null不可のプロパティに空のデータベースカラムを読み込もうとすると、同じエラーが発生します。

