imgAlt; } /** * @return string */ public function getStartImage() { return $this->startImage; } /** * @return int */ public function getDotNoiseLevel() { return $this->dotNoiseLevel; } /** * @return int */ public function getLineNoiseLevel() { return $this->lineNoiseLevel; } /** * Get captcha expiration * * @return int */ public function getExpiration() { return $this->expiration; } /** * Get garbage collection frequency * * @return int */ public function getGcFreq() { return $this->gcFreq; } /** * Get font to use when generating captcha * * @return string */ public function getFont() { return $this->font; } /** * Get font size * * @return int */ public function getFontSize() { return $this->fsize; } /** * Get captcha image height * * @return int */ public function getHeight() { return $this->height; } /** * Get captcha image directory * * @return string */ public function getImgDir() { return $this->imgDir; } /** * Get captcha image base URL * * @return string */ public function getImgUrl() { return $this->imgUrl; } /** * Get captcha image file suffix * * @return string */ public function getSuffix() { return $this->suffix; } /** * Get captcha image width * * @return int */ public function getWidth() { return $this->width; } /** * @param string $startImage * @return Image */ public function setStartImage($startImage) { $this->startImage = $startImage; return $this; } /** * @param int $dotNoiseLevel * @return Image */ public function setDotNoiseLevel($dotNoiseLevel) { $this->dotNoiseLevel = $dotNoiseLevel; return $this; } /** * @param int $lineNoiseLevel * @return Image */ public function setLineNoiseLevel($lineNoiseLevel) { $this->lineNoiseLevel = $lineNoiseLevel; return $this; } /** * Set captcha expiration * * @param int $expiration * @return Image */ public function setExpiration($expiration) { $this->expiration = $expiration; return $this; } /** * Set garbage collection frequency * * @param int $gcFreq * @return Image */ public function setGcFreq($gcFreq) { $this->gcFreq = $gcFreq; return $this; } /** * Set captcha font * * @param string $font * @return Image */ public function setFont($font) { $this->font = $font; return $this; } /** * Set captcha font size * * @param int $fsize * @return Image */ public function setFontSize($fsize) { $this->fsize = $fsize; return $this; } /** * Set captcha image height * * @param int $height * @return Image */ public function setHeight($height) { $this->height = $height; return $this; } /** * Set captcha image storage directory * * @param string $imgDir * @return Image */ public function setImgDir($imgDir) { $this->imgDir = rtrim($imgDir, "/\\") . '/'; return $this; } /** * Set captcha image base URL * * @param string $imgUrl * @return Image */ public function setImgUrl($imgUrl) { $this->imgUrl = rtrim($imgUrl, "/\\") . '/'; return $this; } /** * @param string $imgAlt * @return Image */ public function setImgAlt($imgAlt) { $this->imgAlt = $imgAlt; return $this; } /** * Set captcha image filename suffix * * @param string $suffix * @return Image */ public function setSuffix($suffix) { $this->suffix = $suffix; return $this; } /** * Set captcha image width * * @param int $width * @return Image */ public function setWidth($width) { $this->width = $width; return $this; } /** * Generate random frequency * * @return float */ protected function randomFreq() { return mt_rand(700000, 1000000) / 15000000; } /** * Generate random phase * * @return float */ protected function randomPhase() { // random phase from 0 to pi return mt_rand(0, 3141592) / 1000000; } /** * Generate random character size * * @return int */ protected function randomSize() { return mt_rand(300, 700) / 100; } /** * Generate captcha * * @return string captcha ID */ public function generate() { $id = parent::generate(); $tries = 5; // If there's already such file, try creating a new ID while ($tries-- && file_exists($this->getImgDir() . $id . $this->getSuffix())) { $id = $this->generateRandomId(); $this->setId($id); } $this->generateImage($id, $this->getWord()); if (mt_rand(1, $this->getGcFreq()) == 1) { $this->gc(); } return $id; } /** * Generate image captcha * * Override this function if you want different image generator * Wave transform from http://www.captcha.ru/captchas/multiwave/ * * @param string $id Captcha ID * @param string $word Captcha word * @throws Exception\NoFontProvidedException if no font was set * @throws Exception\ImageNotLoadableException if start image cannot be loaded */ protected function generateImage($id, $word) { $font = $this->getFont(); if (empty($font)) { throw new Exception\NoFontProvidedException('Image CAPTCHA requires font'); } $w = $this->getWidth(); $h = $this->getHeight(); $fsize = $this->getFontSize(); $imgFile = $this->getImgDir() . $id . $this->getSuffix(); if (empty($this->startImage)) { $img = imagecreatetruecolor($w, $h); } else { // Potential error is change to exception ErrorHandler::start(); $img = imagecreatefrompng($this->startImage); $error = ErrorHandler::stop(); if (!$img || $error) { throw new Exception\ImageNotLoadableException( "Can not load start image '{$this->startImage}'", 0, $error ); } $w = imagesx($img); $h = imagesy($img); } $textColor = imagecolorallocate($img, 0, 0, 0); $bgColor = imagecolorallocate($img, 255, 255, 255); imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bgColor); $textbox = imageftbbox($fsize, 0, $font, $word); $x = ($w - ($textbox[2] - $textbox[0])) / 2; $y = ($h - ($textbox[7] - $textbox[1])) / 2; imagefttext($img, $fsize, 0, $x, $y, $textColor, $font, $word); // generate noise for ($i=0; $i < $this->dotNoiseLevel; $i++) { imagefilledellipse($img, mt_rand(0, $w), mt_rand(0, $h), 2, 2, $textColor); } for ($i=0; $i < $this->lineNoiseLevel; $i++) { imageline($img, mt_rand(0, $w), mt_rand(0, $h), mt_rand(0, $w), mt_rand(0, $h), $textColor); } // transformed image $img2 = imagecreatetruecolor($w, $h); $bgColor = imagecolorallocate($img2, 255, 255, 255); imagefilledrectangle($img2, 0, 0, $w-1, $h-1, $bgColor); // apply wave transforms $freq1 = $this->randomFreq(); $freq2 = $this->randomFreq(); $freq3 = $this->randomFreq(); $freq4 = $this->randomFreq(); $ph1 = $this->randomPhase(); $ph2 = $this->randomPhase(); $ph3 = $this->randomPhase(); $ph4 = $this->randomPhase(); $szx = $this->randomSize(); $szy = $this->randomSize(); for ($x = 0; $x < $w; $x++) { for ($y = 0; $y < $h; $y++) { $sx = $x + (sin($x*$freq1 + $ph1) + sin($y*$freq3 + $ph3)) * $szx; $sy = $y + (sin($x*$freq2 + $ph2) + sin($y*$freq4 + $ph4)) * $szy; if ($sx < 0 || $sy < 0 || $sx >= $w - 1 || $sy >= $h - 1) { continue; } else { $color = (imagecolorat($img, $sx, $sy) >> 16) & 0xFF; $colorX = (imagecolorat($img, $sx + 1, $sy) >> 16) & 0xFF; $colorY = (imagecolorat($img, $sx, $sy + 1) >> 16) & 0xFF; $colorXY = (imagecolorat($img, $sx + 1, $sy + 1) >> 16) & 0xFF; } if ($color == 255 && $colorX == 255 && $colorY == 255 && $colorXY == 255) { // ignore background continue; } elseif ($color == 0 && $colorX == 0 && $colorY == 0 && $colorXY == 0) { // transfer inside of the image as-is $newcolor = 0; } else { // do antialiasing for border items $fracX = $sx - floor($sx); $fracY = $sy - floor($sy); $fracX1 = 1 - $fracX; $fracY1 = 1 - $fracY; $newcolor = $color * $fracX1 * $fracY1 + $colorX * $fracX * $fracY1 + $colorY * $fracX1 * $fracY + $colorXY * $fracX * $fracY; } imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newcolor, $newcolor, $newcolor)); } } // generate noise for ($i=0; $i<$this->dotNoiseLevel; $i++) { imagefilledellipse($img2, mt_rand(0, $w), mt_rand(0, $h), 2, 2, $textColor); } for ($i=0; $i<$this->lineNoiseLevel; $i++) { imageline($img2, mt_rand(0, $w), mt_rand(0, $h), mt_rand(0, $w), mt_rand(0, $h), $textColor); } imagepng($img2, $imgFile); imagedestroy($img); imagedestroy($img2); } /** * Remove old files from image directory * */ protected function gc() { $expire = time() - $this->getExpiration(); $imgdir = $this->getImgDir(); if (!$imgdir || strlen($imgdir) < 2) { // safety guard return; } $suffixLength = strlen($this->suffix); foreach (new DirectoryIterator($imgdir) as $file) { if (!$file->isDot() && !$file->isDir()) { if (file_exists($file->getPathname()) && $file->getMTime() < $expire) { // only deletes files ending with $this->suffix if (substr($file->getFilename(), -($suffixLength)) == $this->suffix) { unlink($file->getPathname()); } } } } } /** * Get helper name used to render captcha * * @return string */ public function getHelperName() { return 'captcha/image'; } }