File indexing completed on 2024-12-22 05:36:30
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Captcha 0017 * @subpackage Adapter 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** @see Zend_Captcha_Word */ 0024 // require_once 'Zend/Captcha/Word.php'; 0025 0026 /** 0027 * Image-based captcha element 0028 * 0029 * Generates image displaying random word 0030 * 0031 * @category Zend 0032 * @package Zend_Captcha 0033 * @subpackage Adapter 0034 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0035 * @license http://framework.zend.com/license/new-bsd New BSD License 0036 */ 0037 class Zend_Captcha_Image extends Zend_Captcha_Word 0038 { 0039 /** 0040 * Directory for generated images 0041 * 0042 * @var string 0043 */ 0044 protected $_imgDir = "./images/captcha/"; 0045 0046 /** 0047 * URL for accessing images 0048 * 0049 * @var string 0050 */ 0051 protected $_imgUrl = "/images/captcha/"; 0052 0053 /** 0054 * Image's alt tag content 0055 * 0056 * @var string 0057 */ 0058 protected $_imgAlt = ""; 0059 0060 /** 0061 * Image suffix (including dot) 0062 * 0063 * @var string 0064 */ 0065 protected $_suffix = ".png"; 0066 0067 /** 0068 * Image width 0069 * 0070 * @var int 0071 */ 0072 protected $_width = 200; 0073 0074 /** 0075 * Image height 0076 * 0077 * @var int 0078 */ 0079 protected $_height = 50; 0080 0081 /** 0082 * Font size 0083 * 0084 * @var int 0085 */ 0086 protected $_fsize = 24; 0087 0088 /** 0089 * Image font file 0090 * 0091 * @var string 0092 */ 0093 protected $_font; 0094 0095 /** 0096 * Image to use as starting point 0097 * Default is blank image. If provided, should be PNG image. 0098 * 0099 * @var string 0100 */ 0101 protected $_startImage; 0102 /** 0103 * How frequently to execute garbage collection 0104 * 0105 * @var int 0106 */ 0107 protected $_gcFreq = 10; 0108 0109 /** 0110 * How long to keep generated images 0111 * 0112 * @var int 0113 */ 0114 protected $_expiration = 600; 0115 0116 /** 0117 * Number of noise dots on image 0118 * Used twice - before and after transform 0119 * 0120 * @var int 0121 */ 0122 protected $_dotNoiseLevel = 100; 0123 /** 0124 * Number of noise lines on image 0125 * Used twice - before and after transform 0126 * 0127 * @var int 0128 */ 0129 protected $_lineNoiseLevel = 5; 0130 /** 0131 * @return string 0132 */ 0133 public function getImgAlt () 0134 { 0135 return $this->_imgAlt; 0136 } 0137 /** 0138 * @return string 0139 */ 0140 public function getStartImage () 0141 { 0142 return $this->_startImage; 0143 } 0144 /** 0145 * @return int 0146 */ 0147 public function getDotNoiseLevel () 0148 { 0149 return $this->_dotNoiseLevel; 0150 } 0151 /** 0152 * @return int 0153 */ 0154 public function getLineNoiseLevel () 0155 { 0156 return $this->_lineNoiseLevel; 0157 } 0158 /** 0159 * Get captcha expiration 0160 * 0161 * @return int 0162 */ 0163 public function getExpiration() 0164 { 0165 return $this->_expiration; 0166 } 0167 0168 /** 0169 * Get garbage collection frequency 0170 * 0171 * @return int 0172 */ 0173 public function getGcFreq() 0174 { 0175 return $this->_gcFreq; 0176 } 0177 /** 0178 * Get font to use when generating captcha 0179 * 0180 * @return string 0181 */ 0182 public function getFont() 0183 { 0184 return $this->_font; 0185 } 0186 0187 /** 0188 * Get font size 0189 * 0190 * @return int 0191 */ 0192 public function getFontSize() 0193 { 0194 return $this->_fsize; 0195 } 0196 0197 /** 0198 * Get captcha image height 0199 * 0200 * @return int 0201 */ 0202 public function getHeight() 0203 { 0204 return $this->_height; 0205 } 0206 0207 /** 0208 * Get captcha image directory 0209 * 0210 * @return string 0211 */ 0212 public function getImgDir() 0213 { 0214 return $this->_imgDir; 0215 } 0216 /** 0217 * Get captcha image base URL 0218 * 0219 * @return string 0220 */ 0221 public function getImgUrl() 0222 { 0223 return $this->_imgUrl; 0224 } 0225 /** 0226 * Get captcha image file suffix 0227 * 0228 * @return string 0229 */ 0230 public function getSuffix() 0231 { 0232 return $this->_suffix; 0233 } 0234 /** 0235 * Get captcha image width 0236 * 0237 * @return int 0238 */ 0239 public function getWidth() 0240 { 0241 return $this->_width; 0242 } 0243 0244 /** 0245 * Set start image 0246 * 0247 * @param string $startImage 0248 * @return Zend_Captcha_Image 0249 */ 0250 public function setStartImage ($startImage) 0251 { 0252 $this->_startImage = $startImage; 0253 return $this; 0254 } 0255 0256 /** 0257 * Set dot noise level 0258 * 0259 * @param int $dotNoiseLevel 0260 * @return Zend_Captcha_Image 0261 */ 0262 public function setDotNoiseLevel ($dotNoiseLevel) 0263 { 0264 $this->_dotNoiseLevel = $dotNoiseLevel; 0265 return $this; 0266 } 0267 0268 /** 0269 * Set line noise level 0270 * 0271 * @param int $lineNoiseLevel 0272 * @return Zend_Captcha_Image 0273 */ 0274 public function setLineNoiseLevel ($lineNoiseLevel) 0275 { 0276 $this->_lineNoiseLevel = $lineNoiseLevel; 0277 return $this; 0278 } 0279 0280 /** 0281 * Set captcha expiration 0282 * 0283 * @param int $expiration 0284 * @return Zend_Captcha_Image 0285 */ 0286 public function setExpiration($expiration) 0287 { 0288 $this->_expiration = $expiration; 0289 return $this; 0290 } 0291 0292 /** 0293 * Set garbage collection frequency 0294 * 0295 * @param int $gcFreq 0296 * @return Zend_Captcha_Image 0297 */ 0298 public function setGcFreq($gcFreq) 0299 { 0300 $this->_gcFreq = $gcFreq; 0301 return $this; 0302 } 0303 0304 /** 0305 * Set captcha font 0306 * 0307 * @param string $font 0308 * @return Zend_Captcha_Image 0309 */ 0310 public function setFont($font) 0311 { 0312 $this->_font = $font; 0313 return $this; 0314 } 0315 0316 /** 0317 * Set captcha font size 0318 * 0319 * @param int $fsize 0320 * @return Zend_Captcha_Image 0321 */ 0322 public function setFontSize($fsize) 0323 { 0324 $this->_fsize = $fsize; 0325 return $this; 0326 } 0327 0328 /** 0329 * Set captcha image height 0330 * 0331 * @param int $height 0332 * @return Zend_Captcha_Image 0333 */ 0334 public function setHeight($height) 0335 { 0336 $this->_height = $height; 0337 return $this; 0338 } 0339 0340 /** 0341 * Set captcha image storage directory 0342 * 0343 * @param string $imgDir 0344 * @return Zend_Captcha_Image 0345 */ 0346 public function setImgDir($imgDir) 0347 { 0348 $this->_imgDir = rtrim($imgDir, "/\\") . '/'; 0349 return $this; 0350 } 0351 0352 /** 0353 * Set captcha image base URL 0354 * 0355 * @param string $imgUrl 0356 * @return Zend_Captcha_Image 0357 */ 0358 public function setImgUrl($imgUrl) 0359 { 0360 $this->_imgUrl = rtrim($imgUrl, "/\\") . '/'; 0361 return $this; 0362 } 0363 0364 /** 0365 * Set image alternative text 0366 * 0367 * @param string $imgAlt 0368 * @return Zend_Captcha_Image 0369 */ 0370 public function setImgAlt ($imgAlt) 0371 { 0372 $this->_imgAlt = $imgAlt; 0373 return $this; 0374 } 0375 0376 /** 0377 * Set captch image filename suffix 0378 * 0379 * @param string $suffix 0380 * @return Zend_Captcha_Image 0381 */ 0382 public function setSuffix($suffix) 0383 { 0384 $this->_suffix = $suffix; 0385 return $this; 0386 } 0387 0388 /** 0389 * Set captcha image width 0390 * 0391 * @param int $width 0392 * @return Zend_Captcha_Image 0393 */ 0394 public function setWidth($width) 0395 { 0396 $this->_width = $width; 0397 return $this; 0398 } 0399 0400 /** 0401 * Generate random frequency 0402 * 0403 * @return float 0404 */ 0405 protected function _randomFreq() 0406 { 0407 return mt_rand(700000, 1000000) / 15000000; 0408 } 0409 0410 /** 0411 * Generate random phase 0412 * 0413 * @return float 0414 */ 0415 protected function _randomPhase() 0416 { 0417 // random phase from 0 to pi 0418 return mt_rand(0, 3141592) / 1000000; 0419 } 0420 0421 /** 0422 * Generate random character size 0423 * 0424 * @return int 0425 */ 0426 protected function _randomSize() 0427 { 0428 return mt_rand(300, 700) / 100; 0429 } 0430 0431 /** 0432 * Generate captcha 0433 * 0434 * @return string captcha ID 0435 */ 0436 public function generate() 0437 { 0438 $id = parent::generate(); 0439 $tries = 5; 0440 // If there's already such file, try creating a new ID 0441 while($tries-- && file_exists($this->getImgDir() . $id . $this->getSuffix())) { 0442 $id = $this->_generateRandomId(); 0443 $this->_setId($id); 0444 } 0445 $this->_generateImage($id, $this->getWord()); 0446 0447 if (mt_rand(1, $this->getGcFreq()) == 1) { 0448 $this->_gc(); 0449 } 0450 return $id; 0451 } 0452 0453 /** 0454 * Generate image captcha 0455 * 0456 * Override this function if you want different image generator 0457 * Wave transform from http://www.captcha.ru/captchas/multiwave/ 0458 * 0459 * @param string $id Captcha ID 0460 * @param string $word Captcha word 0461 * @throws Zend_Captcha_Exception 0462 */ 0463 protected function _generateImage($id, $word) 0464 { 0465 if (!extension_loaded("gd")) { 0466 // require_once 'Zend/Captcha/Exception.php'; 0467 throw new Zend_Captcha_Exception("Image CAPTCHA requires GD extension"); 0468 } 0469 0470 if (!function_exists("imagepng")) { 0471 // require_once 'Zend/Captcha/Exception.php'; 0472 throw new Zend_Captcha_Exception("Image CAPTCHA requires PNG support"); 0473 } 0474 0475 if (!function_exists("imageftbbox")) { 0476 // require_once 'Zend/Captcha/Exception.php'; 0477 throw new Zend_Captcha_Exception("Image CAPTCHA requires FT fonts support"); 0478 } 0479 0480 $font = $this->getFont(); 0481 0482 if (empty($font)) { 0483 // require_once 'Zend/Captcha/Exception.php'; 0484 throw new Zend_Captcha_Exception("Image CAPTCHA requires font"); 0485 } 0486 0487 $w = $this->getWidth(); 0488 $h = $this->getHeight(); 0489 $fsize = $this->getFontSize(); 0490 0491 $img_file = $this->getImgDir() . $id . $this->getSuffix(); 0492 if(empty($this->_startImage)) { 0493 $img = imagecreatetruecolor($w, $h); 0494 } else { 0495 $img = imagecreatefrompng($this->_startImage); 0496 if(!$img) { 0497 // require_once 'Zend/Captcha/Exception.php'; 0498 throw new Zend_Captcha_Exception("Can not load start image"); 0499 } 0500 $w = imagesx($img); 0501 $h = imagesy($img); 0502 } 0503 $text_color = imagecolorallocate($img, 0, 0, 0); 0504 $bg_color = imagecolorallocate($img, 255, 255, 255); 0505 imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bg_color); 0506 $textbox = imageftbbox($fsize, 0, $font, $word); 0507 $x = ($w - ($textbox[2] - $textbox[0])) / 2; 0508 $y = ($h - ($textbox[7] - $textbox[1])) / 2; 0509 imagefttext($img, $fsize, 0, $x, $y, $text_color, $font, $word); 0510 0511 // generate noise 0512 for ($i=0; $i<$this->_dotNoiseLevel; $i++) { 0513 imagefilledellipse($img, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color); 0514 } 0515 for($i=0; $i<$this->_lineNoiseLevel; $i++) { 0516 imageline($img, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color); 0517 } 0518 0519 // transformed image 0520 $img2 = imagecreatetruecolor($w, $h); 0521 $bg_color = imagecolorallocate($img2, 255, 255, 255); 0522 imagefilledrectangle($img2, 0, 0, $w-1, $h-1, $bg_color); 0523 // apply wave transforms 0524 $freq1 = $this->_randomFreq(); 0525 $freq2 = $this->_randomFreq(); 0526 $freq3 = $this->_randomFreq(); 0527 $freq4 = $this->_randomFreq(); 0528 0529 $ph1 = $this->_randomPhase(); 0530 $ph2 = $this->_randomPhase(); 0531 $ph3 = $this->_randomPhase(); 0532 $ph4 = $this->_randomPhase(); 0533 0534 $szx = $this->_randomSize(); 0535 $szy = $this->_randomSize(); 0536 0537 for ($x = 0; $x < $w; $x++) { 0538 for ($y = 0; $y < $h; $y++) { 0539 $sx = $x + (sin($x*$freq1 + $ph1) + sin($y*$freq3 + $ph3)) * $szx; 0540 $sy = $y + (sin($x*$freq2 + $ph2) + sin($y*$freq4 + $ph4)) * $szy; 0541 0542 if ($sx < 0 || $sy < 0 || $sx >= $w - 1 || $sy >= $h - 1) { 0543 continue; 0544 } else { 0545 $color = (imagecolorat($img, $sx, $sy) >> 16) & 0xFF; 0546 $color_x = (imagecolorat($img, $sx + 1, $sy) >> 16) & 0xFF; 0547 $color_y = (imagecolorat($img, $sx, $sy + 1) >> 16) & 0xFF; 0548 $color_xy = (imagecolorat($img, $sx + 1, $sy + 1) >> 16) & 0xFF; 0549 } 0550 if ($color == 255 && $color_x == 255 && $color_y == 255 && $color_xy == 255) { 0551 // ignore background 0552 continue; 0553 } elseif ($color == 0 && $color_x == 0 && $color_y == 0 && $color_xy == 0) { 0554 // transfer inside of the image as-is 0555 $newcolor = 0; 0556 } else { 0557 // do antialiasing for border items 0558 $frac_x = $sx-floor($sx); 0559 $frac_y = $sy-floor($sy); 0560 $frac_x1 = 1-$frac_x; 0561 $frac_y1 = 1-$frac_y; 0562 0563 $newcolor = $color * $frac_x1 * $frac_y1 0564 + $color_x * $frac_x * $frac_y1 0565 + $color_y * $frac_x1 * $frac_y 0566 + $color_xy * $frac_x * $frac_y; 0567 } 0568 imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newcolor, $newcolor, $newcolor)); 0569 } 0570 } 0571 0572 // generate noise 0573 for ($i=0; $i<$this->_dotNoiseLevel; $i++) { 0574 imagefilledellipse($img2, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color); 0575 } 0576 for ($i=0; $i<$this->_lineNoiseLevel; $i++) { 0577 imageline($img2, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color); 0578 } 0579 0580 imagepng($img2, $img_file); 0581 imagedestroy($img); 0582 imagedestroy($img2); 0583 } 0584 0585 /** 0586 * Remove old files from image directory 0587 */ 0588 protected function _gc() 0589 { 0590 $expire = time() - $this->getExpiration(); 0591 $imgdir = $this->getImgDir(); 0592 if(!$imgdir || strlen($imgdir) < 2) { 0593 // safety guard 0594 return; 0595 } 0596 $suffixLength = strlen($this->_suffix); 0597 foreach (new DirectoryIterator($imgdir) as $file) { 0598 if (!$file->isDot() && !$file->isDir()) { 0599 if (file_exists($file->getPathname()) && $file->getMTime() < $expire) { 0600 // only deletes files ending with $this->_suffix 0601 if (substr($file->getFilename(), -($suffixLength)) == $this->_suffix) { 0602 unlink($file->getPathname()); 0603 } 0604 } 0605 } 0606 } 0607 } 0608 0609 /** 0610 * Display the captcha 0611 * 0612 * @param Zend_View_Interface $view 0613 * @param mixed $element 0614 * @return string 0615 */ 0616 public function render(Zend_View_Interface $view = null, $element = null) 0617 { 0618 $endTag = ' />'; 0619 if (($view instanceof Zend_View_Abstract) && !$view->doctype()->isXhtml()) { 0620 $endTag = '>'; 0621 } 0622 return '<img width="' . $this->getWidth() . '" height="' . $this->getHeight() . '" alt="' . $this->getImgAlt() 0623 . '" src="' . $this->getImgUrl() . $this->getId() . $this->getSuffix() . '"' . $endTag; 0624 } 0625 }