PageRenderTime 58ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/server/vpn.51sync.com/library/helper/imgcode.php

https://github.com/qibinghua/vpn.51sync
PHP | 629 lines | 325 code | 88 blank | 216 comment | 32 complexity | 65fa7842a2dad4232705138c477e519e MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. // $Id: imgcode.php 2651 2009-08-30 17:14:42Z firzen $
  3. /**
  4. * 定义 Helper_ImgCode 类、Helper_ImgCodeSimple 类和 Helper_ImgCodeTTF 类
  5. *
  6. * @link http://qeephp.com/
  7. * @copyright Copyright (c) 2006-2009 Qeeyuan Inc. {@link http://www.qeeyuan.com}
  8. * @license New BSD License {@link http://qeephp.com/license/}
  9. * @version $Id: imgcode.php 2651 2009-08-30 17:14:42Z firzen $
  10. * @package helper
  11. */
  12. /**
  13. * Helper_ImgCode 类提供验证码生成和检验的接口
  14. *
  15. * @author YuLei Liao <liaoyulei@qeeyuan.com>
  16. * @version $Id: imgcode.php 2651 2009-08-30 17:14:42Z firzen $
  17. * @package helper
  18. */
  19. class Helper_ImgCode
  20. {
  21. /**
  22. * 验证码的配置
  23. *
  24. * @var array
  25. */
  26. static private $_config = array(
  27. // 用什么键名在 session 中保存验证码
  28. 'imgcode_session_key' => '_IMGCODE',
  29. // 从 session 中读取的验证码
  30. 'imgcode_session_value' => null,
  31. // 用什么键名在 session 中保存验证码过期时间
  32. 'expired_session_key' => '_IMGCODE_EXPIRED',
  33. // 验证码过期时间
  34. 'expired_session_value' => null,
  35. );
  36. /**
  37. * 利用 GD 库产生验证码图像,并封装为 QView_Output 对象
  38. *
  39. * $style 参数值不同时,$options 参数包含的选项也不同。
  40. *
  41. * 用法:
  42. * @code php
  43. * // 控制器文件
  44. * class Controller_Default
  45. * {
  46. * function actionImgcode()
  47. * {
  48. * // 在控制器中用下列代码返回一个图像验证码
  49. * return Helper_ImgCode::create(6, 900, 'simple');
  50. * }
  51. * }
  52. *
  53. * // 模板文件中使用下列代码引用一个图像验证码
  54. * <img src="<?php echo url('default/imgcode'); ?>" border="0" />
  55. * @endcode
  56. *
  57. * @param int $length 验证码的长度
  58. * @param int $lefttime 验证码的有效期
  59. * @param string $style 验证码的样式
  60. * @param array $options 具体验证码样式的选项
  61. *
  62. * @return QView_Output 包含验证码图像的输出对象
  63. */
  64. static function create($length = 4, $lefttime = 900,
  65. $style = 'simple', array $options = null)
  66. {
  67. $class_name = 'Helper_ImgCode' . ucfirst(strtolower(preg_replace('/[^a-z0-9_]+/i', '', $style)));
  68. $options = (array)$options;
  69. $options['code_length'] = $length;
  70. $imgcode_obj = new $class_name($options);
  71. $code = $imgcode_obj->generateCode();
  72. self::_writeImgcodeToSession($code, $lefttime);
  73. return $imgcode_obj->generateImage($code);
  74. }
  75. /**
  76. * 比较输入的验证码是否和 session 中保存的验证码一致(不区分大小写)
  77. *
  78. * 用法:
  79. * @code php
  80. * // 控制器文件
  81. * class Controller_Default
  82. * {
  83. * function actionLogin()
  84. * {
  85. * if (Helper_ImgCode::isValid($this->_context->imgcode))
  86. * {
  87. * .... 比对通过
  88. * }
  89. *
  90. * ....
  91. * }
  92. * }
  93. * @endcode
  94. *
  95. * @param string $code 要比对的验证码
  96. * @param boolean $clean_session 是否在比对通过后清理 session 中保存的验证码
  97. * @param boolean $case_sensitive 是否区分大小写
  98. *
  99. * @return boolean 比对结果
  100. */
  101. static function isValid($code, $clean_session = false, $case_sensitive = false)
  102. {
  103. $code_in_session = self::_readImgcodeFromSession();
  104. if (strlen($code_in_session) == 0 || strlen($code) == 0)
  105. {
  106. return false;
  107. }
  108. if ($case_sensitive)
  109. {
  110. $ret = (string)$code_in_session == (string)$code;
  111. }
  112. else
  113. {
  114. $ret = strtolower($code_in_session) == strtolower($code);
  115. }
  116. if ($ret && $clean_session)
  117. {
  118. self::_cleanImgcodeFromSession();
  119. }
  120. return $ret;
  121. }
  122. /**
  123. * 清除 session 中的验证码信息
  124. */
  125. static function clean()
  126. {
  127. self::_cleanImgcodeFromSession();
  128. }
  129. /**
  130. * 将 16 进制颜色值转换为 rgb 值
  131. *
  132. * 用法:
  133. * @code php
  134. * $color = '#369';
  135. * list($r, $g, $b) = Helper_Image::hex2rgb($color);
  136. * echo "red: {$r}, green: {$g}, blue: {$b}";
  137. * @endcode
  138. *
  139. * @param string $color 颜色值
  140. * @param string $default 使用无效颜色值时返回的默认颜色
  141. *
  142. * @return array 由 RGB 三色组成的数组
  143. */
  144. static function hex2rgb($color, $default = 'ffffff')
  145. {
  146. $hex = trim($color, '#&Hh');
  147. $len = strlen($hex);
  148. if ($len == 3)
  149. {
  150. $hex = "{$hex[0]}{$hex[0]}{$hex[1]}{$hex[1]}{$hex[2]}{$hex[2]}";
  151. }
  152. elseif ($len < 6)
  153. {
  154. $hex = $default;
  155. }
  156. $dec = hexdec($hex);
  157. return array(($dec >> 16) & 0xff, ($dec >> 8) & 0xff, $dec & 0xff);
  158. }
  159. /**
  160. * 写入验证码和验证码过期时间到 session
  161. *
  162. * @param string $code 要写入 session 的验证码
  163. * @param int $lefttime 验证码的有效期
  164. */
  165. private static function _writeImgcodeToSession($code, $lefttime)
  166. {
  167. if (isset($_SESSION))
  168. {
  169. $_SESSION[self::$_config['imgcode_session_key']] = $code;
  170. $_SESSION[self::$_config['expired_session_key']] = CURRENT_TIMESTAMP + intval($lefttime);
  171. }
  172. }
  173. /**
  174. * 从 session 取得验证码和验证码过期时间
  175. */
  176. private static function _readImgcodeFromSession()
  177. {
  178. if (!isset($_SESSION))
  179. {
  180. return false;
  181. }
  182. $key = self::$_config['imgcode_session_key'];
  183. $imgcode = isset($_SESSION[$key]) ? $_SESSION[$key] : '';
  184. $key = self::$_config['expired_session_key'];
  185. $expired = isset($_SESSION[$key]) ? $_SESSION[$key] : 0;
  186. if (CURRENT_TIMESTAMP >= $expired) return false;
  187. return $imgcode;
  188. }
  189. /**
  190. * 从 session 中清除验证码和验证码过期时间
  191. */
  192. private static function _cleanImgcodeFromSession()
  193. {
  194. if (isset($_SESSION))
  195. {
  196. $key = self::$_config['imgcode_session_key'];
  197. unset($_SESSION[$key]);
  198. $key = self::$_config['expired_session_key'];
  199. unset($_SESSION[$key]);
  200. }
  201. }
  202. }
  203. /**
  204. * Helper_ImgCodeSimple 实现了一个简单样式的验证码
  205. *
  206. * @author YuLei Liao <liaoyulei@qeeyuan.com>
  207. * @version $Id: imgcode.php 2651 2009-08-30 17:14:42Z firzen $
  208. * @package helper
  209. */
  210. class Helper_ImgCodeSimple
  211. {
  212. /**
  213. * 生成验证码需要的选项
  214. *
  215. * @var array
  216. */
  217. protected $_options = array(
  218. // 验证码类型
  219. 'code_type' => 2,
  220. // 验证码长度
  221. 'code_length' => 4,
  222. // 字符上下浮动像素
  223. 'float_pixel' => 6,
  224. // 图像类型
  225. 'image_type' => IMAGETYPE_JPEG,
  226. // 字体,如果为 0-5,则使用 GD 库内置的字体
  227. // 如果使用自定义字体,font 必须指定为字体文件的绝对路径
  228. 'font' => 5,
  229. // 字体颜色
  230. 'color' => '0xffffff',
  231. // 背景色
  232. 'bgcolor' => '0x666666',
  233. // 验证码图片边界颜色
  234. 'bdcolor' => '0x000000',
  235. // 内空
  236. 'padding' => 2,
  237. // 边界线
  238. 'border' => 1,
  239. );
  240. /**
  241. * 构造函数
  242. *
  243. * @param array $options 生成验证码的选项
  244. */
  245. function __construct(array $options)
  246. {
  247. $this->_options = array_merge($this->_options, $options);
  248. }
  249. /**
  250. * 生成验证码
  251. *
  252. * @return string
  253. */
  254. function generateCode()
  255. {
  256. $code_type = intval($this->_options['code_type']);
  257. $code_length = intval($this->_options['code_length']);
  258. if ($code_length <= 0) { $code_length = 4; }
  259. switch ($code_type)
  260. {
  261. case 0:
  262. $seed = '0123456789';
  263. break;
  264. case 1:
  265. $seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  266. break;
  267. default:
  268. $seed = '346789ABCDEFGHJKLMNPQRTUVWXYabcdefghjklmnpqrtuvwxy';
  269. }
  270. $code = '';
  271. $len = strlen($seed) - 1;
  272. for ($i = 0; $i < $code_length; $i++)
  273. {
  274. $code .= substr($seed, mt_rand(0, $len), 1);
  275. }
  276. return $code;
  277. }
  278. /**
  279. * 生成验证码图像,返回 QView_Output 对象
  280. *
  281. * $param string $code
  282. *
  283. * @return QView_Output
  284. */
  285. function generateImage($code)
  286. {
  287. // 根据选项确定绘图选项
  288. $padding = intval($this->_options['padding']);
  289. if ($padding < 0) { $padding = 0; }
  290. $color = $this->_options['color'];
  291. $bgcolor = $this->_options['bgcolor'];
  292. $border = $this->_options['border'];
  293. $bdcolor = $this->_options['bdcolor'];
  294. $float_pixel = intval($this->_options['float_pixel']);
  295. // 确定要使用的字体
  296. if (is_int($this->_options['font']))
  297. {
  298. $font = intval($this->_options['font']);
  299. if ($font < 0 || $font > 5)
  300. {
  301. $font = 5;
  302. }
  303. }
  304. else
  305. {
  306. $font = imageloadfont($this->_options['font']);
  307. }
  308. // 确定字体宽度和高度
  309. $font_width = imagefontwidth($font);
  310. $font_height = imagefontheight($font);
  311. // 确定图像的宽度和高度
  312. $width = $font_width * strlen($code) + $padding * 2 + $border * 2 + 1;
  313. $height = $font_height + $padding * 2 + $border * 2 + 1 + $float_pixel;
  314. // 创建图像
  315. $img = imagecreate($width, $height);
  316. // 绘制边框
  317. if ($border)
  318. {
  319. list($r, $g, $b) = Helper_ImgCode::hex2rgb($bdcolor);
  320. $color = imagecolorallocate($img, $r, $g, $b);
  321. imagefilledrectangle($img, 0, 0, $width, $height, $color);
  322. }
  323. // 绘制背景
  324. list($r, $g, $b) = Helper_ImgCode::hex2rgb($bgcolor);
  325. $color = imagecolorallocate($img, $r, $g, $b);
  326. imagefilledrectangle($img, $border, $border, $width - $border - 1, $height - $border - 1, $color);
  327. // 绘制文字
  328. list($r, $g, $b) = Helper_ImgCode::hex2rgb($color);
  329. $color = imagecolorallocate($img, $r, $g, $b);
  330. for ($i = 0, $max = strlen($code); $i < $max; $i++)
  331. {
  332. imagestring($img, $font, $padding + $border + $font_width * $i, $padding + $border + mt_rand(0, $float_pixel), $code[$i], $color);
  333. }
  334. $filename = 'imgcode-' . mt_rand();
  335. ob_start();
  336. // 输出图像
  337. switch (strtolower($this->_options['image_type']))
  338. {
  339. case 'png':
  340. $filename .= '.png';
  341. $mime = image_type_to_mime_type(IMAGETYPE_PNG);
  342. imagepng($img);
  343. break;
  344. case 'gif':
  345. $filename .= '.gif';
  346. $mime = image_type_to_mime_type(IMAGETYPE_GIF);
  347. imagegif($img);
  348. break;
  349. case 'jpg':
  350. default:
  351. $filename .= '.jpg';
  352. $mime = image_type_to_mime_type(IMAGETYPE_JPEG);
  353. imagejpeg($img);
  354. }
  355. imagedestroy($img);
  356. unset($img);
  357. $output = new QView_Output($filename, $mime, ob_get_clean());
  358. $output
  359. ->contentDisposition('inline')
  360. ->enableClientCache(false);
  361. return $output;
  362. }
  363. }
  364. /**
  365. * Helper_ImgCodeTTF 使用 ttf 字体生成验证码
  366. *
  367. * @author YuLei Liao <liaoyulei@qeeyuan.com>
  368. * @version $Id: imgcode.php 2651 2009-08-30 17:14:42Z firzen $
  369. * @package helper
  370. */
  371. class Helper_ImgCodeTTF
  372. {
  373. /**
  374. * 生成验证码需要的选项
  375. *
  376. * @var array
  377. */
  378. protected $_options = array
  379. (
  380. // 验证码长度
  381. 'code_length' => 6,
  382. // 验证码图像宽度
  383. 'width' => 170,
  384. // 验证码图像高度
  385. 'height' => 90,
  386. // 字符上下浮动像素
  387. 'float_pixel' => 10,
  388. // 旋转角度
  389. 'max_angle' => 35,
  390. // 字体大小
  391. 'font_size' => 50,
  392. // 字体最低间距
  393. 'font_space' => 0,
  394. // 图像类型
  395. 'image_type' => IMAGETYPE_JPEG,
  396. // 字体,如果为 0-5,则使用 QeePHP 自带的字体
  397. // 如果使用自定义字体,font 必须指定为字体文件的绝对路径
  398. 'font' => 0,
  399. // 字体颜色
  400. 'color' => 'fff',
  401. // 背景色
  402. 'bgcolor' => '333',
  403. // 验证码图片边界颜色
  404. 'bdcolor' => 'ccc',
  405. // 内空
  406. 'padding' => 5,
  407. // 边界线
  408. 'border' => 1,
  409. );
  410. /**
  411. * 内置的字体列表
  412. *
  413. * @var array
  414. */
  415. static protected $_font_list = array
  416. (
  417. 'alpha_thin.ttf',
  418. );
  419. /**
  420. * 构造函数
  421. *
  422. * @param array $options
  423. * 生成验证码的选项
  424. */
  425. function __construct(array $options)
  426. {
  427. $this->_options = array_merge($this->_options, $options);
  428. }
  429. /**
  430. * 生成验证码
  431. *
  432. * @return string
  433. */
  434. function generateCode()
  435. {
  436. $code_length = intval($this->_options['code_length']);
  437. if ($code_length <= 0) { $code_length = 4; }
  438. $seed = '346789ABCDEFGHJKLMN346789PQRTUVWXYabcdefghj346789klmnpqrt346789uvwxy';
  439. $code = '';
  440. $len = strlen($seed) - 1;
  441. for ($i = 0; $i < $code_length; $i++)
  442. {
  443. $code .= substr($seed, mt_rand(0, $len), 1);
  444. }
  445. return $code;
  446. }
  447. /**
  448. * 生成验证码图像,返回 QView_Output 对象
  449. *
  450. * $param string $code
  451. *
  452. * @return QView_Output
  453. */
  454. function generateImage($code)
  455. {
  456. // 确定要使用的字体
  457. if (isset(self::$_font_list[$this->_options['font']]))
  458. {
  459. $font = Q_DIR . '/_resources/' . self::$_font_list[$this->_options['font']];
  460. }
  461. else
  462. {
  463. $font = $this->_options['font'];
  464. }
  465. // 确定图像的宽度和高度
  466. $border = $this->_options['border'];
  467. $float_pixel = intval($this->_options['float_pixel']);
  468. $padding = intval($this->_options['padding']);
  469. $width = intval($this->_options['width']) + $padding * 2 + $border * 2 + 1;
  470. $width *= 2;
  471. $height = intval($this->_options['height']) + $padding * 2 + $border * 2 + 1 + $float_pixel;
  472. // 创建图像
  473. $img = imagecreatetruecolor($width, $height);
  474. // 绘制背景
  475. list($r, $g, $b) = Helper_ImgCode::hex2rgb($this->_options['bgcolor']);
  476. imagefilledrectangle($img, $border, $border, $width - $border - 1, $height - $border - 1, imagecolorallocate($img, $r, $g, $b));
  477. // 绘制文字
  478. $max_angle = intval($this->_options['max_angle']);
  479. $x = $padding + $padding + $border;
  480. $bottom = $height - $padding + $border - $float_pixel * 2;
  481. $font_space = $this->_options['font_space'];
  482. $color_arr = Helper_ImgCode::hex2rgb($this->_options['color']);
  483. $arr = array();
  484. $font_size = $this->_options['font_size'];
  485. $min_r = $r + 50;
  486. $min_g = $g + 50;
  487. $min_b = $b + 50;
  488. for ($i = 0, $max = strlen($code); $i < $max; $i++)
  489. {
  490. $arr[$i]['font_size'] = (mt_rand(50, 150) / 100) * $font_size;
  491. $arr[$i]['angle'] = rand(0, $max_angle) - $max_angle / 2;
  492. list($r, $g, $b) = $color_arr;
  493. $r = ($r + rand(0, 2550)) % (255 - $min_r) + $min_r;
  494. $g = ($g + rand(0, 2550)) % (255 - $min_g) + $min_g;
  495. $b = ($b + rand(0, 2550)) % (255 - $min_b) + $min_b;
  496. $arr[$i]['color'] = imagecolorallocate($img, $r, $g, $b);
  497. }
  498. for ($i = 0; $i < $max; $i++)
  499. {
  500. $x += $font_space;
  501. $y = $bottom;
  502. list(,, $x) = imagettftext($img, $arr[$i]['font_size'], $arr[$i]['angle'], $x, $y, $arr[$i]['color'], $font, $code[$i]);
  503. imagecolordeallocate($img, $arr[$i]['color']);
  504. }
  505. $new_width = intval($this->_options['width']) + $padding * 2 + $border * 2 + 1;
  506. $img_output = imagecreatetruecolor($new_width, $height);
  507. imagecopyresampled($img_output, $img, 0, 0, 0, 0, $new_width, $height, $x + $padding + $border, $height);
  508. imagedestroy($img);
  509. // 绘制边框
  510. if ($border)
  511. {
  512. list($r, $g, $b) = Helper_ImgCode::hex2rgb($this->_options['bdcolor']);
  513. imagerectangle($img_output, 0, 0, $width, $height, imagecolorallocate($img_output, $r, $g, $b));
  514. }
  515. $filename = 'imgcode-' . mt_rand();
  516. ob_start();
  517. // 输出图像
  518. switch (strtolower($this->_options['image_type']))
  519. {
  520. case 'png':
  521. $filename .= '.png';
  522. $mime = image_type_to_mime_type(IMAGETYPE_PNG);
  523. imagepng($img_output);
  524. break;
  525. case 'gif':
  526. $filename .= '.gif';
  527. $mime = image_type_to_mime_type(IMAGETYPE_GIF);
  528. imagegif($img_output);
  529. break;
  530. case 'jpg':
  531. default:
  532. $filename .= '.jpg';
  533. $mime = image_type_to_mime_type(IMAGETYPE_JPEG);
  534. imagejpeg($img_output);
  535. }
  536. imagedestroy($img_output);
  537. $output = new QView_Output($filename, $mime, ob_get_clean());
  538. $output->enableClientCache(false);
  539. return $output;
  540. }
  541. }