首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 驗證碼圖片無法顯示(紅叉)完整排查指南:BOM、Header、Session 三大核心成因

     編輯:PHP 相關知識

典型現象

  • 直接在文件中寫驗證碼代碼:正常顯示
  • 封裝為函數 _code() 放入 global_func.php,引入後調用:顯示紅叉(圖片損壞)

核心成因分析

1. BOM 頭干擾(最常見)

包含文件 global_func.php 若以 UTF-8 with BOM 編碼保存,BOM 字符(EF BB BF)會在 header('Content-Type: image/png') 之前被輸出,導致:

  • HTTP 響應體以 BOM 開頭,非合法 PNG 文件頭
  • 瀏覽器識別為文本/未知格式,顯示紅叉

解決:所有 PHP 文件統一用 UTF-8 without BOM 保存(VS Code: 右下角編碼選 "UTF-8" 而非 "UTF-8 with BOM")。

2. 輸出緩衝區已有內容

<?php
// global_func.php
// ❌ 錯誤:文件開頭有空行/空格/HTML
?> 
<?php
function _code() { ... }

// ✅ 正確:純 PHP 文件無關閉標籤,無任何輸出
function _code() { ... }

檢查 include/require 路徑前後是否有 echo、var_dump、錯誤警告、空白字符。

3. Session 未啟動或路徑錯誤

<?php
// global_func.php
function _code() {
    // 必須在輸出 Header 前啟動 Session
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    // ... 生成圖片
    header('Content-Type: image/png');
    imagepng($img);
}

標準化驗證碼類實現(最佳實踐)

<?php
// Captcha.php
class Captcha
{
    private int $width = 120;
    private int $height = 40;
    private int $codeLen = 4;
    private string $sessionKey = 'captcha_code';

    public function __construct(array $options = [])
    {
        foreach ($options as $k => $v) {
            if (property_exists($this, $k)) $this->$k = $v;
        }
    }

    public function generate(): void
    {
        // 1. 啟動 Session(若未啟動)
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        // 2. 生成隨機碼
        $code = '';
        for ($i = 0; $i < $this->codeLen; $i++) {
            $code .= dechex(mt_rand(0, 15)); // 0-9 a-f
        }
        $_SESSION[$this->sessionKey] = strtolower($code);

        // 3. 創建畫布
        $img = imagecreatetruecolor($this->width, $this->height);
        $white = imagecolorallocate($img, 255, 255, 255);
        imagefill($img, 0, 0, $white);

        // 4. 干擾線
        for ($i = 0; $i < 6; $i++) {
            $color = imagecolorallocate($img, mt_rand(0, 200), mt_rand(0, 200), mt_rand(0, 200));
            imageline($img, mt_rand(0, $this->width), mt_rand(0, $this->height),
                      mt_rand(0, $this->width), mt_rand(0, $this->height), $color);
        }

        // 5. 干擾點
        for ($i = 0; $i < 50; $i++) {
            $color = imagecolorallocate($img, mt_rand(150, 255), mt_rand(150, 255), mt_rand(150, 255));
            imagesetpixel($img, mt_rand(0, $this->width), mt_rand(0, $this->height), $color);
        }

        // 6. 繪製驗證碼
        for ($i = 0; $i < $this->codeLen; $i++) {
            $color = imagecolorallocate($img, mt_rand(0, 100), mt_rand(0, 150), mt_rand(0, 200));
            $x = $i * ($this->width / $this->codeLen) + mt_rand(5, 15);
            $y = mt_rand(5, $this->height / 2);
            imagestring($img, 5, $x, $y, $code[$i], $color);
        }

        // 7. 輸出圖片(關鍵:確保之前無任何輸出)
        header('Content-Type: image/png');
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Pragma: no-cache');
        imagepng($img);
        imagedestroy($img);
    }

    public static function verify(string $input, string $sessionKey = 'captcha_code'): bool
    {
        if (session_status() === PHP_SESSION_NONE) session_start();
        $stored = $_SESSION[$sessionKey] ?? '';
        unset($_SESSION[$sessionKey]); // 一次性驗證
        return strtolower(trim($input)) === strtolower($stored);
    }
}

使用方式

<?php
// captcha.php - 獨立入口
require_once 'Captcha.php';
(new Captcha())->generate();
<!-- HTML 頁面 -->
<img src="captcha.php?t=" id="captchaImg" style="cursor:pointer" title="點擊刷新">
<input type="text" name="captcha" placeholder="輸入驗證碼">

<script>
document.getElementById('captchaImg').onclick = function() {
    this.src = 'captcha.php?t=' + Date.now();
};
</script>
<?php
// 表單驗證
require_once 'Captcha.php';
if (!Captcha::verify($_POST['captcha'] ?? '')) {
    die('驗證碼錯誤');
}
// 繼續業務邏輯

快速排查清單

  1. ☑ 所有 PHP 文件為 UTF-8 無 BOM
  2. ☑ 驗證碼入口文件 純 PHP,無 HTML/空白/關閉標籤 ?>
  3. ☑ header('Content-Type: image/png') 之前 零輸出
  4. ☑ GD 擴展已啟用(php -m | grep gd)
  5. ☑ session_start() 在輸出前調用
  6. ☑ 錯誤報告級別不向瀏覽器輸出(生產環境 display_errors=Off)

進階替代方案

  • Google reCAPTCHA / hCaptcha:無需維護圖片生成,防機器人更強
  • SVG 驗證碼:純文本輸出,無 GD 依賴,header('Content-Type: image/svg+xml')
  • 算術/點選/滑塊:前端生成,後端驗證邏輯分離

遵循「單一入口、無 BOM、零輸出、一次性驗證」原則,驗證碼紅叉問題可徹底根治。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved