Replace Yii Captcha with Zend

Can’t use the YII captcha on Mac due to underlying problems in GD library.

Want to replace with one of Zend’s captcha’s. Prefer for now the dumb one that doesn’t need inet access.

Don’t know how to do it. The Yii one is in the view as a widget How do I “widgetize” a 3rd party captcha? I got as far as including and requiring Zend Library. This should be TRIVIAL but I am a YII complete and total NOOB. I just don’t have a feel for how I can do this.

The issues are:

putting the Captcha in the list of actions I think something like:


	/**

	 * Declares class-based actions.

	 */

	public function actions()

	{

	    Yii::import('application.vendors.*');  //my hack to substitute the Zend Captcha

	    require_once('Zend/Captcha/Dumb.php');  //I want  of this directory is this where to put it?  <img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />? 

	    

		return array(

			// captcha action renders the CAPTCHA image

			// this is used by the contact page

			 'captcha'=>array(

				'class'=> 'Zend_Captcha_Dumb', //'CCaptchaAction',

				'backColor'=>0xF5F5F5,  //what else will you put in here?  

			),

			 

		);

	}

Is this even CLOSE to how you do it?

tweaking the view no clue. How can I use widget?

communicating the code word around (I assume the captcha will generate it but maybe not)

Instead of writing the code to actually DO things, framework require that you tell the right component which

thing you are using and wire all parameters in exactly the right place and time. Once you know how to wire, wiring is always same, you never have to do anything agian. Problem I don’t know how to wire yet. That is the hardest thing.

Can someone please do this very very simple example based on skeleton… EXACTLY WHICH files to change. Please don’t say

put “bla bla bla” in or Configure “Bla bla bla” because I won’t know WHICH file to put it in.

I got immediate response on the bug forum when I reported the bug. Bump. One thing I look for in a framework is a friendly and responsive community. I’ve been reading the doc for Yii and for the Zend Captcha’s all day. I don’t know how to make them play nice together. This is a small well defined task, we can do it on the skeleton app which we all can have in about 3 seconds. I have been RTFM but it is a description of fnctions and atributes, what is missing is HOW TO USE IT TO DO TASKS. Maybe I’m supposed to allready know that but I don’t. Please any little look you can give to point me in the direction, I am all on my own, I really need a little one-on-one. Once I get up to speed I give back. Or maybe this task too hard for Yii? That does not bode well!!!

It’s not that simple to use the zends classes, If you do want to use the classes you will need to register the zend autoloader so it will know how to load them appropriately. For incorporating it into the Yii framework i think it will be a little harder then the code written above. I am not sure why you would want to use Zend captcha instead of Yiis’ but i can tell you that from an inside look they both do the same thing for me. Actually i use my own captcha class much more simple, much more lightweight and doesn’t require a whole lot of code to use it.

May be you want to look here: Integrating with other frameworks

As I already said,

The Yii captcha calls a gd library rotine that blows up on Mac unless you have special custom version of libs.

I can make special version of libs but would rather use a captcha that will work on Mac Dev systems without special modifications, I don’t want everybody required to build libs just so they can use the captcha on their server.

I have seen that code to do the autoloader, I am not afraid of it. I will also need to incorporate some other mmodules from Zend (ie the PDF module, Yii does not have one!) I think the less involved with presentation the easier maybe I’m wrong.

So I have valid reasons to want to swap a different captcha and intersperse other functionality with Zend. Isn’t that what this is all about, reusable modules? One of the big selling points of Yii is interoperability with other frameworks particularly Zend. Cake does this, should I switch back to cake?

I repeat the question, how can I either substitute one of Zend’s captchas. or extend the Yii one so it works different algorithm (like figlet) that does not need that gd call. It is the only call to imagettfbbox in the entire Yii framework.

I don’t know about Zend Captcha, and I don’t think it would be an easy replacement work to plug-in Zend Captcha.

However, the captcha design in Yii allows you to customize it relatively easily. Since your main problem is about generating the captcha image, you may consider extending CCaptchaAction and override its renderImage() method. This method should output an image based on a given string.

Once you have your customized captcha action, you simply need to declare it in the actions() method like you do with CCaptchaAction.

Thank you Qiang,

I really suck at graphics programming but that is also the conclusion I was ending with. Perhaps I can cobble something together out of Zend’s figlet code, though I really hoped NOT to reinvent the wheel. Why is it always I try new system and immediately first thing I want to do is thing it does not do very well easily. Anyway I don’t feel so stupid now. If you of all people tell me this I believe it.

Perhaps the easier way to solve this problem is to fix your GD installation problem. Anyway it is merely an installation issue, and in most hosting environments, this is not really an issue.

[edited incorrectly, see the following post for case correction]

Since I prefer to use Yii logic as much as possible I decide to just take the advice and replace renderImage with a routine that uses Zend’s Figlet at very low level.

I made a symlink to Zend under protected/vendors

I have verified with debugger that

$this->_figlet->render($code) at least produces something figlety looking. It doesn’t do the linebreaks right but I’ll wory about that later. Now I’m not sure how to get it to appear in the form. CHtml::pre or something? I’m just messing in the bare skeleton so we all talking about the same thing.

I extended CCaptcha as well as MyCaptcha and I override the image routine but Not sure what I’m doing there exactly.

Then I also modify the contact view to use MyCaptcha instead of CCaptcha

So here is my extended classes:




<?php

class MyCaptchaAction extends CCaptchaAction

{

    protected $_figlet; 

    public $captchaText;

    

    protected function renderImage($code) 

