2009-11-09

Zend_Captcha_Captchator

Captcha

Captchator Captcha Co to jest CAPTCHA, każdy wie. No, moze nie każdy jest świadomy, że przepisując tekst z obrazka rozwiązuje test (Completely Automated Public Turing test to tell Computers and Humans Apart) który ma za zadanie odróżnić człowieka od maszyny i zapobiec wypełnianiu naszych formularzy przez roboty.

Z tym, że ten artykuł nie jest dla tych którzy żyją nieświadomi co i w jakim celu wypełniają. :)

Jako programista aplikacji internetowych średnio co kilka dni wstawiam gdzieś captchę. Dobrze jest sobie to ułatwiać, dlatego stworzyłem komponent do Zend Framework-a. Mam nadzieję, że komuś się przyda.

Jak używać

Jak każdej standardowej captch-y w Zend Framwork-u :)

// Tworzymy instancję Zend_View
$view = new Zend_View();

// W pierwszym widoku:
$captcha = new Zend_Captcha_Captchator();

$id = $captcha->generate();
echo "
"; echo $captcha->render($view); echo "
"; // W kolejnym rządaniu: // Założmy, ze ustawiona w poprzednium rządaniu wartość $_POST['foo'] // będziwe stanowiła wartość captch-y input => captcha value if ($captcha->isValid($_POST['foo'], $_POST)) { // Sukces, captcha prawidłowa! }

Można nawet pominąć metodę generate()
Dodatkowo jeśli ktoś chce do formularza wyciągnąć jedynie adres obrazka to ma do dyspozycji metodę getImgUrl()

Demonstracja

Jak to działa można zobaczyć na demonstracji http://smoku.net/artykuly/zend-captcha-captchator-demo

Kod

/** Zend_Captcha_Base */
require_once 'Zend/Captcha/Base.php';

class Example_Captcha_Captchator extends Zend_Captcha_Base
{

    /**#@+
     * Error codes
     * @const string
     */
    const MISSING_VALUE = "missingValue";
    const ERR_CAPTCHA   = "errCaptcha";
    const BAD_CAPTCHA   = "badCaptcha";
    /**#@-*/

    /**
     * Error messages
     * @var array
     */
    protected $_messageTemplates = array(
        self::MISSING_VALUE => "Empty captcha value",
        self::ERR_CAPTCHA   => "Captcha error",
        self::BAD_CAPTCHA   => "Captcha value is wrong",
    );
    
    protected $_imgWidth = 130;
    
    protected $_imgHeight = 90;
    
    protected $_imgAlt = "Captcha Image";
    
    protected $_imgUriTpl = "http://captchator.com/captcha/image/%1s";
    
    protected $_validateUriTpl = "http://captchator.com/captcha/check_answer/%s/%s";
    
    protected $_personId;
    
    /**
     * Constructor
     * 
     * @param  null|string|array|Zend_Config $options 
     * @param  null|string $personId 
     * @return void
     */
    public function __construct($options = null, $personId = null)
    {
        parent::__construct($options);
        $this->setPersonId($personId);
    }
    
    /**
     * Generate a new captcha
     *
     * @return string new captcha ID
     */
    public function generate(){
        $personID = $this->getPersonId();
        if(empty($personID)){
            $this->setPersonId();
        }
    }

    /**
     * Display the captcha
     *
     * @param Zend_View_Interface $view
     * @param mixed $element
     * @return string
     */
    public function render(Zend_View_Interface $view = null, $element = null)
    {
        $html = "<"."img";
        $html .= " width=\"".$this->getImgWidth()."\"";
        $html .= " height=\"".$this->getImgHeight()."\"";
        $html .= " alt=\"".$this->getImgAlt()."\"";
        $html .= " src=\"".$this->getImgUrl()."\"";
        $html .= " />";
        $html .= "<"."br"." />";
        return $html;
    }
    
     /**
     * Validate captcha
     *
     * @see    Zend_Validate_Interface::isValid()
     * @param  mixed $value
     * @return boolean
     */
    public function isValid($value)
    {
        $value = preg_replace( "/[^a-z0-9]+/i", "", strval($value) );

        if (empty($value)) {
            $this->_error(self::MISSING_VALUE);
            return false;
        }

        $service = new Zend_Http_Client( $this->_getValidateUrl( $value ) );

        if (!$service) {
            $this->_error(self::ERR_CAPTCHA);
            return false;
        }
        
        $res = $service->request(Zend_Http_Client::GET);
        
        if (!$res) {
            $this->_error(self::ERR_CAPTCHA);
            return false;
        }
        
        if (!$res->isSuccessful()) {
            $this->_error(self::ERR_CAPTCHA);
            return false;
        }
        
        if( trim($res->getBody()) == "0" ){
            $this->_error(self::BAD_CAPTCHA);
            return false;
        }elseif( trim($res->getBody()) != "1" ){
            $this->_error(self::ERR_CAPTCHA);
            return false;
        }

        return true;
    }
    
    private function getImgUrl()
    {
        $this->generate();
        return sprintf( $this->_getImgUriTpl() , $this->getPersonId() );
    }
    
    private function setPersonId($personId = null)
    {
        if(is_string($personId)){
            $personId = preg_replace( "/[^a-z0-9]+/i", "", $personId );
            if(!empty($personId)){
                $this->_personId = $personId;
                return true;
            }
        }
        
        if(Zend_Session::isStarted()){
            //$this->_personId = preg_replace( "/[^a-z0-9]+/i", "", Zend_Session::getOptions("name") ).substr( Zend_Session::getId() , 0 , 15 );
            $this->_personId = substr( Zend_Session::getId() , 0 , 15 );
            return true;
        }else{
            $this->_error(self::ERR_CAPTCHA);
            return false;
        }
    }
    
    private function getPersonId(){
        return strval($this->_personId);
    }
    
    public function setImgUriTpl($uri)
    {
        if(strval($uri) && !empty($uri)){
            $this->_imgUriTpl = strval($uri);
        }
    }
    
    public function setValidateUriTpl($uri)
    {
        if(strval($uri) && !empty($uri)){
            $this->_imgValidateUriTpl = strval($uri);
        }
    }
    
    public function setImgWidth($width)
    {
        if(intval($width)){
            $this->_imgWidth = intval($width);
        }
    }
    
    public function getImgWidth()
    {
        return intval($this->_imgWidth);
    }
    
    public function setImgHeight($height)
    {
        if(intval($height)){
            $this->_imgHeight = intval($height);
        }
    }
    
    public function getImgHeight()
    {
        return intval($this->_imgHeight);
    }
    
    public function setImgAlt($imgAlt)
    {
        if(is_string($imgAlt) && !empty($imgAlt)){
            $this->_imgAlt = $imgAlt;
        }
    }
    
    public function getImgAlt()
    {
        return strval($this->_imgAlt);
    }
    
    protected function _getImgUriTpl()
    {
        return strval($this->_imgUriTpl);
    }
    
    protected function _getValidateUriTpl()
    {
        return strval($this->_validateUriTpl);
    }
    
    protected function _getValidateUrl($value)
    {
        $this->generate();
        return sprintf( $this->_getValidateUriTpl() , $this->getPersonId() , $value );
    }
}