captcha validator don't work properly

I’m trying to develop a users model the main part is


<?php


class Users extends CActiveRecord

{


	public $repeatPassword;

	public $repeatEmail;

	public $verifyCode;

	public $rememberMe = false;

	private $_identity;


	/**

 	* @return array validation rules for model attributes.

 	*/

	public function rules()

	{

    	// NOTE: you should only define rules for those attributes that

    	// will receive user inputs.

    	return array(

        	array('username, password', 'required'),

        	// Signup scenario

        	array('repeatPassword, repeatEmail', 'required', 'on' => 'signup'),

        	array('repeatPassword', 'compare', 'compareAttribute' => 'password', 'strict' => true, 'on' => 'signup'),

        	array('repeatEmail', 'compare', 'compareAttribute' => 'email', 'strict' => true, 'on' => 'signup'),

        	array('username, email', 'unique', 'on' => 'signup'),

        	array('email, repeatEmail', 'email'),

        	// Signin scenario

        	array('rememberMe', 'boolean', 'on' => 'signin'),

        	array('password', 'authenticate', 'on' => 'signin'),

        	array('verifyCode', 'captcha', 'on' => 'signup, signin', 'allowEmpty' => !CCaptcha::checkRequirements()),

        	// The following rule is used by search().

        	// Please remove those attributes that should not be searched.

        	array('id, username, password, verification_code, email, email_notification, sms_notification, forgot_password_code, forgot_password_request_date, status, role, create_time, last_visit, last_ip, security_key', 'safe', 'on' => 'search'),

    	);

	}


	public function beforeSave()

	{

    	if(parent::beforeSave()){

        	// for example

        	$this->password = md5($this->password . Yii::app()->params['saltString']);

        	return true;

    	}

    	return false;

	}


}

and a users controller


<?php


class UsersController extends Controller

{


	/**

 	* @var string the default layout for the views. Defaults to '//layouts/column2', meaning

 	* using two-column layout. See 'protected/views/layouts/column2.php'.

 	*/

	public $layout = '//layouts/column2';


	/**

 	* Declares class-based actions.

 	*/

	public function actions()

	{

    	return array(

        	'captcha' => array(

            	'class' => 'CCaptchaAction',

            	'backColor' => 0xFFFFFF,

            	'testLimit' => 1,

            	'minLength' => 7,

            	'maxLength' => 7,

        	),

        	// page action renders "static" pages stored under 'protected/views/site/pages'

        	// They can be accessed via: index.php?r=site/page&view=FileName

        	'page' => array(

            	'class' => 'CViewAction',

        	),

    	);

	}


	/**

 	* @return array action filters

 	*/

	public function filters()

	{

    	return array(

        	'accessControl', // perform access control for CRUD operations

    	);

	}


	/**

 	* Specifies the access control rules.

 	* This method is used by the 'accessControl' filter.

 	* @return array access control rules

 	*/

	public function accessRules()

	{

    	return array(

        	array('allow', // allow all users to perform 'index' and 'view' actions

            	'actions' => array('captcha', 'signup', 'signin', 'signout', 'forget', 'resetPassword'),

            	'users' => array('*'),

        	),

        	array('allow', // allow all users to perform 'index' and 'view' actions

            	'actions' => array('changePassword'),

            	'users' => array('@'),

        	),

        	array('allow', // no one can create, update, or delete these:

            	'actions' => array('edit', 'admin', 'delete'),

            	'users' => array('@'),

            	'expression' => '(Yii::app()->user->getState("role")==="Operator") || (Yii::app()->user->getState("role")==="Administrator")',

        	),

        	array('deny', // deny all users

            	'users' => array('*'),

        	),

    	);

	}


	/**

 	* Returns the data model based on the primary key given in the GET variable.

 	* If the data model is not found, an HTTP exception will be raised.

 	* @param integer the ID of the model to be loaded

 	*/

	public function loadModel($id)

	{

    	$model = Users::model()->findByPk((int) $id);

    	if($model === null)

        	throw new CHttpException(404, 'The requested page does not exist.');

    	return $model;

	}


	/**

 	* Performs the AJAX validation.

 	* @param CModel the model to be validated

 	*/

	protected function performAjaxValidation($model)

