PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Captcha/Image.php

https://bitbucket.org/svenax/zendframework
PHP | 609 lines | 287 code | 48 blank | 274 comment | 46 complexity | 6b116fa1b1f9e84163532369d28c33f4 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Captcha
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /** @see Zend_Captcha_Word */
  23. require_once 'Zend/Captcha/Word.php';
  24. /**
  25. * Image-based captcha element
  26. *
  27. * Generates image displaying random word
  28. *
  29. * @category Zend
  30. * @package Zend_Captcha
  31. * @subpackage Adapter
  32. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_Captcha_Image extends Zend_Captcha_Word
  36. {
  37. /**
  38. * Directory for generated images
  39. *
  40. * @var string
  41. */
  42. protected $_imgDir = "./images/captcha/";
  43. /**
  44. * URL for accessing images
  45. *
  46. * @var string
  47. */
  48. protected $_imgUrl = "/images/captcha/";
  49. /**
  50. * Image's alt tag content
  51. *
  52. * @var string
  53. */
  54. protected $_imgAlt = "";
  55. /**
  56. * Image suffix (including dot)
  57. *
  58. * @var string
  59. */
  60. protected $_suffix = ".png";
  61. /**
  62. * Image width
  63. *
  64. * @var int
  65. */
  66. protected $_width = 200;
  67. /**
  68. * Image height
  69. *
  70. * @var int
  71. */
  72. protected $_height = 50;
  73. /**
  74. * Font size
  75. *
  76. * @var int
  77. */
  78. protected $_fsize = 24;
  79. /**
  80. * Image font file
  81. *
  82. * @var string
  83. */
  84. protected $_font;
  85. /**
  86. * Image to use as starting point
  87. * Default is blank image. If provided, should be PNG image.
  88. *
  89. * @var string
  90. */
  91. protected $_startImage;
  92. /**
  93. * How frequently to execute garbage collection
  94. *
  95. * @var int
  96. */
  97. protected $_gcFreq = 10;
  98. /**
  99. * How long to keep generated images
  100. *
  101. * @var int
  102. */
  103. protected $_expiration = 600;
  104. /**
  105. * Number of noise dots on image
  106. * Used twice - before and after transform
  107. *
  108. * @var int
  109. */
  110. protected $_dotNoiseLevel = 100;
  111. /**
  112. * Number of noise lines on image
  113. * Used twice - before and after transform
  114. *
  115. * @var int
  116. */
  117. protected $_lineNoiseLevel = 5;
  118. /**
  119. * @return string
  120. */
  121. public function getImgAlt ()
  122. {
  123. return $this->_imgAlt;
  124. }
  125. /**
  126. * @return string
  127. */
  128. public function getStartImage ()
  129. {
  130. return $this->_startImage;
  131. }
  132. /**
  133. * @return int
  134. */
  135. public function getDotNoiseLevel ()
  136. {
  137. return $this->_dotNoiseLevel;
  138. }
  139. /**
  140. * @return int
  141. */
  142. public function getLineNoiseLevel ()
  143. {
  144. return $this->_lineNoiseLevel;
  145. }
  146. /**
  147. * Get captcha expiration
  148. *
  149. * @return int
  150. */
  151. public function getExpiration()
  152. {
  153. return $this->_expiration;
  154. }
  155. /**
  156. * Get garbage collection frequency
  157. *
  158. * @return int
  159. */
  160. public function getGcFreq()
  161. {
  162. return $this->_gcFreq;
  163. }
  164. /**
  165. * Get font to use when generating captcha
  166. *
  167. * @return string
  168. */
  169. public function getFont()
  170. {
  171. return $this->_font;
  172. }
  173. /**
  174. * Get font size
  175. *
  176. * @return int
  177. */
  178. public function getFontSize()
  179. {
  180. return $this->_fsize;
  181. }
  182. /**
  183. * Get captcha image height
  184. *
  185. * @return int
  186. */
  187. public function getHeight()
  188. {
  189. return $this->_height;
  190. }
  191. /**
  192. * Get captcha image directory
  193. *
  194. * @return string
  195. */
  196. public function getImgDir()
  197. {
  198. return $this->_imgDir;
  199. }
  200. /**
  201. * Get captcha image base URL
  202. *
  203. * @return string
  204. */
  205. public function getImgUrl()
  206. {
  207. return $this->_imgUrl;
  208. }
  209. /**
  210. * Get captcha image file suffix
  211. *
  212. * @return string
  213. */
  214. public function getSuffix()
  215. {
  216. return $this->_suffix;
  217. }
  218. /**
  219. * Get captcha image width
  220. *
  221. * @return int
  222. */
  223. public function getWidth()
  224. {
  225. return $this->_width;
  226. }
  227. /**
  228. * @param string $startImage
  229. */
  230. public function setStartImage ($startImage)
  231. {
  232. $this->_startImage = $startImage;
  233. return $this;
  234. }
  235. /**
  236. * @param int $dotNoiseLevel
  237. */
  238. public function setDotNoiseLevel ($dotNoiseLevel)
  239. {
  240. $this->_dotNoiseLevel = $dotNoiseLevel;
  241. return $this;
  242. }
  243. /**
  244. * @param int $lineNoiseLevel
  245. */
  246. public function setLineNoiseLevel ($lineNoiseLevel)
  247. {
  248. $this->_lineNoiseLevel = $lineNoiseLevel;
  249. return $this;
  250. }
  251. /**
  252. * Set captcha expiration
  253. *
  254. * @param int $expiration
  255. * @return Zend_Captcha_Image
  256. */
  257. public function setExpiration($expiration)
  258. {
  259. $this->_expiration = $expiration;
  260. return $this;
  261. }
  262. /**
  263. * Set garbage collection frequency
  264. *
  265. * @param int $gcFreq
  266. * @return Zend_Captcha_Image
  267. */
  268. public function setGcFreq($gcFreq)
  269. {
  270. $this->_gcFreq = $gcFreq;
  271. return $this;
  272. }
  273. /**
  274. * Set captcha font
  275. *
  276. * @param string $font
  277. * @return Zend_Captcha_Image
  278. */
  279. public function setFont($font)
  280. {
  281. $this->_font = $font;
  282. return $this;
  283. }
  284. /**
  285. * Set captcha font size
  286. *
  287. * @param int $fsize
  288. * @return Zend_Captcha_Image
  289. */
  290. public function setFontSize($fsize)
  291. {
  292. $this->_fsize = $fsize;
  293. return $this;
  294. }
  295. /**
  296. * Set captcha image height
  297. *
  298. * @param int $height
  299. * @return Zend_Captcha_Image
  300. */
  301. public function setHeight($height)
  302. {
  303. $this->_height = $height;
  304. return $this;
  305. }
  306. /**
  307. * Set captcha image storage directory
  308. *
  309. * @param string $imgDir
  310. * @return Zend_Captcha_Image
  311. */
  312. public function setImgDir($imgDir)
  313. {
  314. $this->_imgDir = rtrim($imgDir, "/\\") . '/';
  315. return $this;
  316. }
  317. /**
  318. * Set captcha image base URL
  319. *
  320. * @param string $imgUrl
  321. * @return Zend_Captcha_Image
  322. */
  323. public function setImgUrl($imgUrl)
  324. {
  325. $this->_imgUrl = rtrim($imgUrl, "/\\") . '/';
  326. return $this;
  327. }
  328. /**
  329. * @param string $imgAlt
  330. */
  331. public function setImgAlt ($imgAlt)
  332. {
  333. $this->_imgAlt = $imgAlt;
  334. return $this;
  335. }
  336. /**
  337. * Set captch image filename suffix
  338. *
  339. * @param string $suffix
  340. * @return Zend_Captcha_Image
  341. */
  342. public function setSuffix($suffix)
  343. {
  344. $this->_suffix = $suffix;
  345. return $this;
  346. }
  347. /**
  348. * Set captcha image width
  349. *
  350. * @param int $width
  351. * @return Zend_Captcha_Image
  352. */
  353. public function setWidth($width)
  354. {
  355. $this->_width = $width;
  356. return $this;
  357. }
  358. /**
  359. * Generate random frequency
  360. *
  361. * @return float
  362. */
  363. protected function _randomFreq()
  364. {
  365. return mt_rand(700000, 1000000) / 15000000;
  366. }
  367. /**
  368. * Generate random phase
  369. *
  370. * @return float
  371. */
  372. protected function _randomPhase()
  373. {
  374. // random phase from 0 to pi
  375. return mt_rand(0, 3141592) / 1000000;
  376. }
  377. /**
  378. * Generate random character size
  379. *
  380. * @return int
  381. */
  382. protected function _randomSize()
  383. {
  384. return mt_rand(300, 700) / 100;
  385. }
  386. /**
  387. * Generate captcha
  388. *
  389. * @return string captcha ID
  390. */
  391. public function generate()
  392. {
  393. $id = parent::generate();
  394. $tries = 5;
  395. // If there's already such file, try creating a new ID
  396. while($tries-- && file_exists($this->getImgDir() . $id . $this->getSuffix())) {
  397. $id = $this->_generateRandomId();
  398. $this->_setId($id);
  399. }
  400. $this->_generateImage($id, $this->getWord());
  401. if (mt_rand(1, $this->getGcFreq()) == 1) {
  402. $this->_gc();
  403. }
  404. return $id;
  405. }
  406. /**
  407. * Generate image captcha
  408. *
  409. * Override this function if you want different image generator
  410. * Wave transform from http://www.captcha.ru/captchas/multiwave/
  411. *
  412. * @param string $id Captcha ID
  413. * @param string $word Captcha word
  414. */
  415. protected function _generateImage($id, $word)
  416. {
  417. if (!extension_loaded("gd")) {
  418. require_once 'Zend/Captcha/Exception.php';
  419. throw new Zend_Captcha_Exception("Image CAPTCHA requires GD extension");
  420. }
  421. if (!function_exists("imagepng")) {
  422. require_once 'Zend/Captcha/Exception.php';
  423. throw new Zend_Captcha_Exception("Image CAPTCHA requires PNG support");
  424. }
  425. if (!function_exists("imageftbbox")) {
  426. require_once 'Zend/Captcha/Exception.php';
  427. throw new Zend_Captcha_Exception("Image CAPTCHA requires FT fonts support");
  428. }
  429. $font = $this->getFont();
  430. if (empty($font)) {
  431. require_once 'Zend/Captcha/Exception.php';
  432. throw new Zend_Captcha_Exception("Image CAPTCHA requires font");
  433. }
  434. $w = $this->getWidth();
  435. $h = $this->getHeight();
  436. $fsize = $this->getFontSize();
  437. $img_file = $this->getImgDir() . $id . $this->getSuffix();
  438. if(empty($this->_startImage)) {
  439. $img = imagecreatetruecolor($w, $h);
  440. } else {
  441. $img = imagecreatefrompng($this->_startImage);
  442. if(!$img) {
  443. require_once 'Zend/Captcha/Exception.php';
  444. throw new Zend_Captcha_Exception("Can not load start image");
  445. }
  446. $w = imagesx($img);
  447. $h = imagesy($img);
  448. }
  449. $text_color = imagecolorallocate($img, 0, 0, 0);
  450. $bg_color = imagecolorallocate($img, 255, 255, 255);
  451. imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bg_color);
  452. $textbox = imageftbbox($fsize, 0, $font, $word);
  453. $x = ($w - ($textbox[2] - $textbox[0])) / 2;
  454. $y = ($h - ($textbox[7] - $textbox[1])) / 2;
  455. imagefttext($img, $fsize, 0, $x, $y, $text_color, $font, $word);
  456. // generate noise
  457. for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
  458. imagefilledellipse($img, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
  459. }
  460. for($i=0; $i<$this->_lineNoiseLevel; $i++) {
  461. imageline($img, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
  462. }
  463. // transformed image
  464. $img2 = imagecreatetruecolor($w, $h);
  465. $bg_color = imagecolorallocate($img2, 255, 255, 255);
  466. imagefilledrectangle($img2, 0, 0, $w-1, $h-1, $bg_color);
  467. // apply wave transforms
  468. $freq1 = $this->_randomFreq();
  469. $freq2 = $this->_randomFreq();
  470. $freq3 = $this->_randomFreq();
  471. $freq4 = $this->_randomFreq();
  472. $ph1 = $this->_randomPhase();
  473. $ph2 = $this->_randomPhase();
  474. $ph3 = $this->_randomPhase();
  475. $ph4 = $this->_randomPhase();
  476. $szx = $this->_randomSize();
  477. $szy = $this->_randomSize();
  478. for ($x = 0; $x < $w; $x++) {
  479. for ($y = 0; $y < $h; $y++) {
  480. $sx = $x + (sin($x*$freq1 + $ph1) + sin($y*$freq3 + $ph3)) * $szx;
  481. $sy = $y + (sin($x*$freq2 + $ph2) + sin($y*$freq4 + $ph4)) * $szy;
  482. if ($sx < 0 || $sy < 0 || $sx >= $w - 1 || $sy >= $h - 1) {
  483. continue;
  484. } else {
  485. $color = (imagecolorat($img, $sx, $sy) >> 16) & 0xFF;
  486. $color_x = (imagecolorat($img, $sx + 1, $sy) >> 16) & 0xFF;
  487. $color_y = (imagecolorat($img, $sx, $sy + 1) >> 16) & 0xFF;
  488. $color_xy = (imagecolorat($img, $sx + 1, $sy + 1) >> 16) & 0xFF;
  489. }
  490. if ($color == 255 && $color_x == 255 && $color_y == 255 && $color_xy == 255) {
  491. // ignore background
  492. continue;
  493. } elseif ($color == 0 && $color_x == 0 && $color_y == 0 && $color_xy == 0) {
  494. // transfer inside of the image as-is
  495. $newcolor = 0;
  496. } else {
  497. // do antialiasing for border items
  498. $frac_x = $sx-floor($sx);
  499. $frac_y = $sy-floor($sy);
  500. $frac_x1 = 1-$frac_x;
  501. $frac_y1 = 1-$frac_y;
  502. $newcolor = $color * $frac_x1 * $frac_y1
  503. + $color_x * $frac_x * $frac_y1
  504. + $color_y * $frac_x1 * $frac_y
  505. + $color_xy * $frac_x * $frac_y;
  506. }
  507. imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newcolor, $newcolor, $newcolor));
  508. }
  509. }
  510. // generate noise
  511. for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
  512. imagefilledellipse($img2, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
  513. }
  514. for ($i=0; $i<$this->_lineNoiseLevel; $i++) {
  515. imageline($img2, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
  516. }
  517. imagepng($img2, $img_file);
  518. imagedestroy($img);
  519. imagedestroy($img2);
  520. }
  521. /**
  522. * Remove old files from image directory
  523. *
  524. */
  525. protected function _gc()
  526. {
  527. $expire = time() - $this->getExpiration();
  528. $imgdir = $this->getImgDir();
  529. if(!$imgdir || strlen($imgdir) < 2) {
  530. // safety guard
  531. return;
  532. }
  533. $suffixLength = strlen($this->_suffix);
  534. foreach (new DirectoryIterator($imgdir) as $file) {
  535. if (!$file->isDot() && !$file->isDir()) {
  536. if ($file->getMTime() < $expire) {
  537. // only deletes files ending with $this->_suffix
  538. if (substr($file->getFilename(), -($suffixLength)) == $this->_suffix) {
  539. unlink($file->getPathname());
  540. }
  541. }
  542. }
  543. }
  544. }
  545. /**
  546. * Display the captcha
  547. *
  548. * @param Zend_View_Interface $view
  549. * @param mixed $element
  550. * @return string
  551. */
  552. public function render(Zend_View_Interface $view = null, $element = null)
  553. {
  554. $endTag = ' />';
  555. if (($view instanceof Zend_View_Abstract) && !$view->doctype()->isXhtml()) {
  556. $endTag = '>';
  557. }
  558. return '<img width="' . $this->getWidth() . '" height="' . $this->getHeight() . '" alt="' . $this->getImgAlt()
  559. . '" src="' . $this->getImgUrl() . $this->getId() . $this->getSuffix() . '"' . $endTag;
  560. }
  561. }