驗證碼是表單防刷、防機器人提交的第一道防線。本文展示一個完整、符合現代 PHP 規範(PSR-1/12)的 ValidationCode 類別,支援自訂尺寸、字元數、干擾線/點、TrueType 字體渲染,並內建 Session 儲存機制,可直接整合至登入、註冊、留言板等場景。
imagechar)與 TTF 字體(imagettftext)雙模式ValidationCode.php<?php
declare(strict_types=1);
class ValidationCode
{
private int $width;
private int $height;
private int $codeNum;
private $image;
private int $disturbColorNum;
private string $checkCode;
public function __construct(int $width = 80, int $height = 20, int $codeNum = 4)
{
$this->width = $width;
$this->height = $height;
$this->codeNum = $codeNum;
$this->checkCode = $this->createCheckCode();
// 依畫布面積動態計算干擾點上限,避免過度遮蔽
$area = $width * $height;
$this->disturbColorNum = min((int)($area / 15), 240 - $codeNum);
}
/** 公開介面:輸出驗證碼圖片至瀏覽器 */
public function showImage(string $fontFace = ''): void
{
$this->createImage();
$this->setDisturbColor();
$this->outputText($fontFace);
$this->outputImage();
}
/** 公開介面:取得驗證碼字串(供 Session 儲存驗證) */
public function getCheckCode(): string
{
return $this->checkCode;
}
/** 建立真彩色畫布、隨機背景、黑色邊框 */
private function createImage(): void
{
$this->image = imagecreatetruecolor($this->width, $this->height);
$bgColor = imagecolorallocate($this->image, mt_rand(225, 255), mt_rand(225, 255), mt_rand(225, 255));
imagefill($this->image, 0, 0, $bgColor);
$borderColor = imagecolorallocate($this->image, 0, 0, 0);
imagerectangle($this->image, 0, 0, $this->width - 1, $this->height - 1, $borderColor);
}
/** 繪製干擾點與干擾弧線 */
private function setDisturbColor(): void
{
// 干擾像素點
for ($i = 0; $i < $this->disturbColorNum; $i++) {
$color = imagecolorallocate($this->image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
imagesetpixel($this->image, mt_rand(1, $this->width - 2), mt_rand(1, $this->height - 2), $color);
}
// 干擾弧線
for ($i = 0; $i < 10; $i++) {
$color = imagecolorallocate($this->image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(200, 255));
imagearc($this->image, mt_rand(-10, $this->width), mt_rand(-10, $this->height), mt_rand(30, 300), mt_rand(20, 200), 55, 44, $color);
}
}
/** 產生隨機驗證碼字串(排除易混淆字元 0,O,1,l,I 等) */
private function createCheckCode(): string
{
$chars = '23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKMNPQRSTUVWXYZ';
$code = '';
for ($i = 0; $i < $this->codeNum; $i++) {
$code .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $code;
}
/** 將驗證碼字元逐一繪製至畫布 */
private function outputText(string $fontFace = ''): void
{
for ($i = 0; $i < $this->codeNum; $i++) {
$fontColor = imagecolorallocate($this->image, mt_rand(0, 128), mt_rand(0, 128), mt_rand(0, 128));
if ($fontFace === '') {
// 內建字體模式
$fontSize = mt_rand(3, 5);
$x = (int)($this->width / $this->codeNum) * $i + 3;
$y = mt_rand(0, $this->height - 15);
imagechar($this->image, $fontSize, $x, $y, $this->checkCode[$i], $fontColor);
} else {
// TTF 字體模式
$fontSize = mt_rand(12, 16);
$x = (int)(($this->width - 8) / $this->codeNum) * $i + 8;
$y = mt_rand($fontSize + 5, $this->height);
imagettftext($this->image, $fontSize, mt_rand(-30, 30), $x, $y, $fontColor, $fontFace, $this->checkCode[$i]);
}
}
}
/** 偵測 GD 支援格式並輸出圖片 */
private function outputImage(): void
{
$types = imagetypes();
if ($types & IMG_GIF) {
header('Content-Type: image/gif');
imagegif($this->image);
} elseif ($types & IMG_JPG) {
header('Content-Type: image/jpeg');
imagejpeg($this->image);
} elseif ($types & IMG_PNG) {
header('Content-Type: image/png');
imagepng($this->image);
} elseif ($types & IMG_WBMP) {
header('Content-Type: image/vnd.wap.wbmp');
imagewbmp($this->image);
} else {
die('PHP 環境不支援圖片建立功能');
}
}
/** 解構子:釋放記憶體 */
public function __destruct()
{
if (is_resource($this->image)) {
imagedestroy($this->image);
}
}
}
captcha.php<?php
session_start();
require_once 'ValidationCode.php';
$captcha = new ValidationCode(100, 30, 4); // 稍大尺寸,適合行動裝置
$captcha->showImage(__DIR__ . '/fonts/DejaVuSans.ttf'); // 若有 TTF 字體可傳入路徑
$_SESSION['captcha_code'] = $captcha->getCheckCode();
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = strtoupper(trim($_POST['captcha'] ?? ''));
$stored = strtoupper($_SESSION['captcha_code'] ?? '');
if ($input !== $stored) {
die('驗證碼錯誤,請重新輸入。');
}
// 驗證通過,處理業務邏輯...
unset($_SESSION['captcha_code']); // 一次性使用,防重放
}
setBackgroundImages(array $paths) 支援圖片背景setNoiseLevel(int $level) 讓呼叫端控制干擾強度Securimage 或 Gregwar/Captcha 等成熟套件以獲得更強抗辨識能力此類別結構清晰、單一職責、易於測試與擴充,是學習 PHP GD 圖形處理與物件導向封裝的優良教材,亦可直接用於中小型專案的生產環境。