├── README.md ├── composer.json ├── example.png ├── examples ├── index.html └── member.class.php └── src ├── Captcha.php └── images ├── 1.jpeg ├── 2.jpeg ├── 3.jpeg ├── 4.jpeg └── 5.jpeg /README.md: -------------------------------------------------------------------------------- 1 | # captcha 2 | ## PHP输出滑块旋转验证码 3 | 4 | ### 使用方式: 5 | `git clone https://github.com/ljj7410/captcha.git` 6 | #### 或 7 | `composer require ljj7410/captcha` 8 | 9 | ### 示例图片 10 | 11 | ![](./example.png) 12 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ljj7410/captcha", 3 | "description": "PHP滑块旋转验证码", 4 | "keywords": ["captcha","滑块验证码"], 5 | "homepage": "https://github.com/ljj7410/captcha", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Dean.Lee", 10 | "email": "dean7410@163.com", 11 | "homepage": "", 12 | "role": "Developer" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=7.2", 17 | "ext-gd": "*" 18 | }, 19 | "autoload": { 20 | "psr-4": { "Util\\": "src/" } 21 | }, 22 | "minimum-stability": "dev" 23 | } 24 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoole/captcha/c9eae3700ade41a2bfef9a9126586fd7f3857038/example.png -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 测试 6 | 15 | 16 | 17 |
18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 | 27 | 80 | 81 | -------------------------------------------------------------------------------- /examples/member.class.php: -------------------------------------------------------------------------------- 1 | create(); 16 | 17 | //记录验证码 18 | getRedis()->set('captcha_code', $cap->getCode()); 19 | 20 | echo $bin; 21 | } 22 | 23 | public function verifyCode() 24 | { 25 | $code = $_POST['code']; 26 | $angle = getRedis()->get('captcha_code'); 27 | //验证码比对,需要允许存在一些误差 28 | if(($code < $angle + 2) && ($code > $angle - 2)){ 29 | echo '验证成功'; 30 | }else{ 31 | echo '验证失败'; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Captcha.php: -------------------------------------------------------------------------------- 1 | width = $option['width']; 16 | if(isset($option['height']))$this->height = $option['height']; 17 | if(isset($option['dir_path']))$this->dir_path = $option['dir_path']; 18 | } 19 | 20 | public function create(): string 21 | { 22 | //挑选一张原始图 23 | $files = scandir($this->dir_path); 24 | shuffle($files); 25 | foreach($files as $file){ 26 | if(!strpos($file,'.'))continue; 27 | $arr = getimagesize($this->dir_path . $file); 28 | if(in_array($arr[2], [1,2,3]) && $arr[0] >= $this->width && $arr[1] >= $this->height){ 29 | $this->orig_info = [ 30 | 'width' => $arr[0], 31 | 'height' => $arr[1], 32 | 'type' => $arr[2], 33 | 'path' => $this->dir_path . $file 34 | ]; 35 | break; 36 | } 37 | } 38 | if(empty($this->orig_info)){ 39 | trigger_error('没有符合要求的图片库', E_USER_WARNING); 40 | return ''; 41 | } 42 | 43 | //获取图片数据 44 | $im = null; 45 | switch ($this->orig_info['type']){ 46 | case 1: 47 | $im = @imagecreatefromgif($this->orig_info['path']); 48 | break; 49 | case 2: 50 | $im = @imagecreatefromjpeg($this->orig_info['path']); 51 | break; 52 | case 3: 53 | $im = @imagecreatefrompng($this->orig_info['path']); 54 | break; 55 | } 56 | if(empty($im)){ 57 | trigger_error('图片载入失败,请检查GD库版本', E_USER_WARNING); 58 | return ''; 59 | } 60 | 61 | //随机裁切出一块 62 | $master_im = imagecrop($im, [ 63 | 'x' => rand(0, $this->orig_info['width'] - $this->width), 64 | 'y' => rand(0, $this->orig_info['height'] - $this->height), 65 | 'width' => $this->width, 66 | 'height' => $this->height 67 | ]); 68 | 69 | //从裁切图片中再随机裁切一块方形出来 70 | //随机圆形半径 71 | $radius = rand(80,120); 72 | //随机圆形坐标 73 | $position = [ 74 | rand(10 + $radius, $this->width - $radius - 10), 75 | rand(10 + $radius, $this->height - $radius - 10) 76 | ]; 77 | //创建一张透明图片 78 | $sub_im = imagecreatetruecolor($radius * 2, $radius * 2); 79 | $mask_im = imagecreatetruecolor($radius * 2, $radius * 2); 80 | imagesavealpha($mask_im, true); 81 | $bg = imagecolorallocatealpha($mask_im, 255, 255, 255, 127); 82 | imagefill($mask_im, 0, 0, $bg); 83 | $black = imagecolorallocate($mask_im, 0, 0, 0); 84 | 85 | //在透明图片上绘制圆形图像 86 | for ($x = 0; $x < $radius * 2; $x++) { 87 | for ($y = 0; $y < $radius * 2; $y++) { 88 | $rgbColor = imagecolorat($master_im, $x + $position[0] - $radius, $y + $position[1] - $radius); 89 | if(pow($x - $radius,2) + pow($y - $radius, 2) < pow($radius, 2)){ 90 | imagesetpixel($sub_im, $x, $y, $rgbColor); 91 | imagesetpixel($mask_im, $x, $y, $black); 92 | } 93 | } 94 | } 95 | //将黑色遮罩覆盖在原图上 96 | imagecopy($master_im, $mask_im, $position[0] - $radius, $position[1] - $radius, 0, 0, $radius * 2, $radius * 2); 97 | 98 | //将圆形图旋转任意角度 99 | $this->angle = rand(0, 36000) / 100; 100 | $sub_im = imagerotate($sub_im, $this->angle, 0); 101 | $x = imagesx($sub_im) / 2 - $radius; 102 | $sub_im = imagecrop($sub_im, [ 103 | 'x' => $x, 104 | 'y' => $x, 105 | 'width' => $radius * 2, 106 | 'height' => $radius * 2 107 | ]); 108 | 109 | ob_start(); 110 | //写入时间戳 111 | echo pack('I', time()); 112 | //写入X坐标 113 | echo pack('v', $position[0]); 114 | //写入Y坐标 115 | echo pack('v', $position[1]); 116 | //写入主图 117 | imagejpeg($master_im); 118 | $size1 = ob_get_length() - 8; 119 | //写入分图 120 | imagejpeg($sub_im); 121 | $size2 = ob_get_length() - $size1 - 8; 122 | //写入主图大小 123 | echo pack('I', $size1); 124 | //写入分图大小 125 | echo pack('I', $size2); 126 | //输出缓冲区 127 | return ob_get_clean(); 128 | } 129 | 130 | /** 131 | * 获取角度验证码 132 | * @return float 133 | */ 134 | public function getCode() : float 135 | { 136 | return $this->angle; 137 | } 138 | 139 | /** 140 | * 验证code是否正确 141 | * @param int $code 142 | * @return bool 143 | */ 144 | public function verify(int $code) : bool 145 | { 146 | return ($code < $this->angle + 2) && ($code > $this->angle - 2); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/images/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoole/captcha/c9eae3700ade41a2bfef9a9126586fd7f3857038/src/images/1.jpeg -------------------------------------------------------------------------------- /src/images/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoole/captcha/c9eae3700ade41a2bfef9a9126586fd7f3857038/src/images/2.jpeg -------------------------------------------------------------------------------- /src/images/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoole/captcha/c9eae3700ade41a2bfef9a9126586fd7f3857038/src/images/3.jpeg -------------------------------------------------------------------------------- /src/images/4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoole/captcha/c9eae3700ade41a2bfef9a9126586fd7f3857038/src/images/4.jpeg -------------------------------------------------------------------------------- /src/images/5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simoole/captcha/c9eae3700ade41a2bfef9a9126586fd7f3857038/src/images/5.jpeg --------------------------------------------------------------------------------