    {

        Yii::import('application.vendors.*');

        //require_once 'Zend/Loader/Autoloader.php';

        //tell it to use the Zend Figlet Library

        require_once 'Zend/Text/Figlet.php';

        $this->_figlet = new Zend_Text_Figlet();

        $this->captchaText = $this->_figlet->render($code);

    }

}




<?php

class MyCaptcha extends CCaptcha

{

	/**

	 * Override the function that enders the CAPTCHA image.

	 */

	protected function renderImage()

	{

		//if(isset($this->imageOptions['id']))

		//	$id=$this->imageOptions['id'];

		//else

		//	$id=$this->imageOptions['id']=$this->getId();

		//$url=$this->getController()->createUrl($this->captchaAction);

		//$alt=isset($imageOptions['alt'])?$imageOptions['alt']:'';

		//echo CHtml::image($url,$alt,$this->imageOptions);

		echo CHtml::openTag("pre"); echo $this->captchaAction->captchaText; echo CHtml::closeTag("pre");

	}

}






And here is modification in the view





<div class="simple">

	<?php echo CHtml::activeLabel($contact,'verifyCode'); ?>

	<div>

	<?php $this->widget('MyCaptcha'); ?>

	<?php echo CHtml::activeTextField($contact,'verifyCode'); ?>

	</div>

	<p class="hint">Please enter the letters as they are shown in the image above.

	<br/>Letters are not case-sensitive.</p>

</div>



Don’t see anything where the figlet is suppose to go but it does have an effect cuz it makes the alt text go away

(since this is not actually an image)

I have a feeling this is pretty close to the answer.

Pls tell me what to change to make this actually echo the figlet…

From what I read, my GD installation problem is not really fixable in an easy way, it affects a large number of Mac developers, and most people won’t do it. It is very low level code, it involves custom compiles and I’m not sure you can use imagettfbbox anyway – the best you can do is have it not blow up. Instead of going to that trouble people (or their managers) will say, well foo Yii has problems on Macs, we’ll use something else. Most hosting environments won’t have the problem true but lots of develeopers develop on Mac and it needs to run on their localhost first before they can deploy it on hosting. You can’t take that attitude, it is a HUGE barrier to adoption and you will be alienating a huge number of potential Yii converts. I’m trying to create a workaround, hope to publish in the cookbook… Trust an experienced person with usability problems at all levels: YOU NEED THIS.

PS: The Yii captcha is too tightly coupled to an image. What about a question like what is the sume of 6 and 8.

I see what is wrong with it the captchaAction attribute is not what I assumed. Always good to check. This is because you are depending img tag to cause the captcha to appear in the output. Now how to fix.

@tixrus:

Did you try the PHP packages i mentioned in your other post? E.g. MAMP should just work fine. We have some Mac users working on a Yii based project, some of them with no PHP knowledge. Even they didn’t have your problem with CAPTCHA.

There is also the recaptcha extension http://www.yiiframework.com/extension/recaptcha/ may be that can help. PHP extensions are mostly 32bits, i presume your php and apache are 64bit binaries?

Another way forward, is to use a virtual machine for development. It has some advantages and disadvantages with this approach.

You guys all suffer from the same problem. I used to have it myself but I got cured real quick by working directly with real users. You’re missing my point entirely.

To illustrate, I wrote an absolutely fantastic package this one time. There was one minor configuration/installation oversight such that my first test user was not able to use the software out of the box – it spewed scary looking errors and never got to a reassuringly “pretty” page. I saw that and said, oops I forgot about that little config, just make this little change in the config. We did and it was flawless after that and it was also a perfect fit for her needs. But that was lost on her. The emotional impact of that page of cascading errors was too strong. It didn’t work out of the box and that is that. I had lost her forever, and she was even a friend. She didn’t believe, know, or care that once she made that little change everything would be fine. In her mind, if you have to jump through hoops to get it to work, you’ll be jumping through hoops at every turn. That is human nature.

To paraphrase Jack Kennedy, "Instruct not what changes your user should make to accommodate your software, ask instead what changes you should make in your software to accommodate your user. I can bash up a MAMP but that’s not the point. The point is a HUGE number of people (including developers) WON’T. They’ll move on.

Also, I’m not thrilled with how the captcha is tightly coupled to an image only method. I’ll just live without captcha on the dev box. This whole thing was just an exercise to see if I could swap in a component of my choice easily. Obviously some are easier than others.

I think that is a good point. Thank you. Most developers would not have investigated this far. Below is some code to get you started in a better direction. This provides a dummy captcha implementation. Actually, your inquiry allowed us to better refactor the code in CCaptchaAction. For that we thank you as well.




class DummyCaptcha extends CCaptcha

{

    public function run()

    {

        $action = new DummyCaptchaAction(Yii::app()->Controller, 'captcha');

        echo $action->run();

    }

}


class DummyCaptchaAction extends CCaptchaAction

{

    public function run()

    {

        return $this->getVerifyCode($this->shouldRengerate());

    }

     

    /** this method should be refactored out in CCaptchaAction. */

    protected function shouldRengerate()

    {

        $session=Yii::app()->session;

        $session->open();

        $name=$this->getSessionKey().'count';

        if($session[$name]===null || $session[$name]>=$this->testLimit)

            $regenerate=true;

        else

        {

            $session[$name]=$session[$name]+1;

            $regenerate=false;

        }

        return $regenerate;

    }

}



Usage:

In your controller,




class MyController extends CController

{

    public $search;


    public function actions()

    {

        return array(

            'captcha' => array(

                'class'=> 'DummyCaptchaAction') //assuming you placed it in e.g. components/ or models/

            );

    }

}



In your view




<?php $this->widget('DummyCaptcha') ?>



No changes needed for your models that contains the validation rules.