{$this->typeAttribute}; $result = $this->validateCaptcha($model->$attribute, $type); if ($result !== null) { $this->addError($model, $attribute, $result[0], $result[1]); } } /** * @return array{string, array}|null */ private function validateCaptcha(mixed $value, mixed $type): ?array { if (empty($value) || !is_string($value)) { return [$this->requiredMessage, []]; } if (empty($type)) { $type = $this->defaultType; } if (!is_string($type) || !$this->captchaRegistry->hasProvider($type)) { return [$this->typeMessage, []]; } $provider = $this->captchaRegistry->getProvider($type); $this->trigger(self::EVENT_BEFORE_VERIFY, new CaptchaEvent($type)); $remoteIp = Yii::$app->getRequest()->getUserIP(); $repeats = 0; while (true) { $startedAt = hrtime(true); try { $isValid = $provider->verify($value, $remoteIp); } catch (ConnectException|ServerException $e) { if (++$repeats >= self::REPEAT_LIMIT) { throw $e; } sleep(self::REPEAT_TIMEOUT); continue; } $duration = CarbonInterval::microseconds(intdiv(hrtime(true) - $startedAt, 1000)); break; } $this->trigger(self::EVENT_AFTER_VERIFY, new CaptchaResultEvent($type, $isValid, $duration)); if (!$isValid) { return [(string)$this->message, []]; } return null; } }