Yii2 Captcha doesn`t work with Form Ajax validation!

Yii2 Captcha doesn`t work with Form Ajax validation,Because first Ajax Validation’s $valid is true,so $this->getVerifyCode(true) will run,new verifyCode will be generated,second validate $model->validate() will failure.Yii2 code is below:

D:\phpwork\news\vendor\yiisoft\yii2\captcha\CaptchaAction.php




    public function validate($input, $caseSensitive)

    {

        $code = $this->getVerifyCode();

        $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;

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

        $session->open();

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

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

        if ($valid || $session[$name] > $this->testLimit && $this->testLimit > 0) {

            $this->getVerifyCode(true);

        }


        return $valid;

    }



The problem above have successfully solved,below is code:

[size="3"]Source Code:[/size]

file position:D:\phpwork\news\models\RegisterForm.php




class RegisterForm extends Model{

    public function rules()

    {

        return [

            ['verifyCode', 'codeVerify'],

			......

        ];

    }

	//verify with function codeVerify(),avoid bug with Yii2 captcha's validation.

    public function codeVerify($attribute) {

		//Param:'captcha',is name 'captcha' in actions() of controller;Yii::$app->controller,the controller that call this function

        $captcha_validate  = new \yii\captcha\CaptchaAction('captcha',Yii::$app->controller);

        if($this->$attribute){

            $code = $captcha_validate->getVerifyCode();

            if($this->$attribute!=$code){

                $this->addError($attribute, 'The verification code is incorrect.');

            }

        }

    }



file position:D:\phpwork\news\controllers\SiteController.php




class SiteController extends CommonController{

    public function actions(){

        return [

            'captcha' => [

                'class' => 'yii\captcha\CaptchaAction',

            ],

        ];

    }

   public function actionRegister()

    {

        $model = new RegisterForm();

        if ($model->load(Yii::$app->request->post())) {

           if (Yii::$app->request->isAjax) {

                Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

                return \yii\widgets\ActiveForm::validate($model);

            }

            if($model->validate()){

                $error=$model->save();

                if($error) {

                    return $this->errorDisplay($error);

                }else{

					//After data save successful,must generate new verify code.

                    $captcha_validate  = new \yii\captcha\CaptchaAction('captcha',$this);

                    $captcha_validate->getVerifyCode(true);

                    Yii::$app->session->setFlash('success');

                    return $this->refresh();

                }

            }else{

                return $this->errorDisplay($model->getErrors());

            }

        }else{

            return $this->render('register', [

                'model' => $model,

            ]);

        }

    }



file position:D:\phpwork\news\views\site\register.php




<?php

/* @var $this yii\web\View */

/* @var $form yii\bootstrap\ActiveForm */

/* @var $model app\models\ContactForm */

use yii\helpers\Html;

use yii\bootstrap\ActiveForm;

use yii\captcha\Captcha;

$this->title = 'User Register';

$this->params['breadcrumbs'][] = $this->title;

?>

<div class="site-contact">

    <h1><?= Html::encode($this->title) ?></h1>

    <?php if (Yii::$app->session->hasFlash('success')): ?>

        <div class="alert alert-success">

            Register successful!

        </div>

        <p>

            You can now <a href="/site/login">Login</a>.

        </p>

    <?php else: ?>

        <div class="row">

            <div class="col-lg-6">

                <?php $form = ActiveForm::begin([

                    'id' => 'contact-form',

                ]); ?>

                    <?= $form->field($model, 'member',['enableAjaxValidation'=>true])->textInput(['autofocus' => true])->hint('Can be chinese,grapheme or number.Only use to login,not public display!') ?>

                    <?= $form->field($model, 'memkey')->passwordInput() ?>

                    <?= $form->field($model, 'memkey_repeat')->passwordInput()  ?>

                    <?= $form->field($model, 'nickname',['enableAjaxValidation'=>true])->textInput()->hint('Can be chinese,grapheme or number,can have blank space.For public display only!') ?>

                    <?= $form->field($model, 'verifyCode',['enableAjaxValidation'=>true])->widget(Captcha::className(), [

                        'captchaAction'=>'site/captcha',

                        'imageOptions'=>['id'=>'captchaimg','alt'=>'click change image','title'=>'click change image', 'style'=>'cursor:pointer'],

                        'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',

                    ]) ?>

                    <div class="form-group">

                        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>

                    </div>

                <?php ActiveForm::end(); ?>

            </div>

        </div>

    <?php endif; ?>

</div>



[size=“3”]Yii2 Captcha Bug validation’s reason[/size]

First time validation with Ajax is true,so $this->getVerifyCode(true); will run,that new VerifyCode will be generated;Second time validation with $model->verify() will failure,Below is Yii2 source file:

file position:D:\phpwork\news\vendor\yiisoft\yii2\captcha\CaptchaAction.php




    public function validate($input, $caseSensitive)

    {

        $code = $this->getVerifyCode();

        $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;

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

        $session->open();

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

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

		//Bug is produced by these code:

        if ($valid || $session[$name] > $this->testLimit && $this->testLimit > 0) {

            $this->getVerifyCode(true);

        }

        return $valid;

    }



[size="3"](end)[/size]

This bug still exists! How can that be after this was noticed in 2017?

1 Like