PHP生成图片验证码和验证码验证

绘制验证码的过程可分为:

  1. 创建一个指定大小的画板
  2. 给这画板填充一个背景色
  3. 开始画你想要的图形

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 对计算结果进行保存,方便后期比较。

为什么要使用session
如何使用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

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值