page_adsence

2016年2月20日土曜日

composer updateでClass not found

Laravel4.2を利用している環境で、composer updateを行うと、composer updateの後続処理として、
php artisan clear-compiled
php artisan optimize

が実行されるのですが、上記の処理を実行すると、app/config/app.phpに登録している自分で作ったServiceProviderを読み込む時にClass not foundでエラーになるという状態になっていた。
$ composer update
Loading composer repositories with package information                                                                     Updating dependencies (including require-dev)

Writing lock file
Generating autoload files
> php artisan clear-compiled
PHP Fatal error:  Class 'MyAPP\Services\App\AppServiceProvider' not found in /home/user/site/myapp/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php on line 157
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Class 'MyAPP\\Services\\App\\AppServiceProvider' not found","file":"\/home\/user\/site\/myapp\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/ProviderRepository.php","line":157}}Script php artisan clear-compiled handling the post-update-cmd event returned with an error



  [RuntimeException]
  Error Output: PHP Fatal error:  Class 'MyAPP\Services\App\AppServiceProvider' not found in /home/user/site/myapp/vendor/laravel/framework/src/Illuminate
  /Foundation/ProviderRepository.php on line 157



update [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--lock] [--no-plugins] [--no-custom-installers] [--no-autoloader] [--no-scripts] [--no-progress] [--with-dependencies] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--] []...
一度、自分で作ったServiceProviderをコメントアウトして、php artisan dump-autoloadし、コメントアウトを外して再度php artisan dump-autoloadすると問題なく使用出来る様になる。
使用出来る様になる理由は、vendor/composer/autoload_classmap.phpに自分で作ったServiceProviderのパスが記入されるためで、
再度composer updateするとそのファイルを含めたvendor/composer配下は再生成されるため、再びClass not foundになってしまう。

原因はcomposer.jsonに書いていたパスが間違っていたというなんとも言えないミスでした。

ディレクトリ構成は、下記の通りになっています。
/vendor/vendor_name/package_name/src/MyAPP/Service/XXXX/XXXXServiceProvider.php

誤ったcomposer.jsonの記述
"autoload": {
    "psr-4": {
        "MyAPP\\": "src/"
    }
}
正しいcomposer.jsonの記述
"autoload": {
    "psr-4": {
        "MyAPP\\": "src/MyAPP/"
    }
}
このパスが間違っているせいでautoload_psr4.phpの中の処理で引っかからずに、
autoload_classmap.phpにない場合にエラーになってしまっていました。
ちなみにcomposer内のファイル読み込み処理は、下記のファイルで行われている。
PSR-4 lookupと書かれている所の$fileに読み込むファイルのパスがセットされ、ファイルの存在確認をしている。
$ vi vendor/composer/ClassLoader.php

                            〜略〜

private function findFileWithExtension($class, $ext)
{
    // PSR-4 lookup
    $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

    $first = $class[0];
    if (isset($this->prefixLengthsPsr4[$first])) {
        foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
            if (0 === strpos($class, $prefix)) {
                foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-4 fallback dirs
    foreach ($this->fallbackDirsPsr4 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
            return $file;
        }
    }

    // PSR-0 lookup
    if (false !== $pos = strrpos($class, '\\')) {
        // namespaced class name
        $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
            . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
    } else {
        // PEAR-like class name
        $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
    }

    if (isset($this->prefixesPsr0[$first])) {
        foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
            if (0 === strpos($class, $prefix)) {
                foreach ($dirs as $dir) {
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-0 fallback dirs
    foreach ($this->fallbackDirsPsr0 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
            return $file;
        }
    }

    // PSR-0 include paths.
    if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
        return $file;
    }
}
composer.jsonファイルを修正して、composer updateコマンドを打つ。 これでエラーが出なくなりました。