PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/system/core/captcha.php

https://github.com/sony88/answion
PHP | 643 lines | 377 code | 59 blank | 207 comment | 75 complexity | d8009bab6299b1337021abd9bac8631a MD5 | raw file
  1. <?php
  2. /*
  3. +--------------------------------------------------------------------------
  4. | Anwsion [#RELEASE_VERSION#]
  5. | ========================================
  6. | by Anwsion dev team
  7. | (c) 2011 - 2012 Anwsion Software
  8. | http://www.anwsion.com
  9. | ========================================
  10. | Support: zhengqiang@gmail.com
  11. |
  12. +---------------------------------------------------------------------------
  13. */
  14. class core_captcha
  15. {
  16. /**
  17. * 验证码
  18. * char: 字符
  19. * angle: 字符偏移的角度 (-30 <= angle <= 30)
  20. * color: 字符颜色
  21. *
  22. * @var array
  23. * @access private
  24. */
  25. public $code = array();
  26. /**
  27. * 字体信息
  28. * space: 字符间隔 (px)
  29. * size: 字体大小 (px)
  30. * left: 第一个字符距离图像最左边的象素 (px)
  31. * top: 字符距离图像最上边的象素 (px)
  32. * file: 字体文件的路径
  33. *
  34. * @var array
  35. * @access private
  36. */
  37. public $font = array();
  38. /**
  39. * 图像信息
  40. * type: 图像类型
  41. * mime: MIME 类型
  42. * width: 图像的宽 (px)
  43. * height: 图像高 (px)
  44. * func: 创建图像的方法
  45. *
  46. * @var array
  47. * @access private
  48. */
  49. public $image = array();
  50. /**
  51. * 干扰信息
  52. * type: 干扰类型 (false 表示不使用)
  53. * density: 干扰密度
  54. *
  55. * @var array
  56. * @access private
  57. */
  58. public $molestation = array();
  59. /**
  60. * 背景色 (RGB)
  61. * r: 红色 (0 - 255)
  62. * g: 绿色 (0 - 255)
  63. * b: 蓝色 (0 - 255)
  64. *
  65. * @var array
  66. * @access private
  67. */
  68. public $bg_color = array();
  69. /**
  70. * 默认前景色 (RGB)
  71. * r: 红色 (0 - 255)
  72. * g: 绿色 (0 - 255)
  73. * b: 蓝色 (0 - 255)
  74. *
  75. * @var array
  76. * @access private
  77. */
  78. public $fg_color = array();
  79. /**
  80. * Session 变量名
  81. *
  82. * @var string
  83. * @access private
  84. */
  85. public static $session = 'aws_captcha';
  86. public $the_code;
  87. /**
  88. * 设置验证码
  89. *
  90. * @access public
  91. * @param array 字符信息
  92. * characters string 允许的字符
  93. * length int 验证码长度
  94. * deflect boolean 字符是否偏转
  95. * multicolor boolean 字符是否彩色
  96. * @return void
  97. */
  98. function setCode($code)
  99. {
  100. if (is_array($code))
  101. {
  102. if (! isset($code['characters']) || ! is_string($code['characters']))
  103. {
  104. $code['characters'] = 'A-H,J-X,3-9';
  105. }
  106. if (! (is_integer($code['length']) || $code['length'] <= 0))
  107. {
  108. $code['length'] = 4;
  109. }
  110. if (! is_bool($code['deflect']))
  111. {
  112. $code['deflect'] = true;
  113. }
  114. if (! is_bool($code['multicolor']))
  115. {
  116. $code['multicolor'] = true;
  117. }
  118. }
  119. else
  120. {
  121. $code = array(
  122. 'characters' => 'A-Z,2-9',
  123. 'length' => 4,
  124. 'deflect' => true,
  125. 'multicolor' => true
  126. );
  127. }
  128. $this->code = $code;
  129. }
  130. /**
  131. * 设置 session 变量名
  132. *
  133. * @access public
  134. * @param string session 变量名
  135. * @return void
  136. */
  137. function setSession($session)
  138. {
  139. if (isset($session) && ! empty($session))
  140. {
  141. self::$session = $session;
  142. }
  143. }
  144. /**
  145. * 设置背景色
  146. *
  147. * @access public
  148. * @param array RGB 颜色
  149. * @return void
  150. */
  151. function setBgColor($color)
  152. {
  153. if (is_array($color) && is_integer($color['r']) && is_integer($color['g']) && is_integer($color['b']) && ($color['r'] >= 0 && $color['r'] <= 255) && ($color['g'] >= 0 && $color['g'] <= 255) && ($color['b'] >= 0 && $color['b'] <= 255))
  154. {
  155. $this->bg_color = $color;
  156. }
  157. else
  158. {
  159. $this->bg_color = array(
  160. 'r' => 255,
  161. 'g' => 255,
  162. 'b' => 255
  163. );
  164. }
  165. // 设置默认的前景色, 与背景色相反
  166. $fg_color = array(
  167. 'r' => 255 - $this->bg_color['r'],
  168. 'g' => 255 - $this->bg_color['g'],
  169. 'b' => 255 - $this->bg_color['b']
  170. );
  171. $this->setFgColor($fg_color);
  172. }
  173. /**
  174. * 设置干扰信息
  175. *
  176. * @access public
  177. * @param array 干扰信息
  178. * type string 干扰类型 (选项: false, 'point', 'line')
  179. * density string 干扰密度 (选项: 'normal', 'muchness', 'fewness')
  180. * @return void
  181. */
  182. function setMolestation($molestation)
  183. {
  184. if (is_array($molestation))
  185. {
  186. if (! isset($molestation['type']) || ($molestation['type'] != 'point' && $molestation['type'] != 'line' && $molestation['type'] != 'both'))
  187. {
  188. $molestation['type'] = 'point';
  189. }
  190. if (! is_string($molestation['density']))
  191. {
  192. $molestation['density'] = 'normal';
  193. }
  194. $this->molestation = $molestation;
  195. }
  196. else
  197. {
  198. $this->molestation = array(
  199. 'type' => 'point',
  200. 'density' => 'normal'
  201. );
  202. }
  203. }
  204. /**
  205. * 设置字体信息
  206. *
  207. * @access public
  208. * @param array 字体信息
  209. * space int 字符间隔 (px)
  210. * size int 字体大小 (px)
  211. * left int 第一个字符距离图像最左边的象素 (px)
  212. * top int 字符距离图像最上边的象素 (px)
  213. * file string 字体文件的路径
  214. * @return void
  215. */
  216. function setFont($font)
  217. {
  218. if (is_array($font))
  219. {
  220. if (!is_integer($font['space']) || $font['space'] < 0)
  221. {
  222. $font['space'] = 5;
  223. }
  224. if (! is_integer($font['size']) || $font['size'] < 0)
  225. {
  226. $font['size'] = 12;
  227. }
  228. if (! is_integer($font['left']) || $font['left'] < 0 || $font['left'] > $this->image['width'])
  229. {
  230. $font['left'] = 5;
  231. }
  232. if (! is_integer($font['top']) || $font['top'] < 0 || $font['top'] > $this->image['height'])
  233. {
  234. $font['top'] = $this->image['height'] - 5;
  235. }
  236. $this->font = $font;
  237. }
  238. else
  239. {
  240. $this->font = array(
  241. 'space' => 5,
  242. 'size' => 5,
  243. 'left' => 5,
  244. 'top' => ($this->image['height'] - 5),
  245. );
  246. }
  247. }
  248. /**
  249. * 设置图像信息
  250. *
  251. * @access public
  252. * @param array 图像信息
  253. * type string 图像类型 (选项: 'png', 'gif', 'wbmp', 'jpg')
  254. * width int 图像宽 (px)
  255. * height int 图像高 (px)
  256. * @return void
  257. */
  258. function setImage($image)
  259. {
  260. if (is_array($image))
  261. {
  262. if (! is_integer($image['width']) || $image['width'] <= 0)
  263. {
  264. $image['width'] = 50;
  265. }
  266. if (! is_integer($image['height']) || $image['height'] <= 0)
  267. {
  268. $image['height'] = 25;
  269. }
  270. $this->image = $image;
  271. $information = $this->getImageType($image['type']);
  272. if (is_array($information))
  273. {
  274. $this->image['mime'] = $information['mime'];
  275. $this->image['func'] = $information['func'];
  276. }
  277. else
  278. {
  279. $this->image['type'] = 'png';
  280. $information = $this->getImageType('png');
  281. $this->image['mime'] = $information['mime'];
  282. $this->image['func'] = $information['func'];
  283. }
  284. }
  285. else
  286. {
  287. $information = $this->getImageType('png');
  288. $this->image = array(
  289. 'type' => 'png',
  290. 'mime' => $information['mime'],
  291. 'func' => $information['func'],
  292. 'width' => 50,
  293. 'height' => 25
  294. );
  295. }
  296. }
  297. /**
  298. * 绘制图像
  299. *
  300. * @access public
  301. * @param string 文件名, 留空表示输出到浏览器
  302. * @return void
  303. */
  304. function paint($filename = null)
  305. {
  306. // 创建图像
  307. $im = imagecreatetruecolor($this->image['width'], $this->image['height']);
  308. // 设置图像背景
  309. $bg_color = imagecolorallocate($im, $this->bg_color['r'], $this->bg_color['g'], $this->bg_color['b']);
  310. imagefilledrectangle($im, 0, 0, $this->image['width'], $this->image['height'], $bg_color);
  311. // 生成验证码相关信息
  312. $code = $this->generateCode();
  313. // 生成的验证码
  314. $the_code = '';
  315. // 向图像中写入字符
  316. $num = count($code);
  317. $current_left = $this->font['left'];
  318. $current_top = $this->font['top'];
  319. for ($i = 0; $i < $num; $i ++)
  320. {
  321. $font_size = rand(($this->font['size'] / 2), $this->font['size']); // 随机字体大小
  322. if (is_array($this->font['file']))
  323. {
  324. $font_file = $this->font['file'][rand(0, (sizeof($this->font['file']) - 1))];
  325. }
  326. else
  327. {
  328. $font_file = $this->font['file'];
  329. }
  330. $font_color = imagecolorallocate($im, $code[$i]['color']['r'], $code[$i]['color']['g'], $code[$i]['color']['b']);
  331. imagettftext($im, $font_size, $code[$i]['angle'], $current_left, $current_top, $font_color, $font_file, $code[$i]['char']);
  332. $current_left += $font_size + $this->font['space'];
  333. $the_code .= $code[$i]['char'];
  334. }
  335. $this->the_code = $the_code;
  336. // 初始化 session
  337. // 如果 session 未启用, 则开启它
  338. //if (empty($_SESSION['SID'])) @session_start();
  339. //用 md5() 给密码加密, 写入 session
  340. $_SESSION[self::$session] = md5($the_code);
  341. $_SESSION[self::$session . "_none_case"] = md5(strtolower($the_code));
  342. // 绘制图像干扰
  343. $this->paintMolestation($im);
  344. // 输出
  345. if (isset($filename) && $filename != '')
  346. {
  347. $f_name = $this->image['func'];
  348. $f_name($im, $filename . $this->image['type']);
  349. }
  350. else
  351. {
  352. header("Cache-Control: no-cache, must-revalidate");
  353. header("Content-type: " . $this->image['mime']);
  354. $f_name = $this->image['func'];
  355. $f_name($im);
  356. }
  357. imagedestroy($im);
  358. }
  359. /**
  360. * 验证用户输入的验证码
  361. *
  362. * @param string 用户输入的字符串
  363. * @param boolean 是否区分大小写
  364. * @return boolean 正确返回 true
  365. */
  366. static function validate($input, $is_match_case = true)
  367. {
  368. if ($is_match_case)
  369. {
  370. return (strcmp($_SESSION[self::$session], md5($input)) == 0);
  371. }
  372. else
  373. {
  374. return (strcmp($_SESSION[self::$session . '_none_case'], md5(strtolower($input))) == 0);
  375. }
  376. }
  377. /**
  378. * 清除SESSION 记录
  379. *
  380. */
  381. static function clear()
  382. {
  383. unset($_SESSION[self::$session]);
  384. unset($_SESSION[self::$session . '_none_case']);
  385. }
  386. /**
  387. * 设置前景色
  388. *
  389. * @access private
  390. * @param array RGB 颜色
  391. * @return void
  392. */
  393. function setFgColor($color)
  394. {
  395. if (is_array($color) && is_integer($color['r']) && is_integer($color['g']) && is_integer($color['b']) && ($color['r'] >= 0 && $color['r'] <= 255) && ($color['g'] >= 0 && $color['g'] <= 255) && ($color['b'] >= 0 && $color['b'] <= 255))
  396. {
  397. $this->fg_color = $color;
  398. }
  399. else
  400. {
  401. $this->fg_color = array(
  402. 'r' => 0,
  403. 'g' => 0,
  404. 'b' => 0
  405. );
  406. }
  407. }
  408. /**
  409. * 生成随机验证码
  410. *
  411. * @access private
  412. * @return array 生成的验证码
  413. */
  414. function generateCode()
  415. {
  416. // 创建允许的字符串
  417. $characters = explode(',', $this->code['characters']);
  418. $num = count($characters);
  419. for ($i = 0; $i < $num; $i ++)
  420. {
  421. if (substr_count($characters[$i], '-') > 0)
  422. {
  423. $character_range = explode('-', $characters[$i]);
  424. for ($j = ord($character_range[0]); $j <= ord($character_range[1]); $j ++)
  425. {
  426. $array_allow[] = chr($j);
  427. }
  428. }
  429. else
  430. {
  431. $array_allow[] = $array_allow[$i];
  432. }
  433. }
  434. $index = 0;
  435. while (list($key, $val) = each($array_allow))
  436. {
  437. $array_allow_tmp[$index] = $val;
  438. $index ++;
  439. }
  440. $array_allow = $array_allow_tmp;
  441. // 生成随机字符串
  442. mt_srand((double)microtime() * 1000000);
  443. $code = array();
  444. $index = 0;
  445. $i = 0;
  446. while ($i < $this->code['length'])
  447. {
  448. $index = mt_rand(0, count($array_allow) - 1);
  449. $code[$i]['char'] = $array_allow[$index];
  450. if ($this->code['deflect'])
  451. {
  452. $code[$i]['angle'] = mt_rand(- 10, 10);
  453. }
  454. else
  455. {
  456. $code[$i]['angle'] = 0;
  457. }
  458. if ($this->code['multicolor'])
  459. {
  460. $code[$i]['color']['r'] = mt_rand(0, 255);
  461. $code[$i]['color']['g'] = mt_rand(0, 255);
  462. $code[$i]['color']['b'] = mt_rand(0, 255);
  463. }
  464. else
  465. {
  466. $code[$i]['color']['r'] = $this->fg_color['r'];
  467. $code[$i]['color']['g'] = $this->fg_color['g'];
  468. $code[$i]['color']['b'] = $this->fg_color['b'];
  469. }
  470. $i ++;
  471. }
  472. return $code;
  473. }
  474. /**
  475. * 获取图像类型
  476. *
  477. * @access private
  478. * @param string 扩展名
  479. * @return [mixed] 错误时返回 false
  480. */
  481. function getImageType($extension)
  482. {
  483. switch (strtolower($extension))
  484. {
  485. case 'png' :
  486. $information['mime'] = image_type_to_mime_type(IMAGETYPE_PNG);
  487. $information['func'] = 'imagepng';
  488. break;
  489. case 'gif' :
  490. $information['mime'] = image_type_to_mime_type(IMAGETYPE_GIF);
  491. $information['func'] = 'imagegif';
  492. break;
  493. case 'wbmp' :
  494. $information['mime'] = image_type_to_mime_type(IMAGETYPE_WBMP);
  495. $information['func'] = 'imagewbmp';
  496. break;
  497. case 'jpg' :
  498. case 'jpeg' :
  499. case 'jpe' :
  500. $information['mime'] = image_type_to_mime_type(IMAGETYPE_JPEG);
  501. $information['func'] = 'imagejpeg';
  502. break;
  503. default :
  504. $information = false;
  505. }
  506. return $information;
  507. }
  508. /**
  509. * 绘制图像干扰
  510. *
  511. * @access private
  512. * @param resource 图像资源
  513. * @return void
  514. */
  515. function paintMolestation(&$im)
  516. {
  517. // 总象素
  518. $num_of_pels = ceil($this->image['width'] * $this->image['height'] / 5);
  519. switch ($this->molestation['density'])
  520. {
  521. case 'fewness' :
  522. $density = ceil($num_of_pels / 3);
  523. break;
  524. case 'muchness' :
  525. $density = ceil($num_of_pels / 3 * 2);
  526. break;
  527. case 'normal' :
  528. $density = ceil($num_of_pels / 2);
  529. default :
  530. }
  531. switch ($this->molestation['type'])
  532. {
  533. case 'point' :
  534. $this->paintPoints($im, $density);
  535. break;
  536. case 'line' :
  537. $density = ceil($density / 30);
  538. $this->paintLines($im, $density);
  539. break;
  540. case 'both' :
  541. $density = ceil($density / 2);
  542. $this->paintPoints($im, $density);
  543. $density = ceil($density / 30);
  544. $this->paintLines($im, $density);
  545. break;
  546. default :
  547. break;
  548. }
  549. }
  550. /**
  551. * 画点
  552. *
  553. * @access private
  554. * @param resource 图像资源
  555. * @param int 图像资源
  556. * @return void
  557. */
  558. function paintPoints(&$im, $quantity)
  559. {
  560. mt_srand((double)microtime() * 1000000);
  561. for ($i = 0; $i < $quantity; $i ++)
  562. {
  563. $randcolor = imagecolorallocate($im, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
  564. imagesetpixel($im, mt_rand(0, $this->image['width']), mt_rand(0, $this->image['height']), $randcolor);
  565. }
  566. }
  567. /**
  568. * 画线
  569. *
  570. * @access private
  571. * @param resource 图像资源
  572. * @param int 图像资源
  573. * @return void
  574. */
  575. function paintLines(&$im, $quantity)
  576. {
  577. mt_srand((double)microtime() * 1000000);
  578. for ($i = 0; $i < $quantity; $i ++)
  579. {
  580. $randcolor = imagecolorallocate($im, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
  581. imageline($im, mt_rand(0, $this->image['width']), mt_rand(0, $this->image['height']), mt_rand(0, $this->image['width']), mt_rand(0, $this->image['height']), $randcolor);
  582. }
  583. }
  584. }