在 ThinkPHP 開發中,驗證碼(`Image::buildImageVerify()`)無法顯示、僅出現「小 X」或圖片損壞圖示,是開發者常遇到的棘手問題。官方文檔列舉了四大檢查項(GD庫、輸出前輸出、類庫載入、字體檔案),但實務上最常見卻最容易被忽略的原因是:驗證碼輸出前已有內容輸出(含 BOM、空白、echo、var_dump、錯誤訊息等),導致 HTTP 標頭已送出,圖片二進位資料被污染。本文提供徹底解決方案與原理解析。
PHP 輸出圖片時,必須確保:
若在呼叫 `Image::buildImageVerify()` 之前,任何地方(框架核心、配置檔、控制器建構子、模型、視圖、甚至 `php.ini` 設定的 `auto_prepend_file`)有輸出,瀏覽器會將其視為圖片資料一部分,導致圖片解析失敗。
在驗證碼生成方法的最開頭呼叫 `ob_end_clean()`,丟棄所有已產生的輸出緩衝區內容,確保乾淨輸出。
<?php
namespace app\controller;
use think\Controller;
use think\Image;
class Auth extends Controller
{
/**
* 顯示驗證碼
* 路由:GET /auth/captcha
*/
public function captcha()
{
// 關鍵:清除所有先前的輸出緩衝區
// ob_end_clean() 會關閉並丟棄「最內層」的緩衝區
// 若有多層巢狀 ob_start(),需呼叫多次或用 ob_get_level() 迴圈
while (ob_get_level() > 0) {
ob_end_clean();
}
// 現在可以安全輸出圖片標頭與二進位資料
Image::buildImageVerify([
'length' => 4,
'fontSize' => 20,
'width' => 120,
'height' => 40,
'useNoise' => true,
'fontttf' => 'path/to/your/font.ttf', // 確保字體檔案存在
]);
}
}
<?php
namespace app\traits;
trait CaptchaHelper
{
/**
* 確保乾淨輸出環境並生成驗證碼
* @param array $config Image::buildImageVerify 設定
*/
protected function outputCaptcha(array $config = []): void
{
// 1. 清除所有輸出緩衝區
while (ob_get_level() > 0) {
ob_end_clean();
}
// 2. 發送正確標頭(Image 類別內部會發送,但可在此明確設定)
header('Content-Type: image/jpeg');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
// 3. 生成驗證碼
\think\Image::buildImageVerify($config);
exit; // 確保無後續輸出
}
}
// 控制器使用
namespace app\controller;
use think\Controller;
use app\traits\CaptchaHelper;
class Auth extends Controller
{
use CaptchaHelper;
public function captcha()
{
$this->outputCaptcha([
'length' => 4,
'width' => 130,
'height' => 50,
]);
}
}
| 檢查項 | 實際排查步驟 |
|---|---|
| ① GD 庫支援 | `php -m | grep gd` 或 `phpinfo()` 確認 GD Support => enabled、FreeType Support => enabled、JPEG/PNG Support => enabled |
| ② 輸出前無輸出 | 檢查所有 `.php` 檔案無 BOM(用編輯器存為 UTF-8 無 BOM)、無結尾 `?>` 後的空白行、無 `echo`/`var_dump`/`print_r`、無錯誤/警告輸出(設定 `display_errors=Off` 生產環境) |
| ③ Image 類庫載入 | ThinkPHP 5/6 通常自動載入,若手動 `require` 確認路徑正確;命名空間 `use think\Image;` |
| ④ 中文驗證碼字體 | 中文驗證碼需 TTF 字體檔,確認 `fontttf` 參數路徑正確、檔案可讀、字體支援所需字元 |
將驗證碼生成獨立為無狀態 API 服務(如 `/api/captcha`),前端 `` 請求,後端純輸出圖片,**不載入框架完整啟動流程**甚至使用獨立微框架(Slim、Lumen)或純 PHP 腳本,徹底避免框架層級的輸出干擾。
ThinkPHP 驗證碼無法顯示,**90% 以上案例源於輸出緩衝區污染**。徹底解決之道:在驗證碼生成入口**最優先**呼叫 `while (ob_get_level()) ob_end_clean();` 確保乾淨環境,並配合 `exit` 結束腳本。搭配官方四大檢查項(GD、BOM、類庫、字體),即可永久根除「小 X」問題。建議將此邏輯封裝為 Trait 或基底控制器方法,專案統一複用,避免重複踩坑。