PHP Fatal error: Call to a member function on null の修正方法

intermediate🐘 PHP2026-04-22| PHP 7.0以降 / PHP 8.x、任意のOS(Linux、macOS、Windows)、任意のフレームワーク(Laravel、Symfony、WordPress、バニラPHP)

Error Message

PHP Fatal error: Uncaught Error: Call to a member function [function_name]() on null
#php#致命的エラー#nullポインタ#オブジェクト

エラーの内容

PHPアプリは正常に動いていた――ある日突然、そうでなくなる。ログを確認すると、こんなエラーが目に入る:

PHP Fatal error: Uncaught Error: Call to a member function getName() on null
in /var/www/html/app/Controllers/UserController.php:42

メソッド名はプロジェクトによって異なる。しかしパターンは変わらない――オブジェクトであるべき変数がnullを保持した状態でメソッドを呼び出してしまっている。PHPは即座に停止する。リカバリも部分的な出力もなく、ただ致命的エラーが発生するだけだ。

根本原因

代入からメソッド呼び出しまでの間のどこかで、変数からオブジェクトが失われている。ほとんどのケースは以下のいずれかに当てはまる:

  • データベースクエリが結果を返さず(nullまたは空)、確認せずにメソッドを呼び出した。
  • ファクトリメソッドやDIコンテナが、バインドの欠如や設定ミスによりnullを返した。
  • クラスのプロパティが使用前に初期化されていなかった。
  • ある処理ルートで関数がnullを返すケースを見落としていた――json_decode()preg_match()でよくある。
  • 例外を握りつぶすカスタムコンストラクタのせいで、new ClassName()の呼び出しがサイレントに失敗した。

ステップ1:何がnullなのかを正確に特定する

推測はしないこと。PHPが報告した行に直接移動し、問題のある呼び出しの直前で変数をダンプする:

<?php
// エラーはここで発生している:
$user->getName();

// この直上に追加する:
var_dump($user); // NULLなら、それが原因だ。
die();

何がnullかを確認したら、それがどこで代入されたかを遡って調べる。問題の本質は、メソッド呼び出しの行ではなく、代入の行にある。

修正1:nullチェックでガードする

nullが正当な状態(ユーザーが存在しない、レコードが削除済みなど)である場合、明示的なチェックが適切な対処法だ:

<?php
$user = getUserById($id);

if ($user !== null) {
    echo $user->getName();
} else {
    echo 'User not found';
}

PHP 8.0以降を使用しているなら、nullsafe演算子で1行にまとめられる:

<?php
// $userがnullの場合、getName()は呼び出されずnullを返す
$name = $user?->getName();
echo $name ?? 'User not found';

nullsafe演算子はチェーン全体をショートサーキットする。オブジェクトがなければ、メソッドも呼ばれず、致命的エラーも起きない。

修正2:nullを返すクエリやデータソースを修正する

nullチェックは往々にして応急処置にすぎない。本質的な問いは「なぜデータソースが何も返さないのか」だ:

<?php
// 悪い例: findById()はIDが存在しない場合nullを返す
$user = User::findById($id);
$user->getName(); // $idが0、空文字、または削除済みレコードなら爆発する

// オプションA: findOrFail()はnullを返す代わりに適切な例外をスローする
$user = User::findOrFail($id); // 見つからない場合はModelNotFoundExceptionをスロー
$user->getName(); // 安全

// オプションB: デフォルトオブジェクトにフォールバックする
$user = User::findById($id) ?? new GuestUser();
$user->getName();

まずクエリのパラメータを確認しよう。空文字、ゼロ、削除済み行の古いIDが原因のケースがおよそ80%を占める。

修正3:オブジェクトのプロパティを使用前に初期化する

初期化されていないクラスプロパティは静かな罠だ。プロパティは存在している――ただ何かがセットするまでnullを保持しているだけだ:

<?php
class OrderProcessor
{
    private ?Logger $logger = null; // 宣言されているが、インジェクトされていない

    public function process(): void
    {
        $this->logger->log('Processing...'); // loggerがセットされていなければ致命的エラー
    }
}

// 修正A: コンストラクタで依存関係を必須にする
class OrderProcessor
{
    private Logger $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }

    public function process(): void
    {
        $this->logger->log('Processing...');
    }
}

// 修正B: フォールバックで遅延初期化する
public function process(): void
{
    if ($this->logger === null) {
        $this->logger = new NullLogger();
    }
    $this->logger->log('Processing...');
}

修正4:制御できない関数のnull戻り値を処理する

標準ライブラリの関数は、想像以上に多くの人がここで痛い目を見る。json_decode()はその典型例だ:

<?php
// json_decode()はパース失敗時(不正なJSON、空文字など)にnullを返す
$data = json_decode($response);
$data->status; // $responseが不正なJSONなら致命的エラー

// 修正: 結果を使う前にjson_last_error()を確認する
$data = json_decode($response);
if (json_last_error() !== JSON_ERROR_NONE || $data === null) {
    throw new \RuntimeException('Invalid JSON: ' . json_last_error_msg());
}
echo $data->status; // 安全

// PHP 8: preg_match()も正規表現エラー時にnullを返すことがある
$result = preg_match('/pattern/', $subject, $matches);
if ($result === false || $result === null) {
    // 正規表現エラーを処理する
}

修正5:依存性注入・サービスコンテナの問題

LaravelやSymfonyでは、コンテナバインドの欠如がまさにこのエラーを引き起こす。コンストラクタのシグネチャは問題なく見えても、注入されるオブジェクトが登録されていないだけだ:

<?php
// Laravelのコントローラ
public function __construct(private UserRepository $repo)
{
    // UserRepositoryにバインドがなければ$repoはnull
}

// AppServiceProviderで登録する:
// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->bind(UserRepository::class, EloquentUserRepository::class);
}

サービスバインドを変更した後は、キャッシュされたコンテナをクリアすること。古いキャッシュが残っていると、以前の(壊れた)設定が有効なままになる:

php artisan clear-compiled
php artisan optimize:clear

予防策

このエラーをコードベースから根絶するための4つの習慣:

  • 厳格な戻り値の型指定。 nullが有効な戻り値でない場合は: ?Userではなく: Userを宣言する。PHPは5回のコール下流での紛らわしい致命的エラーではなく、発生源でTypeErrorをスローしてくれる。
  • strict_types=1。 すべてのファイルの先頭にdeclare(strict_types=1);を追加する。型の不一致が積み重なる前に、早い段階で表面化する。
  • 静的解析。 PHPStanをレベル5以上で使用すれば、1行も実行する前にnull参照を検出できる。Psalmも同様だ。どちらも実行時のオーバーヘッドはない。
  • nullを返すな、例外をスローせよ。 処理を完了できないメソッドは例外をスローすべきだ。サイレントなnullはバグを隠蔽し、例外はバグを露わにする。

修正の確認

まず元の失敗を再現し、それが解消されたことを確認する:

# 1. エラーが発生した正確なシナリオを再現する
php artisan tinker
>>> $user = App\Models\User::find(99999); // 存在しないID
>>> $user?->getName(); // 例外をスローせず、nullを返すはず

# 2. 関連するテストを実行する
php artisan test --filter UserTest

# 3. エラーログがクリーンであることを確認する
tail -f /var/log/php/error.log
# またはLaravelの場合:
tail -f storage/logs/laravel.log

致命的エラーが消え、nullの経路が期待するフォールバック値を返していれば――作業完了だ。

Related Error Notes