PHP

「Fatal error: Class ‘…’ not found」に陥ったときのチェック5項目

クラスが見つからないエラー「Fatal error: Class ‘○○○’ not found」が発生したとき、その典型的な原因とその対処方法をチェックリスト形式で紹介します。

vendor/autoload.phpをrequireしているか?

composer installで必要なライブラリをインストールして、いざ使おうとしたらFatalになるパターンです。たとえば、guzzlehttp/guzzleをインストールして、

composer require guzzlehttp/guzzle

main.phpでGuzzleHttp\Clientnewするコードを書いて、

main.php
<?php
$client = new GuzzleHttp\Client();

それを実行してみると、「Uncaught Error: Class 'GuzzleHttp\Client' not found」と言われます。

実行結果
❯ php main.php
PHP Fatal error:  Uncaught Error: Class 'GuzzleHttp\Client' not found in /Users/suin/Desktop/myapp/main.php:2
Stack trace:
#0 {main}
  thrown in /Users/suin/Desktop/myapp/main.php on line 2

Fatal error: Uncaught Error: Class 'GuzzleHttp\Client' not found in /Users/suin/Desktop/myapp/main.php:2
Stack trace:
#0 {main}
  thrown in /Users/suin/Desktop/myapp/main.php on line 2

対処法: vendor/autoload.phpをrequireする

この場合、単純にvendor/autoload.phpを読み込むのを忘れているだけなので、読み込むようにします。

main.php
<?php
require 'vendor/autoload.php'; // この行を追加
$client = new GuzzleHttp\Client();

composer.jsonにautoloadの定義を忘れていないか?

プロジェクトによってはsrcディレクトリにプロジェクトのクラスを格納して、そのパッケージを使うことがあります。そうしたパッケージを名前解決するには、composer.jsonにautoloadディレクティブを宣言しないといけません。

たとえば次のようなautoloadディレクティブの無い状態で、

composer.json
{
    "name": "suin/myapp",
    "require": {
        "guzzlehttp/guzzle": "^6.3"
    }
}

次のプロジェクト独自クラスを、

src/Application.php
<?php

namespace MyApp;

class Application
{
}

main.phpなどのクライアントコードで使おうとしても、

main.php
<?php
require 'vendor/autoload.php';
$app = new MyApp\Application();

Composerのクラスローダーはクラスを見つけることができず、Fatalエラーになります。

実行結果
❯ php main.php
PHP Fatal error:  Uncaught Error: Class 'MyApp\Application' not found in /Users/suin/Desktop/myapp/main.php:3
Stack trace:
#0 {main}
  thrown in /Users/suin/Desktop/myapp/main.php on line 3

Fatal error: Uncaught Error: Class 'MyApp\Application' not found in /Users/suin/Desktop/myapp/main.php:3
Stack trace:
#0 {main}
  thrown in /Users/suin/Desktop/myapp/main.php on line 3

対処法: composer.jsonにautoloadディレクティブを追加する

composer.jsonにautoloadディレクティブが追加されているか確認しましょう。

composer.json
{
    "name": "suin/myapp",
    "require": {
        "guzzlehttp/guzzle": "^6.3"
    },
    // ここ↓
    "autoload": {
        "psr-4": {
            "MyApp\\": "src/"
        }
    }
}

そして、それを追加したらcomposer dumpautoloadを実行して、autoload.php関連のファイルを再生成しましょう。

❯ composer dumpautoload
Generating autoload files

vendor/composer/autoload_*.phpの内容が最新になっているか?

チームで開発していてvendorディレクトリをgitignore等でバージョン管理の管理外にしていると、クラスローダーマップが最新でなくなっていることがあります。もし、vendorの中身をバージョン管理しているなら、このチェックは飛ばして構いません。

対処法: composer dumpautoloadでクラスローダーのマップを最新にする

composer dumpautoloadでvendor/composer/autoload_*.phpの内容を最新にしましょう。

❯ composer dumpautoload
Generating autoload files

ちなみに、composer.jsonのautoloadディレクティブでpsr-0やpsr-4などのローディング方法を設定できますが、ここで設定した内容はcomposer dumpautoloadをしてはじめて、vendor/composer/autoload_*.phpのファイル群に反映されます。

たとえば、次のようなcomposer.jsonに対して、

composer.json
{
    "name": "suin/myapp",
    "require": {
        "guzzlehttp/guzzle": "^6.3"
    },
    "autoload": {
        "psr-4": {
            "MyApp\\": "src/"
        }
    }
}

composer dumpautoloadを実行すると、次のようなマップ情報を生成してくれます。'MyApp\\' => array($baseDir . '/src')が宣言されているところに注目してください。これはMyAppパッケージのクラス等をsrcディレクトリの中にあるよ、というマップ情報になっています。

vendor/composer/autoload_psr4.php
<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
    'MyApp\\' => array($baseDir . '/src'),
    'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
    'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
    'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
);

スペルミスは無いか?

ありがちなのはクラス名やネームスペースのスペルミスです。

対処法: スペルミスがないかひとつひとつ確認する

  • 大文字小文字は合っているか?
  • 単数形複数形の間違いはないか?
  • タイプミスがないか?
  • クラス名とファイル名は同じか?
  • パッケージ名とネームスペース名は同じか?

目視確認するだけでなく、クラス名をクラスの定義からクライアントコードにコピペする、クラス定義からクラスファイル名にコピペするなどを繰り返していって確実に間違っていない状況を作って確認してください。目視だけだと見落としがありえます。

いま作業しているディレクトリは実行環境のものか?

今あなたが変更を加えているファイルが、実は実行しているファイルではないかもしれません。変更しているクラスが、実は別プロジェクトの同じ名前のクラスかもしれません。同じプロジェクト内でも、別パッケージの同名クラスかもしれません。タグエディタにはパッケージやパスが隠れているものもあるので、この手のミスはなかなか気づけません。

スクリーンショット_2017-09-19_17_47_29.png

対処法: エディタとは別の方法(CLI)でダブルチェックする

エディタ上だけで見ていると「全然別のファイル変更してたわー」ということに気がつくまで時間がかかったりするので、まずはファイルシステムにあなたの変更がちゃんと反映されているかを、コマンドラインで二重の確認しましょう。

  • pwdでPHPを実行している場所が想定している場所か確認しよう。
  • lsでクラスファイルがあるか確認しよう。
  • catlessで変更がちゃんとファイルシステムに保存されているか確認しよう。
catコマンドで確認する例
❯ cat src/Application.php
<?php

namespace MyApp; // ネームスペースはあっていますか?

class Application // クラス名はあっていますか?
{
}