出現的問題
公司開發使用PHP,技術框架使用Laravel。最近線上出現一個問題,就是上線之后,每次都會出錯。查看出錯原因,是composer安裝的第三方出現class not found。因為這個問題,在線下使用Lumen框架的時候,遇到過,查找問題原因是因為依賴的composer包中composer.json中的”autoload”:{“psr-4”:{}}書寫格式問題。解決方法使用命令:composer dump-autoload -o;
雖然知道問題的所在,但是有一個現象比較費解:這個第三方包已經使用很久了,為什么最近才開始報錯呢?下面就開始查找出錯原因
解決方案
如果確認第三方包已安裝,并且正確使用use引用了,嘗試執行composer dump-autoload -o
最終結果
因為可能篇幅會比較長,所以這里先說明一下最終問題處理結果:原因還未準確定位到,現推測發布服務器環境問題,但因為發布服務器監控服務較多,不允許進行測試,所以具體環境哪個配置導致的問題,還沒有定位到。
下面主要介紹問題解決過程:
1. 查看laravel autoload 2. 查看composer源碼; 3. 重新編譯composer打印日志; 4. 分析composer install過程; 5. 查看php artisan optimize源碼
對分析查找問題的過程感興趣的同學可以繼續往下看。
問題分析及解決過程
1. 查找class not found原因
分析
既然class not found,確認composer包已經安裝。那問題就確定在autoload過程
查看源碼
首先自動加載入口 public/index.php 中
require __DIR__.'/../bootstrap/autoload.php';
然后繼續進入 bootstrap/autoload.php 文件
require __DIR__.'/../vendor/autoload.php';
然后繼續進入 vendor/autoload.php
// require 自動加載類require_once __DIR__ . '/composer/autoload_real.php';// 真正返回文件列表的操作return ComposerAutoloaderInit3f39d071b2e74e04102a9c9b6f221123::getLoader();
進入getLoader()方法中
public static function getLoader(){ if (null !== self::$loader) { return self::$loader; } // 注冊自動加載方法,用來后面初始化ClassLoader類 spl_autoload_register(array('ComposerAutoloaderInit3f39d071b2e74e04102a9c9b6f221123', 'loadClassLoader'), true, true); // 初始化ClassLoarder self::$loader = $loader = new /Composer/Autoload/ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit3f39d071b2e74e04102a9c9b6f221123', 'loadClassLoader')); // 這里zend_loader_file_encoded查了一下,解釋為: // Returns TRUE if the current file was encoded with Zend Guard or FALSE otherwise. If FALSE, consider disabling the Guard Loader // 又查了一下Zend Guard,貌似是php代碼加密并提高執行效率的,提高有限,比較雞肋 // 打印了一下,發現不存在這個方法,即!function_exists('zend_loader_file_encoded')為true $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { // 程序在這里執行 // 引用ComposerStaticInit類 require_once __DIR__ . '/autoload_static.php'; // 調用ComposerStaticInit類中的getInitializer方法 // 主要作用是使用ComposerStaticInit類中的值初始化上面創建的ComposerAutoloader對象中的prefixLengthsPsr4、prefixDirsPsr4、prefixesPsr0、classMap等值 call_user_func(/Composer/Autoload/ComposerStaticInit3f39d071b2e74e04102a9c9b6f221123::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } } // 重點在這個方法 $loader->register(true); if ($useStaticLoader) { $includeFiles = Composer/Autoload/ComposerStaticInit3f39d071b2e74e04102a9c9b6f221123::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequire3f39d071b2e74e04102a9c9b6f221123($fileIdentifier, $file); } return $loader;}
新聞熱點
疑難解答