绘制验证码的过程可分为:
- 创建一个指定大小的画板
- 给这画板填充一个背景色
- 开始画你想要的图形
PHP图形处理依赖GD库在开始前
执行打印当前安装的 GD 库的信息
<?php
var_dump(gd_info());
?>
显示的大致与下面差不多,那么我们就可以继续了
array(13) {
["GD Version"]=>string(26)
"bundled (2.1.0 compatible)"
["FreeType Support"]=>bool(true)
["FreeType Linkage"]=>string(13)
"with freetype"
["GIF Read Support"]=>bool(true)
["GIF Create Support"]=>bool(true)
["JPEG Support"]=>bool(true)
["PNG Support"]=>bool(true)
["WBMP Support"]=>bool(true)
["XPM Support"]=>bool(true)
["XBM Support"]=>bool(true)
["WebP Support"]=>bool(true)
["BMP Support"]=>bool(true)
["JIS-mapped Japanese Font Support"]=>bool(false)
}
首先新建一个名为 Captcha.php文件
然后新建一个类 并声明在本类中使用的公共变量
下面的方法都是在Captcha类内,为了方便后期维护,我们把每个功能都封装成一个单独的方法。
为了不让其他人随意调用内部方法以及修改变量值 大部分方法和变量作用域声明为protected(本类和子类中可以访问,类外部不能访问)
<?php
class Captcha
{
protected $height;//画板高度
protected $width;//画板宽度
protected $res;//画板资源
protected $len;//生成随机字符的个数
protected $code;//保存随机生成的字符串
创建画板和填充画板
public function render()
{
//创建画板
$res = imagecreatetruecolor($this->width, $this->height);
//对画板进行填充
imagefill($this->res = $res,0,0,imagecolorallocate($res,222,222,222)
);
这里使用到了
//创建画布并设定大小
imagecreatetruecolor($width, $height);
imagecreatetruecolor(宽度,高度);
//填充画布
imagefill($image, $x, $y, $color);
imagefill(画布,x起始填充位置 ,y起始填充位置,颜色);
//设定颜色 RBG值0-255
imagecolorallocate($image, $red, $green, $blue);
imagecolorallocate(画布,R,G,B);
绘制验证码内容
为了不让一些OCR软件正常识别,一个验证码中通常有很多的干扰点和干扰线如下图
这张验证码中有三个图形
- 点
- 线
- 字符
绘制的时候需要分顺序绘制,这就和画画一样后面画的覆盖前面的,所以把字符放在最下面,也就是需要第一个画他,其他的画在字符之后就行了不讲究顺序。
创建构造函数对画布宽高以及字符个数进行初始化操作
//构造函数 实例化的时候如果没有传值 那么它将会有一个初始值
public function __construct(int $width = 200, int $height = 60, int $len = 5)
{
$this->width = $width;
$this->height = $height;
$this->len = $len;
}
生成随机颜色
由于很多地方用到了随机颜色,所以我们 将他封装成一个方法.
背景偏向于白色,所以我们字符为了方便区别于背景,所以我们将字符的随机颜色设定偏向于黑色
//返回随机颜色
protected function getColor()
{
return imagecolorallocate(
$this->res,
mt_rand(0, 255),
mt_rand(0, 255),
mt_rand(0, 255)
);
}
//随机文本颜色
protected function getTextColor()
{
return imagecolorallocate(
$this->res,
mt_rand(0, 125),
mt_rand(0, 125),
mt_rand(0, 125)
);
}
//生成一个可以等于最小值与最大值之间的数
mt_rand($min = 0, $max = null);
mt_rand(最小值,最大值);
生成随机字符
绘制前我们需要得到随机的字符所以我们先写随机字符生成方法
每调用一次就会返回一个 1-9 || a-z || A-Z 之间的字符
如果你需要更多的字符可以参考ASCII码表进行修改
//随机生成字符
protected function getRandText()
{
while (true) {
//随机asscii码中数字大写字母小写字母对应的十进制 其中包含一些 我们不需要的
$ran = mt_rand(48, 122);
if (
$ran >= 48 && $ran <= 57 || //数字范围
$ran >= 65 && $ran <= 90 || //大写字母范围
$ran >= 97 && $ran <= 122 //小写字母范围
) {
//返回对应字符
return chr($ran);
}
}
}
绘制字符
绘制前要准备一个绘制时使用的字体
protected function text()
{
//指定绘制文本所使用的字体文件 realpath('相对路径') 方法会返回一个绝对路径
$font = realpath('../font/Alibaba-PuHuiTi-Medium.ttf');
//根据画布宽度 决定字体的大小
$fontsize = $this->width / $this->len;
//绘制指定个数的随机字符
for ($i = 0; $i < $this->len; $i++) {
//保存得到的随机字符
$this->code .= $code = $this->getRandText();
//平均分布字符
$x = $this->width / $this->len;
//随机倾斜角度
$angle = mt_rand(-30, 30);
//imagettfbbox得到四个边界的位置 返回一个数组
$box = imagettfbbox($fontsize, $angle, $font, 'A');
//绘制文本
imagettftext(
$this->res,
$fontsize,
$angle,
$x * $i + $this->width * 0.05,
//使文字居中 屏幕高度的一半-文字高度的一半
$this->height / 2 + ($box[0] - $box[7]) / 2,
//获取文本颜色
$this->getTextColor(),
$font,
$code
);
}
}
//返回一个字符串使用指定字体 字体磅数 倾斜角度 的四个角坐标的数组
imagettfbbox($size, $angle, $fontfile, $text);
imagettfbbox(字体磅数,倾斜角度,字体,字符串);
//在画布指定位置绘制指定大小颜色的字符
imagettftext($image, $size, $angle, $x, $y, $color, $fontfile, $text);
imagettftext(画布,字体磅数,倾斜角度,X轴基点,Y轴基点,颜色,字体,需要绘制的字符);
imagettfbbox( )下标对应
下标 | 对应位置 |
---|---|
0 | 左下X |
1 | 左下Y |
2 | 右下X |
3 | 右下Y |
4 | 右上X |
5 | 右上Y |
6 | 左上X |
7 | 左上Y |
绘制干扰点和干扰线
//生成线
protected function line()
{
//指定要随机生成线的条数
for ($i = 0; $i < $this->6; $i++) {
//设定生成线的宽度 宽度在1和2之间随机
imagesetthickness($this->res, mt_rand(1, 2));
//画线 两点一线 一个点有xy两个坐标 所以我们有四个坐标
imageline(
$this->res,
mt_rand(0, $this->width),
mt_rand(0, $this->height),
mt_rand(0, $this->width),
mt_rand(0, $this->height),
$this->getColor()
);
}
}
//生成点
protected function pixel()
{
//指定生成点的个数 $this->height * $this->width * 0.1 得到当前画布的十分之一个像素点
for ($i = 0; $i < $this->height * $this->width * 0.1; $i++) {
//绘制点
imagesetpixel(
$this->res,
mt_rand(0, $this->width),
mt_rand(0, $this->height),
$this->getColor()
);
}
}
//设定线条粗细
imagesetthickness($image, $thickness);
imagesetthickness(画布, 线条粗细);
//绘制线
imageline($image, $x1, $y1, $x2, $y2, $color);
imageline(画布,第一个点X坐标,第一个点Y坐标,第二个点X坐标,第二个点Y坐标,颜色);
//绘制点
imagesetpixel ($image, $x, $y, $color);
imagesetpixel (画布,X坐标,Y坐标,颜色);
输出方法
在输出前要声明输出的是什么资源,否则无法输出图像png可以改成gif,jpg之类的图片格式
protected function show()
{
//声明当前资源头文件
header('Content-type:image/png');
//图片输出
imagepng($this->res);
}
调用方法
所有的模块都已经写好了,现在需要一个调用他们的方法我们只留给外部一个render方法,所以我们把方法的调用加在render方法中
public function render()
{
//创建画板
$res = imagecreatetruecolor($this->width, $this->height);
//对画板进行填充
imagefill(
$this->res = $res,
0,
0,
//填充的颜色
imagecolorallocate(
$res,
222,
222,
222
)
);
$this->text();//生成随机字符
$this->pixel();//生成点
$this->line();//生成线
$this->show();//进行输出
return $this->code;//返回生成的字符串
}
为了方便比较验证码是否正确我们将验证码生成的字符串返回
完整Captcha.php代码
<?php
class Captcha
{
protected
$height,//画板高度
$width,//画板宽度
$res,//画板资源
$len,//生成随机字符的个数
$code;//保存随机生成的字符串
public function render()
{
//创建画板
$res = imagecreatetruecolor($this->width, $this->height);
//对画板进行填充
imagefill(
$this->res = $res,
0,
0,
//填充的颜色
imagecolorallocate(
$res,
222,
222,
222
)
);
$this->text();//生成随机字符
$this->pixel();//生成点
$this->line();//生成线
$this->show();//进行输出
return $this->code;//返回生成的字符串
}
//构造函数 实例化的时候如果没有传值 那么它将会有一个初始值
public function __construct(int $width = 200, int $height = 60, int $len = 5)
{
$this->width = $width;
$this->height = $height;
$this->len = $len;
}
protected function show()
{
//声明当前资源头文件
header('Content-type:image/png');
//图片输出
imagepng($this->res);
}
//生成线
protected function line()
{
//指定要随机生成线的条数
for ($i = 0; $i < $this->width * 0.035; $i++) {
//设定生成线的宽度 宽度在1和2之间随机
imagesetthickness($this->res, mt_rand(1, 2));
//画线 两点一线 一个点有xy两个坐标 所以我们有四个坐标
imageline(
$this->res,
mt_rand(0, $this->width),
mt_rand(0, $this->height),
mt_rand(0, $this->width),
mt_rand(0, $this->height),
$this->getColor()
);
}
}
//生成点
protected function pixel()
{
//指定生成点的个数 $this->height * $this->width * 0.1 得到当前画布的十分之一个像素点
for ($i = 0; $i < $this->height * $this->width * 0.1; $i++) {
//绘制点
imagesetpixel(
$this->res,
mt_rand(0, $this->width),
mt_rand(0, $this->height),
$this->getColor()
);
}
}
//绘制字符
protected function text()
{
//指定绘制文本所使用的字体 realpath('相对路径') 方法会返回一个绝对路径
$font = realpath('../font/Alibaba-PuHuiTi-Medium.ttf');
//根据画布宽度 决定字体的大小
$fontsize = $this->width / $this->len;
//绘制指定个数的随机字符
for ($i = 0; $i < $this->len; $i++) {
//保存得到的随机字符
$this->code .= $code = $this->getRandText();
//平均分布字符
$x = $this->width / $this->len;
//计算倾斜角度
$angle = mt_rand(-30, 30);
//imagettfbbox得到四个边界的位置 返回一个数组
$box = imagettfbbox($fontsize, $angle, $font, 'A');
//绘制文本
imagettftext(
$this->res,
$fontsize,
$angle,
$x * $i + $this->width * 0.05,
//使文字居中 屏幕高度的一半-文字高度的一半
$this->height / 2 + ($box[0] - $box[7]) / 2,
//获取文本颜色
$this->getTextColor(),
$font,
$code
);
}
}
//随机生成字符
protected function getRandText()
{
while (true) {
//随机asscii码中数字大写字母小写字母对应的十进制 其中包含一些 我们不需要的
$ran = mt_rand(48, 122);
if (
$ran >= 48 && $ran <= 57 || //数字范围
$ran >= 65 && $ran <= 90 || //大写字母范围
$ran >= 97 && $ran <= 122 //小写字母范围
) {
//返回对应字符
return chr($ran);
}
}
}
//随机颜色
protected function getColor()
{
return imagecolorallocate(
$this->res,
mt_rand(0, 255),
mt_rand(0, 255),
mt_rand(0, 255)
);
}
//随机文本颜色
protected function getTextColor()
{
return imagecolorallocate($this->res,
mt_rand(0, 125),
mt_rand(0, 125),
mt_rand(0, 125)
);
}
}
调用Captcha.php中的render方法
创建另一个getImage.php文件引入Captcha.php文件将Captcha.php实例化再调用render方法。
由于HTTP协议是一种无状态协议,也就是客户端向服务器发送一个请求request,然后服务器返回一个相应response,之后这个连接就会被关闭,两者也没有任何关系了,意思就是验证码字符计算出来后就被释放了,后期用户输入验证码后无法进行比较,那怎么解决呢?
答案就是使用session 对计算结果进行保存,方便后期比较。
GetImage.php代码
<?php
//启动session会话
session_start();
//导入Capthca.php文件
include 'Captcha.php';
//实例化并且传递参数 Captcha(图片宽度,图片高度,需要生成字符的个数)
$captcha =new Captcha(100,50,4);
//调用render方法进行图片的绘制 并将生成图片所用的字符保存到code中
$code=$captcha->render();
//将生成的字符放在session中
$_SESSION['captcha']=$code;
将图片输出到页面
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="Check.php" method="post">
<table>
<tr>
<td>
<input type="text" name="captcha">
</td>
<td>
<img src="GetImage.php" alt="验证码" onclick="this.src='GetImage.php?'+Math.random()" >
</td>
</tr>
<tr>
<input type="submit" >
</tr>
</table>
</form>
</body>
</html>
Check.php代码
上面我们将表单提交到Check.php进行验证
<?php
session_start();
if (strtoupper(trim($_SESSION['captcha']))==strtoupper(trim($_POST['captcha']))){
echo "true";
}else{
echo "false";
}
这是我第一次发文,如果你能看到这里 非常感谢
如果能够帮到你我非常开心,如果我有什么不足的地方希望多多指正
https://blog.csdn.net/sinat_15274667/article/details/53517311
https://www.runoob.com/php/php-sessions.html
http://houdunren.gitee.io/note/php/9%20%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86.html