PHP Composer 的一個小坑

eddy8發表於2019-08-22

今天遇到一個問題,在一個 Laravel 專案中執行資料庫遷移和填充時,報了一個找不到類定義的錯誤,錯誤提示如下:
Class MenusTableSeeder does not exist
類定義檔案確定是存在的,命名也沒問題。第一感覺是重新生成下 composer 的自動載入檔案。可執行如下命令生成:
composer dump-autoload
常規情況到這一般都能解決問題,網上能搜到的相關主題也基本是這個辦法可以解決此類問題。但這個專案還是報這個類不存在的錯誤。懷疑composer引入的檔案是不是哪損壞了,重新 composer install 安裝一遍,還是報錯。此時還有一點比較詭異的是另外一臺機器上執行同樣的操作不會報這個類不存在的錯誤,一切正常。

此時,我懷疑是 composer 的 bug 。。。

又 google 了下,沒看到相關問題的描述。沒辦法,只有去看看 composer 的原始碼是怎麼生成的自動載入相關檔案。經過一番debug,找到了關鍵程式碼所在,有興趣的可以去看看:https://github.com/composer/composer/blob/...

在這個 findClasses 函式中,有這麼一個去除程式碼中 heredoc 或 nowdoc 的操作:

// strip heredocs/nowdocs
$contents = preg_replace('{<<<[ \t]*([\'"]?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)(?:\s*)\\2(?=\s+|[;,.)])}s', 'null', $contents);

問題就出在這個正則。對於內容比較長的類原始檔,這個正則替換可能會直接報錯返回null,這樣就導致 composer 無法識別出該類,從而你的應用中就會報類不存在的錯誤。

通過呼叫 preg_last_error() 函式可以獲取正則錯誤程式碼。我這裡返回的錯誤程式碼為 2,也就是 PREG_BACKTRACK_LIMIT_ERROR,意思是回溯限制錯誤。這個錯誤受 php 配置影響,可以把 pcre.backtrack_limit 引數設定更大或者直接設定成-1不受限制(可能會造成效能問題,謹慎操作)。這也是前面提到的為什麼有的機器上正常,有的機器上又不正常,原因就在於這個配置不同。

OK,到此弄清楚是什麼原因了。解決辦法:1、可以將 pcre.backtrack_limit 引數設定大點再試試 2、看是否能減小類原始檔中 heredocs/nowdocs 字串的大小。此處我的解決辦法是直接把這個超長 heredocs 字串獨立到sql檔案中,程式碼中讀取檔案內容即可。

最後,這到底算不算 composer 的一個bug呢^_^ 個人覺得這裡的程式碼可以寫的更完善些,對 preg_replace 的返回值進行錯誤判斷,對於異常情況可以在此直接丟擲異常,讓使用者很清晰的知道是什麼原因出錯了,進而迅速解決問題。而不是最終這個類找不到定義,又不知道是哪出問題了。。。

相關文章