jumper423/decaptcha

View on GitHub
src/core/DeCaptchaBase.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace jumper423\decaptcha\core;

/**
 * Распознавание капчи.
 *
 * Class DeCaptchaBase
 */
class DeCaptchaBase extends DeCaptchaAbstract implements DeCaptchaInterface
{
    const ACTION_RECOGNIZE = 1;
    const ACTION_UNIVERSAL = 2;
    const ACTION_UNIVERSAL_WITH_CAPTCHA = 3;

    const ACTION_FIELD_METHOD = 1;
    const ACTION_FIELD_KEY = 2;
    const ACTION_FIELD_FILE = 3;
    const ACTION_FIELD_PHRASE = 4;
    const ACTION_FIELD_REGSENSE = 5;
    const ACTION_FIELD_NUMERIC = 6;
    const ACTION_FIELD_MIN_LEN = 7;
    const ACTION_FIELD_MAX_LEN = 8;
    const ACTION_FIELD_LANGUAGE = 9;
    const ACTION_FIELD_SOFT_ID = 10;
    const ACTION_FIELD_CAPTCHA_ID = 11;
    const ACTION_FIELD_ACTION = 12;
    const ACTION_FIELD_QUESTION = 13;
    const ACTION_FIELD_CALC = 14;
    const ACTION_FIELD_HEADER_ACAO = 15;
    const ACTION_FIELD_INSTRUCTIONS = 16;
    const ACTION_FIELD_PINGBACK = 17;
    const ACTION_FIELD_TASK = 18;
    const ACTION_FIELD_IS_RUSSIAN = 19;
    const ACTION_FIELD_LABEL = 20;
    const ACTION_FIELD_GOOGLEKEY = 21;
    const ACTION_FIELD_RECAPTCHA = 22;
    const ACTION_FIELD_PROXY = 22;
    const ACTION_FIELD_PROXYTYPE = 23;
    const ACTION_FIELD_PAGEURL = 24;
    const ACTION_FIELD_GOOGLETOKEN = 25;
    const ACTION_FIELD_PROXYLOGIN = 26;
    const ACTION_FIELD_PROXYPASS = 27;
    const ACTION_FIELD_USERAGENT = 28;
    const ACTION_FIELD_COOKIES = 29;
    const ACTION_FIELD_PROXYPORT = 30;
    const ACTION_FIELD_COORDINATE = 31;
    const ACTION_FIELD_INVISIBLE = 32;
    const ACTION_FIELD_SSC_USER_ID = 33;
    const ACTION_FIELD_SSC_SESSION_ID = 34;
    const ACTION_FIELD_SSC_WEB_SERVER_SIGN = 35;
    const ACTION_FIELD_SSC_WEB_SERVER_SIGN2 = 36;
    const ACTION_FIELD_NOJS = 37;
    const ACTION_FIELD_PUBLICKEY = 38;
    const ACTION_FIELD_LANG = 39;
    const ACTION_FIELD_VERSION = 40;
    const ACTION_FIELD_MIN_SCORE = 41;
    const ACTION_FIELD_GT = 42;
    const ACTION_FIELD_CHALLENGE = 43;
    const ACTION_FIELD_API_SERVER = 44;

    const RESPONSE_RECOGNIZE_OK = 'OK';
    const RESPONSE_RECOGNIZE_REPEAT = 'ERROR_NO_SLOT_AVAILABLE';
    const RESPONSE_GET_OK = 'OK';
    const RESPONSE_GET_REPEAT = 'CAPCHA_NOT_READY';

    const SLEEP_RECOGNIZE = 5;
    const SLEEP_GET = 2;
    const SLEEP_BETWEEN = 5;

    const DECODE_ACTION_RECOGNIZE = 1;
    const DECODE_ACTION_GET = 2;
    const DECODE_ACTION_UNIVERSAL = 3;

    const DECODE_PARAM_RESPONSE = 1;
    const DECODE_PARAM_CAPTCHA = 2;
    const DECODE_PARAM_CODE = 3;

    public $actions = [
        self::ACTION_RECOGNIZE              => [],
        self::ACTION_UNIVERSAL              => [],
        self::ACTION_UNIVERSAL_WITH_CAPTCHA => [],
    ];

    protected $decodeSettings = [
        self::DECODE_FORMAT => self::RESPONSE_TYPE_STRING,
        self::DECODE_ACTION => [
            self::DECODE_ACTION_RECOGNIZE => [],
            self::DECODE_ACTION_GET       => [],
        ],
    ];

    protected $limitSettings = [
        self::ACTION_RECOGNIZE              => 5,
        self::ACTION_UNIVERSAL_WITH_CAPTCHA => 40,
    ];

