Ogromną wadą komponentu Zend_Captcha_Image jest jego niska wydajność. Główną przyczyną jest użycie biblioteki GD i czystego PHP do utworzenia i zdeformowania obrazka. Jak obejść ten problem?
Interesującym rozwiązaniem będzie zastosowanie ImageMagick. Rozszerzenie to dostarcza zestaw klas manipulujących obrazkami, a bogaty zestaw funkcji pozwoli na uzyskanie efektu podobnego do Zend_Captcha_Image. Ta ostatnia klasa zostanie wykorzystana jako baza, co bardzo ułatwi proces kodowania.
Oto podstawowa klasa:
<?php
class My_Captcha_Imagick extends Zend_Captcha_Image
{
/**
* Generate image captcha
*
* @param string $id Captcha ID
* @param string $word Captcha word
*/
protected function _generateImage($id, $word)
{
// jeśli brakuje rozszerzenia imagick, to obrazek
// jest generowany zwykłą metodą - przy pomocy biblioteki GD
if (!extension_loaded("imagick")) {
return parent::_generateImage($id, $word);
}
$font = $this->getFont();
if (empty($font)) {
require_once 'Zend/Captcha/Exception.php';
throw new Zend_Captcha_Exception("Image CAPTCHA requires font");
}
$w = $this->getWidth();
$h = $this->getHeight();
$fsize = $this->getFontSize();
$img_file = $this->getImgDir() . $id . $this->getSuffix();
if(empty($this->_startImage)) {
$img = new Imagick();
$img->newImage($w, $h, new ImagickPixel('#FFFFFF'), 'png');
} else {
$img = new Imagick($this->_startImage);
$w = $img->getImageWidth();
$h = $img->getImageHeight();
}
$text = new ImagickDraw();
$text->setFilLColor('#000000');
$text->setFont($font);
$text->setFontSize($h - 10);
$text->setGravity(Imagick::GRAVITY_CENTER);
$text->annotation(0, 0, $word);
// generate noise
$noise = new ImagickDraw();
$noise->setFilLColor('#000000');
for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
$x = mt_rand(0,$w);
$y = mt_rand(0,$h);
$noise->circle($x, $y, $x+mt_rand(0.3, 1.7), $y+mt_rand(0.3, 1.7));
}
for($i=0; $i<$this->_lineNoiseLevel; $i++) {
$noise->line(mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h));
}
$img->drawImage($text);
$img->waveImage(5, mt_rand(60, 100));
$img->drawImage($noise);
$img->swirlImage(mt_rand(10, 30));
file_put_contents($img_file, $img);
unset($img);
}
}
jedyna metoda, _generateImage(), jest odpowiednio przerobioną wersją z klasy bazowej. Można poeksperymentować z argumentami metod waveImage() i swirtlImage(), ewentualnie dodać własne filtry dla uzyskania ciekawszego efektu.
Naszą nową klasę można stosować dokładnie tak samo jak Zend_Captcha_Image. Przykład - zastosowanie jej w formularzu:
$this->addElement('captcha', 'captcha', array(
'label' => 'Przepisz tekst z obrazka:',
'ignore' => true,
'captcha' => new My_Captcha_Imagick(array(
'font' => APPLICATION_PATH . '/../data/fonts/arialcebold.ttf',
'wordLen' => 6,
'timeout' => 300
))
));
A co z wydajnością? Sam proces generowania obrazka jest ok. 10 razy szybszy niż w rozwiązaniu Zenda. Po dodaniu narzutu związanego z zapisem do pliku stosunek ten będzie wynosił ok. 1:4.