PHP生成Gif图片验证码
验证码在好多地方都要用到,这里提供一个新开发的安全验证码类,支持生成Gif图片验证码(带噪点,干扰线,网格,随机色背景,随机自定义字体,倾斜,Gif动画)。从中还可以学到如何用PHP生成Gif图片的方法。
先看效果图
字体及字体文件的路径需要在类中$FontFilePath及$FontFileName中设置。如:
private static $FontFilePath = "static/font/"; //相对地本代码文件的位置 private static $FontFileName = array("3.ttf");// array("1.ttf", "2.ttf", "3.ttf", "4.ttf", "5.ttf", "6.ttf", "7.ttf", "8.ttf"); //
完整代码如下:
<?PHP/** 说明: 验证码生成类,支持生成Gif图片验证码(带噪点,干扰线,网格,随机色背景,随机自定义字体,倾斜,Gif动画)
服务端: $mod = strtolower(isset($_REQUEST["mod"]) ? $_REQUEST["mod"] : ""); if($mod == "code"){ echo SecurityCode::Draw(4, 1, 120, 30, 5, 10, 100, "secode"); die(); } 调用: <img src="/getcode.php?mod=code" onclick="this.src='/getcode.php?mod=code&r='+Math.round(Math.random(0)*1000)"> 验证: $reqCode = strtolower(isset($_REQUEST["secode"]) ? $_REQUEST["secode"] : ""); //请求的验证码 $sessionCode = strtolower(isset($_SESSION["secode"]) ? $_SESSION["secode"] : ""); //会话生成的验证码 if($reqCode != $sessionCode){ echo "安全验证码错误!"; die(); } */ $mod = strtolower(isset($_REQUEST["mod"]) ? $_REQUEST["mod"] : ""); if ($mod == "code") { echo SecurityCode::Draw(4, 15, 100, 27, 10, 2, 100, "secode"); die(); }
//安全验证码类 class SecurityCode {
private static $Debug = 0; private static $Code = ''; private static $Chars = 'bcdefhkmnrstuvwxyABCDEFGHKMNPRSTUVWXY34568'; //private static $Chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890'; private static $TextGap = 20; private static $TextMargin = 5; private static $FontFilePath = "static/font/"; //相对地本代码文件的位置 private static $FontFileName =array("3.ttf");// array("1.ttf", "2.ttf", "3.ttf", "4.ttf", "5.ttf", "6.ttf", "7.ttf", "8.ttf"); // private static $Img = 'GIF89a'; //GIF header 6 bytes private static $BUF = Array(); private static $LOP = 0; private static $DIS = 2; private static $COL = -1; private static $IMG = -1;
/** 生成GIF图片验证 @param int $L 验证码长度 @param int $F 生成Gif图的帧数 @param int $W 宽度 @param int $H 高度 @param int $MixCnt 干扰线数 @param int $lineGap 网格线间隔 @param int $noisyCnt 澡点数 @param int $sessionName 验证码Session名称 */ public static function Draw($L = 4, $F = 1, $W = 150, $H = 30, $MixCnt = 2, $lineGap = 0, $noisyCnt = 10, $sessionName = "Code") { ob_start(); ob_clean();
for ($i = 0; $i < $L; $i++) { self::$Code .= SubStr(self::$Chars, mt_rand(0, strlen(self::$Chars) - 1), 1); }
if (!isset($_SESSION)) session_start(); $_SESSION[$sessionName] = strtolower(self::$Code);
$bgRGB = array(rand(0, 255), rand(0, 255), rand(0, 255)); //生成一个多帧的GIF动画 for ($i = 0; $i < $F; $i++) { $img = ImageCreate($W, $H);
//背景色 $bgColor = imagecolorallocate($img, $bgRGB[0], $bgRGB[1], $bgRGB[2]); ImageColorTransparent($img, $bgColor); unset($bgColor);
//添加噪点 $maxNoisy = rand(0, $noisyCnt); $noisyColor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255)); for ($k = 0; $k <= $maxNoisy; $k++) { imagesetpixel($img, rand(0, $W), rand(0, $H), $noisyColor); }
//添加网格 if ($lineGap > 0) { for ($m = 0; $m < ($W / $lineGap); $m++) { //竖线 imageline($img, $m * $lineGap, 0, $m * $lineGap, $H, $noisyColor); } for ($n = 0; $n < ($H / $lineGap); $n++) { //横线 imageline($img, 0, $n * $lineGap, $W, $n * $lineGap, $noisyColor); } } unset($noisyColor);
// 添加干扰线 for ($k = 0; $k < $MixCnt; $k++) { $wr = mt_rand(0, $W); $hr = mt_rand(0, $W); $lineColor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255)); imagearc($img, $W - floor($wr / 2), floor($hr / 2), $wr, $hr, rand(90, 180), rand(180, 270), $lineColor); unset($lineColor); unset($wr, $hr); }
//第一帧忽略文字 if ($i != 0 || $F <= 1) { //文字 $foreColor = imagecolorallocate($img, rand(0, 255), rand(0, 255), rand(0, 255)); for ($j = 0; $j < $L; $j++) { $fontFile = self::$FontFilePath . self::$FontFileName[rand(0, count(self::$FontFileName) - 1)]; if (!file_exists($fontFile)) imagestring($img, 4, self::$TextMargin + $j * self::$TextGap, ($H - rand($H / 2, $H)), self::$Code[$j], $foreColor); else imageTTFtext($img, rand(15, 18), rand(-15, 15), self::$TextMargin + $j * self::$TextGap, ($H - rand(7, 10)), $foreColor, $fontFile, self::$Code[$j]); } unset($foreColor); }
ImageGif($img); Imagedestroy($img); $Imdata[] = ob_get_contents(); OB_clean(); }
unset($W, $H, $B); if (self::$Debug) { echo $_SESSION['code']; echo '<pre>', Var_Dump($Imdata), '</pre>'; die(); } header('Content-type:image/gif'); return self::CreateGif($Imdata, 20); unset($Imdata); }
private static function CreateGif($GIF_src, $GIF_dly = 10, $GIF_lop = 0, $GIF_dis = 0, $GIF_red = 0, $GIF_grn = 0, $GIF_blu = 0, $GIF_mod = 'bin') { if (!is_array($GIF_src) && !is_array($GIF_tim)) { throw New Exception('Error:' . __LINE__ . ',Does not supported function for only one image!!'); die(); } self::$LOP = ($GIF_lop > -1) ? $GIF_lop : 0; self::$DIS = ($GIF_dis > -1) ? (($GIF_dis < 3) ? $GIF_dis : 3) : 2; self::$COL = ($GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1) ? ($GIF_red | ($GIF_grn << 8) | ($GIF_blu << 16)) : -1; for ($i = 0, $src_count = count($GIF_src); $i < $src_count; $i++) { if (strToLower($GIF_mod) == 'url') { self::$BUF[] = fread(fopen($GIF_src[$i], 'rb'), filesize($GIF_src[$i])); } elseif (strToLower($GIF_mod) == 'bin') { self::$BUF[] = $GIF_src[$i]; } else { throw New Exception('Error:' . __LINE__ . ',Unintelligible flag (' . $GIF_mod . ')!'); die(); } if (!(Substr(self::$BUF[$i], 0, 6) == 'GIF87a' Or Substr(self::$BUF[$i], 0, 6) == 'GIF89a')) { throw New Exception('Error:' . __LINE__ . ',Source ' . $i . ' is not a GIF image!'); die(); } for ($j = (13 + 3 * (2 << (ord(self::$BUF[$i]{10}) & 0x07))), $k = TRUE; $k; $j++) { switch (self::$BUF[$i]{$j}) { case '!': if ((substr(self::$BUF[$i], ($j + 3), 8)) == 'NETSCAPE') { throw New Exception('Error:' . __LINE__ . ',Could not make animation from animated GIF source (' . ($i + 1) . ')!'); die(); } break; case ';': $k = FALSE; break; } } } self::AddHeader(); for ($i = 0, $count_buf = count(self::$BUF); $i < $count_buf; $i++) { self::AddFrames($i, $GIF_dly); } self::$Img .= ';'; return (self::$Img); }
private static function AddHeader() { $i = 0; if (ord(self::$BUF[0]{10}) & 0x80) { $i = 3 * (2 << (ord(self::$BUF[0]{10}) & 0x07)); self::$Img .= substr(self::$BUF[0], 6, 7); self::$Img .= substr(self::$BUF[0], 13, $i); self::$Img .= "!\377\13NETSCAPE2.0\3\1" . chr(self::$LOP & 0xFF) . chr((self::$LOP >> 8) & 0xFF) . "\0"; } unset($i); }
private static function AddFrames($i, $d) { $L_str = 13 + 3 * (2 << (ord(self::$BUF[$i]{10}) & 0x07)); $L_end = strlen(self::$BUF[$i]) - $L_str - 1; $L_tmp = substr(self::$BUF[$i], $L_str, $L_end); $G_len = 2 << (ord(self::$BUF[0]{10}) & 0x07); $L_len = 2 << (ord(self::$BUF[$i]{10}) & 0x07); $G_rgb = substr(self::$BUF[0], 13, 3 * (2 << (ord(self::$BUF[0]{10}) & 0x07))); $L_rgb = substr(self::$BUF[$i], 13, 3 * (2 << (ord(self::$BUF[$i]{10}) & 0x07))); $L_ext = "!\xF9\x04" . chr((self::$DIS << 2) + 0) . chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . "\x0\x0"; if (self::$COL > -1 && ord(self::$BUF[$i]{10}) & 0x80) { for ($j = 0; $j < (2 << (ord(self::$BUF[$i]{10}) & 0x07)); $j++) { if (ord($L_rgb{3 * $j + 0}) == (self::$COL >> 0) & 0xFF && ord($L_rgb{3 * $j + 1}) == (self::$COL >> 8) & 0xFF && ord($L_rgb{3 * $j + 2}) == (self::$COL >> 16) & 0xFF) { $L_ext = "!\xF9\x04" . chr((self::$DIS << 2) + 1) . chr(($d >> 0) & 0xFF) . chr(($d >> 8) & 0xFF) . chr($j) . "\x0"; break; } } } switch ($L_tmp{0}) { case '!': $L_img = substr($L_tmp, 8, 10); $L_tmp = substr($L_tmp, 18, strlen($L_tmp) - 18); break; case ',': $L_img = substr($L_tmp, 0, 10); $L_tmp = substr($L_tmp, 10, strlen($L_tmp) - 10); break; } if (ord(self::$BUF[$i]{10}) & 0x80 && self::$IMG > -1) { if ($G_len == $L_len) { if (self::Compare($G_rgb, $L_rgb, $G_len)) { self::$Img .= ($L_ext . $L_img . $L_tmp); } else { $byte = ord($L_img{9}); $byte |= 0x80; $byte &= 0xF8; $byte |= (ord(self::$BUF[0]{10}) & 0x07); $L_img{9} = chr($byte); self::$Img .= ($L_ext . $L_img . $L_rgb . $L_tmp); } } else { $byte = ord($L_img{9}); $byte |= 0x80; $byte &= 0xF8; $byte |= (ord(self::$BUF[$i]{10}) & 0x07); $L_img{9} = chr($byte); self::$Img .= ($L_ext . $L_img . $L_rgb . $L_tmp); } } else { self::$Img .= ($L_ext . $L_img . $L_tmp); } self::$IMG = 1; }
private static function Compare($G_Block, $L_Block, $Len) { for ($i = 0; $i < $Len; $i++) { if ($G_Block{3 * $i + 0} != $L_Block{3 * $i + 0} || $G_Block{3 * $i + 1} != $L_Block{3 * $i + 1} || $G_Block{3 * $i + 2} != $L_Block{3 * $i + 2}) { return (0); } } return (1); }
}
用法在类开头的注释里。