驗證碼是 Web 表單防機器人提交的基礎防線。本系列教學從零開始,帶你理解隨機碼生成策略、GD 圖形庫核心函數,並一步步實作完整的圖形驗證碼系統。本篇(上)聚焦隨機碼生成與 GD 基礎。
核心函數:rand(int $min, int $max) 或 mt_rand()(更快、隨機性更好)。
$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$code = '';
for ($i = 0; $i < 4; $i++) {
$code .= $chars[mt_rand(0, strlen($chars) - 1)];
}
// 結果:如 "A7K2"
$code = '';
for ($i = 0; $i < 4; $i++) {
$type = mt_rand(0, 2);
switch ($type) {
case 0: $ascii = mt_rand(48, 57); break; // 0-9
case 1: $ascii = mt_rand(65, 90); break; // A-Z
case 2: $ascii = mt_rand(97, 122); break; // a-z
}
$code .= chr($ascii);
}
$code = '';
for ($i = 0; $i < 4; $i++) {
$code .= dechex(mt_rand(0, 15)); // 0-9, a-f
}
$chinese = ['我', '愛', '中', '國', '程', '式', '開', '發'];
$code = '';
for ($i = 0; $i < 4; $i++) {
$code .= $chinese[mt_rand(0, count($chinese) - 1)];
}
// 需配合 iconv 轉 UTF-8 給 imagettftext 使用
$code = iconv('GB2312', 'UTF-8', $code); // 若原檔為 GB2312
class CaptchaGenerator {
public static function generate($length = 4, $type = 'alphanum') {
$pools = [
'num' => '0123456789',
'alpha' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'alphanum' => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'mixed' => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
'hex' => '0123456789abcdef',
];
$pool = $pools[$type] ?? $pools['alphanum'];
$code = '';
$max = strlen($pool) - 1;
for ($i = 0; $i < $length; $i++) {
$code .= $pool[mt_rand(0, $max)];
}
return $code;
}
}
// 使用
$code = CaptchaGenerator::generate(4, 'alphanum');
; php.ini
extension=gd2 ; PHP 7.2+ 內建 GD,無需 dll
; extension=php_gd2.dll ; PHP 5.x Windows
驗證:php -m | grep gd 或 phpinfo() 查看 GD 區塊。
| 函數 | 用途 | 關鍵參數 |
|---|---|---|
| imagecreatetruecolor() | 建立真彩色畫布 | 寬、高(像素) |
| imagecolorallocate() | 分配顏色(回傳顏色標識) | 畫布資源、R、G、B (0-255) |
| imagefill() | 填充背景 | 畫布、起始座標、顏色 |
| imagestring() | 繪製內建字體字串 | 畫布、字體(1-5)、x、y、字串、顏色 |
| imagettftext() | 繪製 TrueType 字體字串 | 畫布、大小、角度、x、y、顏色、字體檔、字串 |
| imageline() | 繪製直線 | 畫布、x1、y1、x2、y2、顏色 |
| imagesetpixel() | 繪製單一像素點 | 畫布、x、y、顏色 |
| imagejpeg/png/gif() | 輸出圖片到瀏覽器/檔案 | 畫布、[檔案路徑]、[品質] |
| imagedestroy() | 釋放畫布記憶體 | 畫布資源 |
// 輸出 JPEG
header('Content-Type: image/jpeg');
imagejpeg($im);
// 輸出 PNG(支援透明)
header('Content-Type: image/png');
imagepng($im);
// 輸出 GIF
header('Content-Type: image/gif');
imagegif($im);
<?php
session_start();
// 1. 生成 4 位隨機碼
$code = '';
for ($i = 0; $i < 4; $i++) {
$code .= mt_rand(0, 9);
}
$_SESSION['captcha'] = $code; // 存入 Session 驗證用
// 2. 建立畫布 80x30
$im = imagecreatetruecolor(80, 30);
// 3. 分配顏色
$bg = imagecolorallocate($im, 240, 240, 240); // 淺灰背景
$fg = imagecolorallocate($im, 50, 50, 50); // 深灰文字
$noise = imagecolorallocate($im, 180, 180, 180); // 干擾色
// 4. 填充背景
imagefill($im, 0, 0, $bg);
// 5. 繪製干擾點
for ($i = 0; $i < 50; $i++) {
imagesetpixel($im, mt_rand(0, 79), mt_rand(0, 29), $noise);
}
// 6. 繪製干擾線
for ($i = 0; $i < 3; $i++) {
imageline($im,
mt_rand(0, 79), mt_rand(0, 29),
mt_rand(0, 79), mt_rand(0, 29),
$noise
);
}
// 7. 繪製驗證碼文字(內建字體,大小 5)
imagestring($im, 5, 15, 5, $code, $fg);
// 8. 輸出
header('Content-Type: image/jpeg');
imagejpeg($im);
imagedestroy($im);
<form action="login.php" method="post">
<img src="captcha.php" id="captchaImg" alt="驗證碼" style="vertical-align:middle; cursor:pointer"
onclick="this.src='captcha.php?'+Math.random()" title="點擊更換">
<input type="text" name="captcha" maxlength="4" required>
<button type="submit">登入</button>
</form>
| 問題 | 原因 | 解決/優化 |
|---|---|---|
| 圖片顯示「無法顯示」/ 破圖 | 腳本有輸出(空白、BOM、錯誤訊息) | ob_clean()、檔案無 BOM、錯誤導向日誌 |
| 字體顯示方塊/亂碼 | imagettftext 字體路徑錯誤/編碼不符 | 絕對路徑、iconv 轉 UTF-8、字體檔存在 |
| 驗證碼太易被識別 | 干擾不足、字體固定、無扭曲 | 增加干擾線/點、使用 TTF 字體、隨機旋轉/縮放 |
| Session 驗證失效 | Session 未啟動/域名不匹配/過期 | session_start() 最前、檢查 cookie 域、延長 gc_maxlifetime |
下一篇將深入 **TTF 字體繪製、文字扭曲變形、進階干擾模式、Session 驗證邏輯封裝**,並封裝成可複用的 Captcha 類,敬請期待!