｢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\Client`を`new`するコードを書いて、

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

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

```console:実行結果
❯ 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を読み込むのを忘れているだけなので、読み込むようにします。

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

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

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

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

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

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

```php:src/Application.php
<?php

namespace MyApp;

class Application
{
}
```

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

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

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

```console:実行結果
❯ 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ディレクティブが追加されているか確認しましょう。

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

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

```console
❯ composer dumpautoload
Generating autoload files
```

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

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

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

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

```console
❯ composer dumpautoload
Generating autoload files
```

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

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

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

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

```php: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'),
);
```

## スペルミスは無いか？

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

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

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

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

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

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

<img width="571" alt="スクリーンショット_2017-09-19_17_47_29.png" src="https://qiita-image-store.s3.amazonaws.com/0/889/34fb428b-878e-f94d-7c09-5a7a346fcdf4.png">



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

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

* `pwd`でPHPを実行している場所が想定している場所か確認しよう。
* `ls`でクラスファイルがあるか確認しよう。
* `cat`や`less`で変更がちゃんとファイルシステムに保存されているか確認しよう。

```console:catコマンドで確認する例
❯ cat src/Application.php
<?php

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

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

---

最後までお読みくださりありがとうございました。Twitterでは、Qiitaに書かない技術ネタなどもツイートしているので、よかったらフォローお願いします:relieved:→[Twitter@suin](https://twitter.com/suin)