	{

    	if(isset($_POST['ajax']) && $_POST['ajax'] === 'users-form'){

        	echo CActiveForm::validate($model);

        	Yii::app()->end();

    	}

	}


	/**

 	* Creates a new model.

 	* If creation is successful, the browser will be redirected to the 'view' page.

 	*/

	public function actionSignup()

	{

    	$model = new Users('signup');




    	if(isset($_POST['ajax']) && $_POST['ajax'] === 'users-signup-form'){

        	echo CActiveForm::validate($model);

        	Yii::app()->end();

    	}


    	if(isset($_POST['Users'])){

        	$model->attributes = $_POST['Users'];

        	if($model->validate()){

            	$activationCode = md5(rand(1, 10000));

            	$model->activation_code = $activationCode;

            	$model->create_time = time();

            	$model->status = 'none';

            	$model->role = 'User';


            	if($model->save()){

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

                	require_once('Setak/EmailGenerator.php');

                	$emailBodyArray = array(

                    	'siteName' => Yii::app()->params['name'],

                    	'email' => $model->email,

                    	'adminEmail' => Yii::app()->params['adminEmail'],

                    	'signupValidatorUrl' => $this->createAbsoluteUrl('users/signupValidator', array('vc' => $verificationCode)),

                	);


                	echo $emailBody = EmailGenerator::generat('signup', $emailBodyArray);

                	$headers = 'From: ' . Yii::app()->params['adminEmail'] . '\r\nReply-To: ' . Yii::app()->params['adminEmail'];

                	$headers = 'MIME-Version: 1.0\r\nFrom: ' . Yii::app()->params['adminEmail'] . '\r\nReply-To: ' . Yii::app()->params['adminEmail'] . '\r\nContent-Type: text/html; charset=utf-8';

                	$emailBody = wordwrap($emailBody, 70);

                	$emailBody = str_replace("\n.", "\n..", $emailBody);

                	//mail($model->email, '=?UTF-8?B?'.base64_encode(Yii::t('users','No-Reply Please')).'?=', $emailBody, $headers);

                	Yii::app()->user->setFlash('signup', Yii::t('site', 'We will respond to you as soon as possible.'));

            	}else{

                	Yii::app()->user->setFlash('signup', Yii::t('site', 'Unknown Error: We have faced to some error in storing data to database please contact with Administrator.'));

            	}

        	}

    	}


    	$this->render('signup', array(

        	'model' => $model,

    	));

	}


	/**

 	* Displays the login page

 	*/

	public function actionSignin()

	{

    	$model = new Users('signin');


    	// if it is ajax validation request

    	if(isset($_POST['ajax']) && $_POST['ajax'] === 'users-signin-form'){

        	echo CActiveForm::validate($model);

        	Yii::app()->end();

    	}


    	// collect user input data

    	if(isset($_POST['Users'])){

        	$model->attributes = $_POST['Users'];

        	// validate user input and redirect to the previous page if valid

        	if($model->validate() && $model->signin())

            	$this->redirect(Yii::app()->user->returnUrl);

    	}

    	// display the login form

    	$this->render('signin', array('model' => $model));

	}


	/**

 	* Logout the current user and redirect to returnLogoutUrl.

 	*/

	public function actionSignout($token)

	{

    	if($token !== Yii::app()->getRequest()->getCsrfToken())

        	throw new CHttpException(400, Yii::t('app', 'Invalid request. Please do not repeat this request again.'));

    	Yii::app()->user->logout();

    	$this->redirect(Yii::app()->homeUrl);

	}




}



captcha validator in signin action work fine but it does not pass in signup action.

what’s the problem

no idea?

when i change


 'testLimit' => 1,



to 2 the problem is solved but I want to know the main reason and how can I use 1 as test limit?

Actually Captcha in Yii still has a little problem with validation.

$testLimit is the limit of validation tests it will accept before changing the code.

When you submit your form, the validation rule will run and it will count 1.

But when you save an AR model, validation runs again, and it will count 2.

So $model->save(false) is a appropriate way for solving the problem?

in order for the testlimit to work you should set ‘enableClientValidation’=>false, on the CActiveForm

you can also try with this extension

http://www.yiiframework.com/extension/captcha-extended