_code() 放入 global_func.php,引入後調用:顯示紅叉(圖片損壞)包含文件 global_func.php 若以 UTF-8 with BOM 編碼保存,BOM 字符(EF BB BF)會在 header('Content-Type: image/png') 之前被輸出,導致:
解決:所有 PHP 文件統一用 UTF-8 without BOM 保存(VS Code: 右下角編碼選 "UTF-8" 而非 "UTF-8 with BOM")。
<?php
// global_func.php
// ❌ 錯誤:文件開頭有空行/空格/HTML
?>
<?php
function _code() { ... }
// ✅ 正確:純 PHP 文件無關閉標籤,無任何輸出
function _code() { ... }
檢查 include/require 路徑前後是否有 echo、var_dump、錯誤警告、空白字符。
<?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('驗證碼錯誤');
}
// 繼續業務邏輯
?>header('Content-Type: image/png') 之前 零輸出php -m | grep gd)session_start() 在輸出前調用display_errors=Off)header('Content-Type: image/svg+xml')遵循「單一入口、無 BOM、零輸出、一次性驗證」原則,驗證碼紅叉問題可徹底根治。