    /**
     * @var DeCaptchaWiki
     */
    protected $wiki;

    /**
     * DeCaptchaBase constructor.
     *
     * @param $params
     */
    public function __construct($params)
    {
        $this->setParams($params);
        $this->wiki = new DeCaptchaWiki($this);
        $this->init();
    }

    public function init()
    {
    }

    /**
     * @param $filePath
     * @param array $additionally
     *
     * @throws DeCaptchaErrors
     *
     * @return bool
     */
    public function recognize($filePath, $additionally = [])
    {
        try {
            $this->resetLimits();
            if ($filePath) {
                $additionally[static::ACTION_FIELD_FILE] = $filePath;
            }
            $this->setParams($additionally);

            return $this->requestRecognize() && $this->requestCode();
        } catch (DeCaptchaErrors $e) {
            if ($this->causeAnError) {
                throw $e;
            }
            $this->errorObject = $e;

            return false;
        }
    }

    /**
     * Запуск распознавания капчи.
     *
     * @deprecated
     *
     * @param string $filePath Путь до файла или ссылка на него
     *
     * @return bool
     */
    public function run($filePath)
    {
        return $this->recognize($filePath);
    }

    /**
     * Универсальная отправка повторяющихся запросов.
     *
     * @param int      $action
     * @param int      $decodeAction
     * @param int      $setParam
     * @param int      $decodeSerParam
     * @param int      $ok
     * @param int      $sleep
     * @param int      $repeat
     * @param int|null $error
     *
     * @throws DeCaptchaErrors
     *
     * @return bool
     */
    protected function requestRepeat($action, $decodeAction, $setParam, $decodeSerParam, $ok, $sleep, $repeat, $error = null)
    {
        if (is_null($error)) {
            $error = static::DECODE_PARAM_RESPONSE;
        }
        while ($this->limitHasNotYetEnded($action)) {
            $this->executionDelayed($sleep);
            $response = $this->getResponse($action);
            $dataRecognize = $this->decodeResponse($decodeAction, $response);
            if ($dataRecognize[static::DECODE_PARAM_RESPONSE] === $ok && !empty($dataRecognize[$decodeSerParam])) {
                $this->setParam($setParam, $dataRecognize[$decodeSerParam]);
                $this->executionDelayed(static::SLEEP_BETWEEN);

                return true;
            } elseif ($dataRecognize[static::DECODE_PARAM_RESPONSE] === $repeat) {
                continue;
            }

            throw new DeCaptchaErrors($dataRecognize[$error], null, $this->errorLang);
        }

        throw new DeCaptchaErrors(DeCaptchaErrors::ERROR_LIMIT, null, $this->errorLang);
    }

    /**
     * Универсальная отправка.
     *
     * @param string $action
     *
     * @return array
     */
    protected function requestUniversal($action)
    {
        $this->setParam(static::ACTION_FIELD_ACTION, $action);
        $response = $this->getResponse(static::ACTION_UNIVERSAL);

        return $this->decodeResponse(static::DECODE_ACTION_UNIVERSAL, $response);
    }

    /**
     * @throws DeCaptchaErrors
     *
     * @return bool
     */
    protected function requestRecognize()
    {
        return $this->requestRepeat(static::ACTION_RECOGNIZE, static::DECODE_ACTION_RECOGNIZE, static::PARAM_SPEC_CAPTCHA, static::DECODE_PARAM_CAPTCHA, static::RESPONSE_RECOGNIZE_OK, static::SLEEP_RECOGNIZE, static::RESPONSE_RECOGNIZE_REPEAT);
    }

    /**
     * @throws DeCaptchaErrors
     *
     * @return bool
     */
    protected function requestCode()
    {
        return $this->requestRepeat(static::ACTION_UNIVERSAL_WITH_CAPTCHA, static::DECODE_ACTION_GET, static::PARAM_SPEC_CODE, static::DECODE_PARAM_CODE, static::RESPONSE_GET_OK, static::SLEEP_GET, static::RESPONSE_GET_REPEAT);
    }

    /**
     * @return array|string
     */
    public function getCode()
    {
        return $this->getParamSpec(static::PARAM_SPEC_CODE);
    }

    /**
     * @return string
     */
    public function getError()
    {
        return $this->errorObject->getMessage();
    }

    /**
     * @param bool $causeAnError
     */
    public function setCauseAnError($causeAnError)
    {
        $this->causeAnError = $causeAnError;
    }

    public function getWiki($lang = 'en')
    {
        $this->wiki->setLang($lang);

        return $this->wiki;
    }
}