PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/core/classes/captcha.class.php

https://github.com/lewisliud/myqee
PHP | 357 lines | 192 code | 52 blank | 113 comment | 23 complexity | 9b505cc0166fadc4847e64db5d1b88db MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * 缩略图对象
  4. *
  5. * @author 呼吸二氧化碳 <jonwang@myqee.com>
  6. * @category MyQEE
  7. * @package System
  8. * @subpackage Core
  9. * @copyright Copyright (c) 2008-2013 myqee.com
  10. * @license http://www.myqee.com/license.html
  11. */
  12. class Core_Captcha
  13. {
  14. /**
  15. * 输出类型
  16. *
  17. * 可以是png,gif,jpeg
  18. * @var string
  19. */
  20. protected static $image_type = 'png';
  21. // Config values
  22. public static $config = array
  23. (
  24. 'width' => 150,
  25. 'height' => 50,
  26. 'complexity' => 4,
  27. 'background' => '',
  28. 'fonts' => array
  29. (
  30. 'fonts/DejaVuSerif.ttf'
  31. ),
  32. 'promote' => false,
  33. 'life' => 1800
  34. );
  35. protected static $image;
  36. protected static $response = '';
  37. protected static $background_image;
  38. protected static $sessionname = '_img_captcha';
  39. protected static $valid_countname = '_img_captcha_valid_count';
  40. public static function valid($mycode, $delsession = false)
  41. {
  42. if ( !($code = Session::instance()->get(Captcha::$sessionname)) )
  43. {
  44. return 0;
  45. }
  46. else
  47. {
  48. if ( TIME - $code['time'] <= Captcha::$config['life'] && $code['time'] > 0 && strtoupper($mycode) == strtoupper($code['code']) )
  49. {
  50. if ( $delsession ) Session::instance()->delete(Captcha::$sessionname, Captcha::$valid_countname);
  51. return 1;
  52. }
  53. else
  54. {
  55. $errornum = (int)Session::instance()->get(Captcha::$valid_countname) + 1;
  56. Session::instance()->set(Captcha::$valid_countname, $errornum);
  57. return - $errornum;
  58. }
  59. }
  60. }
  61. /**
  62. * Gets or sets the number of valid Captcha responses for this session.
  63. *
  64. * @param integer new counter value
  65. * @param boolean trigger invalid counter (for internal use only)
  66. * @return integer counter value
  67. */
  68. public static function valid_count($new_count = null, $invalid = false)
  69. {
  70. // Pick the right session to use
  71. $session = Captcha::$valid_countname;
  72. // Update counter
  73. if (null!==$new_count)
  74. {
  75. $new_count = (int)$new_count;
  76. // Reset counter = delete session
  77. if ( $new_count < 1 )
  78. {
  79. Session::instance()->delete($session);
  80. }
  81. // Set counter to new value
  82. else
  83. {
  84. Session::instance()->set($session, (int)$new_count);
  85. }
  86. // Return new count
  87. return (int)$new_count;
  88. }
  89. // Return current count
  90. return (int)Session::instance()->get($session);
  91. }
  92. /**
  93. * Checks whether user has been promoted after having given enough valid responses.
  94. *
  95. * @param integer valid response count threshold
  96. * @return boolean
  97. */
  98. public static function promoted($threshold = null)
  99. {
  100. // Promotion has been disabled
  101. if (Captcha::$config['promote'] === false) return false;
  102. // Use the config threshold
  103. if ( $threshold === null )
  104. {
  105. $threshold = Captcha::$config['promote'];
  106. }
  107. // Compare the valid response count to the threshold
  108. return (Captcha::valid_count() >= $threshold);
  109. }
  110. /**
  111. * render image
  112. *
  113. * @param array $config
  114. * @return image
  115. */
  116. public static function render($config = false)
  117. {
  118. if (is_array($config))
  119. {
  120. Captcha::$config = array_merge(Captcha::$config, $config);
  121. }
  122. if (empty(Captcha::$response))
  123. {
  124. Captcha::generate_challenge();
  125. }
  126. // Creates Captcha::$image
  127. Captcha::image_create(Captcha::$config['background']);
  128. // Add a random gradient
  129. if ( empty(Captcha::$config['background']) )
  130. {
  131. $color1 = imagecolorallocate(Captcha::$image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));
  132. $color2 = imagecolorallocate(Captcha::$image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));
  133. Captcha::image_gradient($color1, $color2);
  134. }
  135. // Add a few random circles
  136. for( $i = 0, $count = mt_rand(10, Captcha::$config['complexity'] * 3); $i < $count; $i ++ )
  137. {
  138. $color = imagecolorallocatealpha(Captcha::$image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255), mt_rand(80, 120));
  139. $size = mt_rand(5, Captcha::$config['height'] / 3);
  140. imagefilledellipse(Captcha::$image, mt_rand(0, Captcha::$config['width']), mt_rand(0, Captcha::$config['height']), $size, $size, $color);
  141. }
  142. // Calculate character font-size and spacing
  143. $default_size = min(Captcha::$config['width'], Captcha::$config['height'] * 2) / strlen(Captcha::$response);
  144. $spacing = (int)(Captcha::$config['width'] * 0.9 / strlen(Captcha::$response));
  145. // Background alphabetic character attributes
  146. $color_limit = mt_rand(96, 160);
  147. $chars = 'ABEFGJKLPQRTVY';
  148. // Draw each captcha character with varying attributes
  149. for( $i = 0, $strlen = strlen(Captcha::$response); $i < $strlen; $i ++ )
  150. {
  151. // Use different fonts if available
  152. $font = Core::find_file('data',Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])],false);
  153. $angle = mt_rand(- 40, 20);
  154. // Scale the character size on image height
  155. $size = $default_size / 10 * mt_rand(8, 12);
  156. if ( !function_exists('imageftbbox') ) Core::show_500(__('function imageftbbox not exist.'));
  157. $box = imageftbbox($size, $angle, $font, Captcha::$response[$i]);
  158. // Calculate character starting coordinates
  159. $x = $spacing / 4 + $i * $spacing;
  160. $y = Captcha::$config['height'] / 2 + ($box[2] - $box[5]) / 4;
  161. // Draw captcha text character
  162. // Allocate random color, size and rotation attributes to text
  163. $color = imagecolorallocate(Captcha::$image, mt_rand(150, 255), mt_rand(200, 255), mt_rand(0, 255));
  164. // Write text character to image
  165. imagefttext(Captcha::$image, $size, $angle, $x, $y, $color, $font, Captcha::$response[$i]);
  166. // Draw "ghost" alphabetic character
  167. $text_color = imagecolorallocatealpha(Captcha::$image, mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand(70, 120));
  168. $char = substr($chars, mt_rand(0, 14), 1);
  169. imagettftext(Captcha::$image, $size * 1.4, mt_rand(- 45, 45), ($x - (mt_rand(5, 10))), ($y + (mt_rand(5, 10))), $text_color, $font, $char);
  170. }
  171. // Output
  172. return Captcha::image_render();
  173. }
  174. /**
  175. * Generates a new captcha challenge.
  176. *
  177. * @return string the challenge answer
  178. */
  179. protected static function generate_challenge()
  180. {
  181. // Complexity setting is used as character count
  182. Captcha::$response = Captcha::random(max(1, Captcha::$config['complexity']));
  183. Session::instance()->set(Captcha::$sessionname, array('code' => Captcha::$response, 'time' => TIME));
  184. }
  185. protected static function random($length = 8)
  186. {
  187. $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
  188. $str = '';
  189. $pool_size = strlen($pool);
  190. for( $i = 0; $i < $length; $i ++ )
  191. {
  192. $str .= substr($pool, mt_rand(0, $pool_size - 1), 1);
  193. }
  194. return $str;
  195. }
  196. /**
  197. * Creates an image resource with the dimensions specified in config.
  198. * If a background image is supplied, the image dimensions are used.
  199. *
  200. * @throws Kohana_Exception if no GD2 support
  201. * @param string path to the background image file
  202. * @return void
  203. */
  204. protected function image_create($background = null)
  205. {
  206. // Check for GD2 support
  207. if (!function_exists('imagegd2'))Core::show_500(__('captcha.requires_GD2'));
  208. // Create a new image (black)
  209. Captcha::$image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']);
  210. // Use a background image
  211. if ( !empty($background) )
  212. {
  213. /*
  214. // Create the image using the right function for the filetype
  215. $function = 'imagecreatefrom' . Captcha::image_type($filename);
  216. Captcha::$background_image = $function($background);
  217. // Resize the image if needed
  218. if ( imagesx(Captcha::background_image) !== Captcha::$config['width'] or imagesy(Captcha::background_image) !== Captcha::$config['height'] )
  219. {
  220. imagecopyresampled(Captcha::image, Captcha::background_image, 0, 0, 0, 0, Captcha::$config['width'], Captcha::$config['height'], imagesx(Captcha::background_image), imagesy(Captcha::background_image));
  221. }
  222. // Free up resources
  223. imagedestroy(Captcha::background_image);
  224. */
  225. }
  226. }
  227. /**
  228. * Fills the background with a gradient.
  229. *
  230. * @param resource gd image color identifier for start color
  231. * @param resource gd image color identifier for end color
  232. * @param string direction: 'horizontal' or 'vertical', 'random' by default
  233. * @return void
  234. */
  235. protected function image_gradient($color1, $color2, $direction = null)
  236. {
  237. $directions = array('horizontal', 'vertical');
  238. // Pick a random direction if needed
  239. if ( !in_array($direction, $directions) )
  240. {
  241. $direction = $directions[array_rand($directions)];
  242. // Switch colors
  243. if ( mt_rand(0, 1) === 1 )
  244. {
  245. $temp = $color1;
  246. $color1 = $color2;
  247. $color2 = $temp;
  248. }
  249. }
  250. // Extract RGB values
  251. $color1 = imagecolorsforindex(Captcha::$image, $color1);
  252. $color2 = imagecolorsforindex(Captcha::$image, $color2);
  253. // Preparations for the gradient loop
  254. $steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height'];
  255. $r1 = ($color1['red'] - $color2['red']) / $steps;
  256. $g1 = ($color1['green'] - $color2['green']) / $steps;
  257. $b1 = ($color1['blue'] - $color2['blue']) / $steps;
  258. $i = null;
  259. if ( $direction === 'horizontal' )
  260. {
  261. $x1 = & $i;
  262. $y1 = 0;
  263. $x2 = & $i;
  264. $y2 = Captcha::$config['height'];
  265. }
  266. else
  267. {
  268. $x1 = 0;
  269. $y1 = & $i;
  270. $x2 = Captcha::$config['width'];
  271. $y2 = & $i;
  272. }
  273. // Execute the gradient loop
  274. for( $i = 0; $i <= $steps; $i ++ )
  275. {
  276. $r2 = $color1['red'] - floor($i * $r1);
  277. $g2 = $color1['green'] - floor($i * $g1);
  278. $b2 = $color1['blue'] - floor($i * $b1);
  279. $color = imagecolorallocate(Captcha::$image, $r2, $g2, $b2);
  280. imageline(Captcha::$image, $x1, $y1, $x2, $y2, $color);
  281. }
  282. }
  283. /**
  284. * Returns the img html element or outputs the image to the browser.
  285. *
  286. * @param boolean html output
  287. * @return mixed html string or void
  288. */
  289. protected function image_render()
  290. {
  291. // Send the correct HTTP header
  292. header("Cache-Control:no-cache,must-revalidate");
  293. header("Pragma:no-cache");
  294. header('Content-Type: image/' . Captcha::$image_type);
  295. header("Connection:close");
  296. // Pick the correct output function
  297. $function = 'image'.Captcha::$image_type;
  298. $function(Captcha::$image);
  299. // Free up resources
  300. imagedestroy(Captcha::$image);
  301. }
  302. }