Yii2 Captcha image does not exist

I’m having a hard time with the captcha in yii2. I am working on localhost and I would simply like to have a captcha in my contact form, but the captcha images will not be generated no matter what I do.

I have Imagick installed and basically kept the advanced installation just like it was. Also every required package was installed. After some debugging I found out that it seems like the first if-statement in public function run() in the captcha file located in vendor/yiisoft/yii2/captcha evaluates to false, even though it should be true.

So when I change this

if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) !== null) {
            // AJAX request for regenerating code
            $code = $this->getVerifyCode(true);
            Yii::$app->response->format = Response::FORMAT_JSON;
            return [
                'hash1' => $this->generateValidationHash($code),
                'hash2' => $this->generateValidationHash(strtolower($code)),
                // we add a random 'v' parameter so that FireFox can refresh the image
                // when src attribute of image tag is changed
                'url' => Url::to([$this->id, 'v' => uniqid('', true)]),
            ];
        }

to this:

if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) == null) {
            // AJAX request for regenerating code
            $code = $this->getVerifyCode(true);
            Yii::$app->response->format = Response::FORMAT_JSON;
            return [
                'hash1' => $this->generateValidationHash($code),
                'hash2' => $this->generateValidationHash(strtolower($code)),
                // we add a random 'v' parameter so that FireFox can refresh the image
                // when src attribute of image tag is changed
                'url' => Url::to([$this->id, 'v' => uniqid('', true)]),
            ];
        }

I don’t understand why the following line is evaluated to null:

if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) == null) {

it will actually create a JSON object and pass it to the frontend, but with an invalid image id, as it was not generated, just passed the link. I’m actually concerned that it does not find Imagick or something, even though there’s no error in requirements.php.

contact.php

<?php

use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;

$this->title = 'Contact';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-contact">
    <h1><?= Html::encode($this->title) ?></h1>

    <p>
        If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
    </p>

    <div class="row">
        <div class="col-lg-5">
            <?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>

                <?= $form->field($model, 'name')->textInput(['autofocus' => true]) ?>

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

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

                <?= $form->field($model, 'body')->textarea(['rows' => 6]) ?>

                <?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
                    '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>

</div>

ContactForm.php

<?php
namespace frontend\models;

use Yii;
use yii\base\Model;

/**
 * ContactForm is the model behind the contact form.
 */
class ContactForm extends Model
{
    public $name;
    public $email;
    public $subject;
    public $body;
    public $verifyCode;


    public function rules()
    {
        return [
            // name, email, subject and body are required
            [['name', 'email', 'subject', 'body'], 'required'],
            // email has to be a valid email address
            ['email', 'email'],
            // verifyCode needs to be entered correctly
            ['verifyCode', 'captcha'],
        ];
    }

    public function attributeLabels()
    {
        return [
            'verifyCode' => 'Verification Code',
        ];
    }

    public function sendEmail($email)
    {
        return Yii::$app->mailer->compose()
            ->setTo($email)
            ->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']])
            ->setReplyTo([$this->email => $this->name])
            ->setSubject($this->subject)
            ->setTextBody($this->body)
            ->send();
    }
}

This is the error message when trying to access the image link: Translated: The graphic cannot be displayed because it contains errors.

See my original post from stack overflow: https://stackoverflow.com/questions/62405103/yii2-captcha-image-does-not-exist

Has anyone encountered something similar? Any help highly appreciated!

Hi @tombjarne, welcome to the forum.

The statement should be evaluated to null when you initially render the page with a captcha. It will be other than null (a string ‘refresh’ in fact) only when the user clicks on the captcha image to refresh it.

So, restore the core code as it was.

You could debug by stepping into this line.

It’s only a guess, but I think the most probable cause of the problem is in imagick installation. I would uninstall it and try gd instead.

Hi @softark, thanks for your help. I had gd installed actually and it did not work either. Now it’s constantly telling me that it can’t find imagick when trying to remove the package.

That’s strange. Could you check ‘phpinfo()’ output or the result of ‘requirement.php’?

Yes I did check that, but both of them show up.

Could you try this in your SiteController?

    /**
     * {@inheritdoc}
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
            ],
            'captcha' => [
                'class' => 'softark\mbcaptcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
                'imageLibrary' => 'gd', // use gd instead of imagick
            ],
        ];
    }

CaptchaAction::imageLibrary defaults to ‘imagick’ when both of them are installed, but we want to use ‘gd’ instead.

I have just tried, but still no change and also no error message is being printed :frowning:
@softark this is what it shows in requirements.php. So it should not have any problems using the gd library, right?

Right, I don’t see anything wrong in the results of ‘requirement.php’.
Hmm, what could be wrong?

My colleague said that it worked a week ago, but I don’t see why it should have worked a week ago but not now. I did not change anything regarding the captcha. Although I modified the contact page to take a field “lastName”. I reverted all of these changes for now to test if I did anything wrong.

What environment do you have for your development?

We’re running on an ubuntu vm with version 18.04.4

I just fired up my old 32-bit Ubuntu (18.04.2). Installing updates now. Yii 2.0.16 Captcha fine. Will check again after update.
I already checked Ubuntu 20.04 with Yii 2.0.35.
Did you try a standard Yii2 installation (e.g. basic template) ?

No I did not try to install the basic template yet. I will try that out as well.

UPDATE: It seems like the application finds the imagick installation, but is referring to another one during runtime. So there won’t be an error when running it, and show it as “passed” in requirements.php. Any ideas on why that could be and where it could refer to that other imagick library (that obviously does not exist, as it’s not being rendered) ?

I’m not familiar with Imagick but obviously had it installed (and updated)

sudo apt install imagemagick
“imagemagick is already the newest version (8:6.9.7.4+dfsg-16ubuntu6.8)”
sudo apt install php-imagick
“… Setting up php-imagick (3.4.3~rc2-2ubuntu4)”

(remember to restart Apache)
My google search for hints “imagick ubuntu 18.04”.
The Captcha works as expected.

@tri I did try it out prior today and it installed just fine, but somehow only the requirements file found imagick. The actual captcha controller didn’t seem to find it anywhere

@tombjarne If you have a debugger ready try to set a breakpoint in CaptchaAction.php. To me it looks like the code first perfoms a requirement test (same as the external test except explicitly looking for Imagick in global namespace). As proposed by @softark, a value in the imageLibrary property will override the requirement test in CaptchaAction.
You should be able to detect that you reach one of renderImageByImagick() or renderImageByGD() functions with or without first loading the image library in the requirement test.
BTW it looks like @softark uses his own CaptchaAction. Seems to be a good idea for inserting trace steps without modifying system code.
BTW2 You mentioned the Captcha worked just days before. I came to think about e.g. available memory (well, just a thought), permissions or some other environmental issue.

Oh, I’m sorry. I forgot to change this line. It should be

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

for your environment, @tombjarne.
I’m using my own extension (https://github.com/softark/yii2-mb-captcha). But there’s no fundamental change from the original. So it should also fail when the image library has some failure.

Anyway, I agree with @tri that we need to trace the code step by step to find out the pitfall.

Yes I did already change that to what my application was using. It seems like the images are not even created. The app.log does not show any error and it’s creating the image id’s / links.

But imagick will not render anything because I believe it might not be referenced correctly. But where could that be the case? Where is a path or something referenced to imagick? The requirements php detects it somehow, but it does not do anything.

I also tried finding out where the captcha images are being rendered in but I could not find any proof that they were created, so I assume there is a mistake during rendering / referencing imagick.