jumper423/decaptcha

View on GitHub
src/core/DeCaptchaWiki.php

Summary

Maintainability
F
4 days
Test Coverage
<?php

namespace jumper423\decaptcha\core;

/**
 * Class DeCaptchaAbstract.
 */
class DeCaptchaWiki
{
    protected $texts = [];
    /**
     * @var DeCaptchaBase
     */
    protected $class;
    protected $lang = 'en';

    public function setLang($lang)
    {
        $this->lang = $lang;
    }

    public function __construct($class)
    {
        $this->class = $class;
        $this->texts = [
            'constructor_data' => [
                $class::ACTION_FIELD_KEY => '94f39af4bb295c40546fba5c932e0d32',
            ],
            'recognize_file'                            => true,
            'recognize_data_file'                       => 'http://site.com/captcha.jpg',
            'recognize_data'                            => [],
            'field_main_name_'.$class::ACTION_FIELD_KEY => [
                'ru' => 'Ключ',
                'en' => 'Key',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_KEY => [
                'ru' => 'Ключ от учетной записи',
                'en' => 'Key account',
            ],
            'field_main_name_'.$class::ACTION_FIELD_LANGUAGE => [
                'ru' => 'Язык',
                'en' => 'Language',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_LANGUAGE => [
                'ru' => 'На каком языке текст на капче',
                'en' => 'What language the text on the captcha',
            ],
            'field_main_name_'.$class::ACTION_FIELD_LANG => [
                'ru' => 'Код языка',
                'en' => 'Language code',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_LANG => [
                'ru' => 'См. список поддерживаемых языков. https://rucaptcha.com/api-rucaptcha#language',
                'en' => 'See the list of supported languages. https://2captcha.com/api-rucaptcha#language',
            ],
            'field_main_name_'.$class::ACTION_FIELD_FILE => [
                'ru' => 'Картинка',
                'en' => 'Picture',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_FILE => [
                'ru' => 'Путь на файл с картинкой или ссылка на него',
                'en' => 'The path to the picture file or link to it',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PHRASE => [
                'ru' => 'Несколько слов',
                'en' => 'A few words',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PHRASE => [
                'ru' => 'Работник должен ввести текст с одним или несколькими пробелами',
                'en' => 'The worker must enter text with one or more spaces',
            ],
            'field_main_name_'.$class::ACTION_FIELD_REGSENSE => [
                'ru' => 'Регистр',
                'en' => 'Register',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_REGSENSE => [
                'ru' => 'Работник должен ввести ответ с учетом регистра',
                'en' => 'The worker must enter the answer case sensitive',
            ],
            'field_main_name_'.$class::ACTION_FIELD_NUMERIC => [
                'ru' => 'Символы',
                'en' => 'Characters',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_NUMERIC => [
                'ru' => 'Какие символы используется в капче',
                'en' => 'What are the symbols used in captcha',
            ],
            'field_main_name_'.$class::ACTION_FIELD_CALC => [
                'ru' => 'Вычисление',
                'en' => 'Calculation',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_CALC => [
                'ru' => 'На капче изображенно математичекая выражение и её необходимо решить',
                'en' => 'The captcha shows matematicheskaya expression and must be addressed',
            ],
            'field_main_name_'.$class::ACTION_FIELD_MIN_LEN => [
                'ru' => 'Длина min',
                'en' => 'Length min',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_MIN_LEN => [
                'ru' => 'Минимальная длина капчи',
                'en' => 'The minimum length of captcha',
            ],
            'field_main_name_'.$class::ACTION_FIELD_MAX_LEN => [
                'ru' => 'Длина max',
                'en' => 'Length max',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_MAX_LEN => [
                'ru' => 'Максимальная длина капчи',
                'en' => 'The maximum length of the captcha',
            ],
            'field_main_name_'.$class::ACTION_FIELD_QUESTION => [
                'ru' => 'Вопрос',
                'en' => 'Question',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_QUESTION => [
                'ru' => 'На изображении задан вопрос, работник должен написать ответ',
                'en' => 'The image asked, the employee must write the answer',
            ],
            'field_main_name_'.$class::ACTION_FIELD_IS_RUSSIAN => [
                'ru' => 'Кириллица',
                'en' => 'Cyrillic',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_IS_RUSSIAN => [
                'ru' => 'На изображении присутствуют русские символы',
                'en' => 'In the image there are Russian characters',
            ],
            'field_main_name_'.$class::ACTION_FIELD_LANGUAGE => [
                'ru' => 'Язык',
                'en' => 'Language',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_LANGUAGE => [
                'ru' => 'Символы какого языка размещенны на капче',
                'en' => 'The symbols of the language posted on the captcha',
            ],
            'field_main_name_'.$class::ACTION_FIELD_HEADER_ACAO => [
                'ru' => 'Кросс-доменный',
                'en' => 'Cross-domain',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_HEADER_ACAO => [
                'ru' => 'Необходимо для кросс-доменных AJAX запросов в браузерных приложениях.',
                'en' => 'Need for cross-domain AJAX requests in browser-based applications.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_INSTRUCTIONS => [
                'ru' => 'Инструкция',
                'en' => 'Manual',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_INSTRUCTIONS => [
                'ru' => 'Текстовая капча или инструкция для прохождения капчи.',
                'en' => 'Text captcha or manual to pass the captcha.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PINGBACK => [
                'ru' => 'Ответ на',
                'en' => 'Response to',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PINGBACK => [
                'ru' => 'Указание для сервера, что после распознания изображения, нужно отправить ответ на указанный адрес.',
                'en' => 'Note to server, after recognizing the image, you need to send a reply to the specified address.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_LABEL => [
                'ru' => 'От куда',
                'en' => 'From where',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_LABEL => [
                'ru' => 'Пояснение от куда пришла капча ("vk", "google", "recaptcha", "yandex", "mailru", "yahoo" и т.д.).',
                'en' => 'Clarification from where came the captcha ("vk", "google", "recaptcha", "yandex", "Google", "yahoo", etc.).',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PAGEURL => [
                'ru' => 'Адрес',
                'en' => 'Link',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PAGEURL => [
                'ru' => 'Адрес страницы на которой решается капча.',
                'en' => 'The address of the page where the captcha is solved.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_GOOGLEKEY => [
                'ru' => 'Google key',
                'en' => 'Google key',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_GOOGLEKEY => [
                'ru' => 'Ключ-индентификатор рекапчи на целевой странице. <div class="g-recaptcha" data-sitekey="ВОТ_ЭТОТ"></div>',
                'en' => 'Key-the identifier of the recaptcha on the landing page. <div class="g-recaptcha" data-sitekey="THIS"></div>',
            ],
            'field_main_name_'.$class::ACTION_FIELD_GOOGLETOKEN => [
                'ru' => 'Google token',
                'en' => 'Google token',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_GOOGLETOKEN => [
                'ru' => 'Секретный токен для предыдущей версии рекапчи. В большинстве случаев сайты используют новую версию и этот токен не требуется. Секретный токен генерируется на сервере Google и вставляется на страницу в атрибуте data-stoken. Выглядит это примерно так: <script type="text/javascript" src="...." data-type="normal"  data-ray="..." async data-sitekey="..." data-stoken="ВОТ_ЭТОТ"></script> Токен действует пару минут после генерации, затем нужно снова зайти на страницу и получить его.',
                'en' => 'The secret token for the previous version of recaptcha. In most cases, sites use the new version and this token is not required. The secret token is generated on a Google server and inserted into the page in the attribute data-stoken. It looks like this: <script type="text/javascript" src="...." data-type="normal" data-ray="..." async data-sitekey="..." data-stoken="THIS"></script> the Token is valid a few minutes after generation, then you need to go back to the page and get it.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_INVISIBLE => [
                'ru' => 'Невидимая ReCaptcha',
                'en' => 'Invisible ReCaptcha',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_INVISIBLE => [
                'ru' => '1 — говорит нам, что на сайте невидимая ReCaptcha. 0 — обычная ReCaptcha.',
                'en' => '1 - tells us that the site is invisible ReCaptcha. 0 - regular ReCaptcha.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_SSC_USER_ID => [
                'ru' => 'Параметра s_s_c_user_id',
                'en' => 'Parameter s_s_c_user_id',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_SSC_USER_ID => [
                'ru' => 'Значение параметра s_s_c_user_id, найденное на странице',
                'en' => 'The value of the s_s_c_user_id parameter found on the page',
            ],
            'field_main_name_'.$class::ACTION_FIELD_SSC_SESSION_ID => [
                'ru' => 'Параметра s_s_c_session_id',
                'en' => 'Parameter s_s_c_session_id',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_SSC_SESSION_ID => [
                'ru' => 'Значение параметра s_s_c_session_id, найденное на странице',
                'en' => 'The value of the s_s_c_session_id parameter found on the page',
            ],
            'field_main_name_'.$class::ACTION_FIELD_SSC_WEB_SERVER_SIGN => [
                'ru' => 'Параметра s_s_c_web_server_sign',
                'en' => 'Parameter s_s_c_web_server_sign',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_SSC_WEB_SERVER_SIGN => [
                'ru' => 'Значение параметра s_s_c_web_server_sign, найденное на странице',
                'en' => 'The value of the s_s_c_web_server_sign parameter found on the page',
            ],
            'field_main_name_'.$class::ACTION_FIELD_SSC_WEB_SERVER_SIGN2 => [
                'ru' => 'Параметра s_s_c_web_server_sign2',
                'en' => 'Parameter s_s_c_web_server_sign2',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_SSC_WEB_SERVER_SIGN2 => [
                'ru' => 'Значение параметра s_s_c_web_server_sign2, найденное на странице',
                'en' => 'The value of the s_s_c_web_server_sign2 parameter found on the page',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PUBLICKEY => [
                'ru' => 'Параметра data-pkey',
                'en' => 'Parameter data-pkey',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PUBLICKEY => [
                'ru' => 'Найти div с FunCaptcha и посмотреть на значение параметра data-pkey или же найти элемент с именем (name) fc-token, а из его значения вырезать ключ, который указан после pk',
                'en' => 'Find a div with FunCaptcha and look at the value of the data-pkey parameter, or find an element with the name (name) fc-token, and cut the key from its value after the pk',
            ],
            'field_main_name_'.$class::ACTION_FIELD_NOJS => [
                'ru' => 'Истользовать JS',
                'en' => 'Истользовать JS',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_NOJS => [
                'ru' => 'Говорит нам решать FunCaptcha с выключенным javascript. Может быть использован в случае, если нормальный метод по какой-то причине не срабатывает. Важно: имейте в виду, что в этом случае мы вернём только часть токена. Выше описано, что делать в этом случае.',
                'en' => 'Tells us to solve FunCaptcha with javascript turned off. It can be used in case the normal method for some reason does not work. Important: keep in mind that in this case we will return only part of the token. The above is what to do in this case.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_MIN_SCORE => [
                'ru' => 'Минимальный рейтинг',
                'en' => 'Min rating',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_MIN_SCORE => [
                'ru' => 'Требуемое значение рейтинга (score). На текущий момент сложно получить токен со score выше 0.3',
                'en' => 'Required rating value (score). Currently it is difficult to get a token with a score above 0.3',
            ],
            'field_main_name_'.$class::ACTION_FIELD_GT => [
                'ru' => 'Параметр gt',
                'en' => 'gt parameter',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_GT => [
                'ru' => 'Значение параметра gt найденное на сайте',
                'en' => 'The value of the api_server parameter found on the site',
            ],
            'field_main_name_'.$class::ACTION_FIELD_CHALLENGE => [
                'ru' => 'Параметр challenge',
                'en' => 'challenge parameter',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_CHALLENGE => [
                'ru' => 'Значение параметра challenge найденное на сайте',
                'en' => 'The value of the api_server parameter found on the site',
            ],
            'field_main_name_'.$class::ACTION_FIELD_API_SERVER => [
                'ru' => 'Параметр api_server',
                'en' => 'api_server parameter',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_API_SERVER => [
                'ru' => 'Значение параметра api_server найденное на сайте',
                'en' => 'The value of the api_server parameter found on the site',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PROXYTYPE => [
                'ru' => 'Тип прокси',
                'en' => 'The proxy type',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PROXYTYPE => [
                'ru' => 'Тип прокси (http, socks4, ...)',
                'en' => 'The proxy type (http, socks4, ...)',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PROXY => [
                'ru' => 'Адрес прокси',
                'en' => 'The proxy address',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PROXY => [
                'ru' => 'IP адрес прокси ipv4/ipv6.',
                'en' => 'IP address of the proxy ipv4/ipv6.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PROXYPORT => [
                'ru' => 'Порт прокси',
                'en' => 'Proxy port',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PROXYPORT => [
                'ru' => 'Порт прокси.',
                'en' => 'Proxy port.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PROXYLOGIN => [
                'ru' => 'Логин прокси',
                'en' => 'Login proxy',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PROXYLOGIN => [
                'ru' => 'Логин от прокси-сервера.',
                'en' => 'Login from proxy server.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_PROXYPASS => [
                'ru' => 'Пароль прокси',
                'en' => 'Password proxy',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_PROXYPASS => [
                'ru' => 'Пароль от прокси-сервера.',
                'en' => 'The password for the proxy server.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_USERAGENT => [
                'ru' => 'User-Agent браузера',
                'en' => 'User-Agent browser',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_USERAGENT => [
                'ru' => 'User-Agent браузера, используемый в эмуляции. Необходимо использовать подпись современного браузера, иначе Google будет возвращать ошибку, требуя обновить браузер.',
                'en' => 'User-Agent browser used in emulation. You must use the signature modern browser, otherwise Google will return an error requiring you to upgrade your browser.',
            ],
            'field_main_name_'.$class::ACTION_FIELD_COOKIES => [
                'ru' => 'Куки',
                'en' => 'Cookies',
            ],
            'field_main_desc_'.$class::ACTION_FIELD_COOKIES => [
                'ru' => 'Дополнительные cookies которые мы должны использовать во время взаимодействия с целевой страницей.',
                'en' => 'Additional cookies which we should use during the interaction with the target page.',
            ],
            'table_th_name' => [
                'ru' => 'Название',
                'en' => 'Name',
            ],
            'table_th_code' => [
                'ru' => 'Код',
                'en' => 'Code',
            ],
            'table_th_type' => [
                'ru' => 'Тип',
                'en' => 'Type',
            ],
            'table_th_req' => [
                'ru' => 'Обяз.', //Обязательное
                'en' => 'Req.', //Required
            ],
            'table_th_def' => [
                'ru' => 'По ум.', //По умолчания
                'en' => 'By def.', //By default
            ],
            'table_th_enum' => [
                'ru' => 'Возможные значения',
                'en' => 'Possible values',
            ],
            'table_th_desc' => [
                'ru' => 'Описание',
                'en' => 'Description',
            ],
            'slug_link' => [
                'ru' => 'Ссылка',
                'en' => 'Link',
            ],
            'slug_link_to_service' => [
                'ru' => 'Ссылка на сервис',
                'en' => 'The link to the service',
            ],
            'slug_price' => [
                'ru' => 'Цены',
                'en' => 'Prices',
            ],
            'slug_service_desc' => [
                'ru' => 'Описание сервиса',
                'en' => 'The description of the service',
            ],
            'slug_recognize_desc' => [
                'ru' => 'Описание распознания',
                'en' => 'Description recognition',
            ],
            'slug_fields_desc' => [
                'ru' => 'Описание полей',
                'en' => 'A description of the fields',
            ],
            'example' => [
                'ru' => 'Примеры',
                'en' => 'Examples',
            ],
            'example_initialization' => [
                'ru' => 'Инициализация',
                'en' => 'Initialization',
            ],
            'example_initialization_desc' => [
                'ru' => 'Указываем ключ, обязательные и дополнительные параметры. Старайтесь по максимуму их заполнить это способствует более быстрому распознанию капчи.',
                'en' => 'Specify the key mandatory and optional parameters. Try the best to fill this promotes more rapid recognition of captcha.',
            ],
            'example_recognize' => [
                'ru' => 'Распознавание',
                'en' => 'Recognition',
            ],
            'example_recognize_desc' => [
                'ru' => 'В первом параметре передаём ссылку или путь на файл с картинкой, во второй параметры распознания при необходимости переопределения тех которые были переданы при инициализации.',
                'en' => 'In the first parameter, pass the link or path to the picture file in the second parameters of the recognition if necessary, override those which were transferred during the initialization.',
            ],
            'example_nottrue' => [
                'ru' => 'Не верно распознано',
                'en' => 'Not correctly recognized',
            ],
            'example_nottrue_desc' => [
                'ru' => 'Если Вы сможете понять что ответ которые пришёл не верные. Обязательно добавьте ниже написанный код. Это Вам съекономит деньги.',
                'en' => 'If You can understand that the answer which did not come true. Be sure to add below written code. It will save You money.',
            ],
            'example_balance' => [
                'ru' => 'Баланс',
                'en' => 'Balance',
            ],
            'example_error_lang_if' => [
                'ru' => true,
                'en' => false,
            ],
            'example_error_lang' => [
                'ru' => 'Язык ошибки',
                'en' => 'Language errors',
            ],
            'example_error_lang_desc' => [
                'ru' => 'По умолчанию ошибки на англиском языке, если необходимо переоперелить, сделайте следующее',
                'en' => 'Default error in English, if you want to properlyt, do the following',
            ],
            'example_error_interception' => [
                'ru' => 'Перехват ошибки',
                'en' => 'Intercept errors',
            ],
            'example_error_interception_desc' => [
                'ru' => 'При желании Вы можете перехватывать ошибку, но для этого надо вызвать setCauseAnError',
                'en' => 'If you wish, You can catch the error, but you need to call setCauseAnError',
            ],
            'install' => [
                'ru' => 'Установка',
                'en' => 'Installation',
            ],
            'install_preferred' => [
                'ru' => 'Предпочтительный способ установить это расширение через',
                'en' => 'The preferred way to install this extension via',
            ],
            'install_start' => [
                'ru' => 'Либо запустить',
                'en' => 'Or you can run',
            ],
            'install_add' => [
                'ru' => 'или добавить',
                'en' => 'or add',
            ],
            'install_add_file' => [
                'ru' => 'в файл',
                'en' => 'in file',
            ],
            'slug_menu' => [
                'ru' => 'Меню',
                'en' => 'Menu',
            ],
            'slug_menu_main' => [
                'ru' => 'Главная',
                'en' => 'Main',
            ],
            'slug_menu_another' => [
                'ru' => 'Documentation in English language',
                'en' => 'Документация на русском языке',
            ],
            'slug_menu_anchor' => [
                'ru' => 'Якоря',
                'en' => 'Anchor',
            ],
            'slug_menu_from_service' => [
                'ru' => 'Другой функционал от сервиса',
                'en' => 'Other functionality from the service',
            ],
        ];
    }

    /**
     * @param string|array      $name
     * @param string|array|bool $value
     */
    public function setText($name, $value)
    {
        if (is_array($name)) {
            $name = implode('_', $name);
        }
        $this->texts[$name] = $value;
    }

    /**
     * @param string|array $name
     * @param string       $separator
     *
     * @return string|array
     */
    public function getText($name, $separator = '; ')
    {
        $getResult = function ($name, $texts) {
            if (is_array($name)) {
                $name = implode('_', $name);
            }
            if (!isset($texts[$name])) {
                return null;
            }
            if (is_array($texts[$name])) {
                if (isset($texts[$name][$this->lang])) {
                    return $texts[$name][$this->lang];
                }

                return array_values($texts[$name])[0];
            }

            return $texts[$name];
        };
        $result = $getResult($name, $this->texts);
        if (is_array($result)) {
            if ($separator) {
                $result = implode($separator, $result);
            }
        }

        return $result;
    }

    protected function viewInstall()
    {
        $str = "{$this->getText(['install', 'preferred'])} [composer](http://getcomposer.org/download/).".PHP_EOL;
        $str .= PHP_EOL;
        $str .= "{$this->getText(['install', 'start'])}".PHP_EOL;
        $str .= '```'.PHP_EOL;
        $str .= 'composer require --prefer-dist jumper423/decaptcha "*"'.PHP_EOL;
        $str .= '```'.PHP_EOL;
        $str .= "{$this->getText(['install', 'add'])}".PHP_EOL;
        $str .= '```'.PHP_EOL;
        $str .= '"jumper423/decaptcha": "*"'.PHP_EOL;
        $str .= '```'.PHP_EOL;
        $str .= "{$this->getText(['install', 'add', 'file'])} `composer.json`.".PHP_EOL;

        return $str;
    }

    protected function viewExamples()
    {
        $class = $this->class;
        $reflection = (new \ReflectionClass($class));

        $str = "__{$this->getText(['example', 'initialization'])}__".PHP_EOL;
        $str .= "{$this->getText(['example', 'initialization', 'desc'])}".PHP_EOL;
        $str .= '```'.PHP_EOL;
        $str .= "use {$reflection->getName()};".PHP_EOL;
        $str .= ''.PHP_EOL;
        $str .= '$captcha = new '.$reflection->getShortName().'(['.PHP_EOL;
        foreach ($this->texts['constructor_data'] as $key => $val) {
            $str .= "    {$reflection->getShortName()}::{$this->getNameConst('ACTION_FIELD_', $key)} => ";
            $str .= is_string($val) ? "'{$val}'" : $val;
            $str .= ','.PHP_EOL;
        }
        $str .= ']);'.PHP_EOL;
        $str .= '```'.PHP_EOL;

        $str .= "__{$this->getText(['example', 'recognize'])}__".PHP_EOL;
        $str .= "{$this->getText(['example', 'recognize', 'desc'])}".PHP_EOL;
        $str .= '```'.PHP_EOL;
        $str .= 'if ($captcha->recognize(';
        $str .= $this->getRecognizeFile();
        $str .= $this->getRecognizeData();
        $str .= ')) {'.PHP_EOL;
        $str .= '    $code = $captcha->getCode();'.PHP_EOL;
        $str .= '} else {'.PHP_EOL;
        $str .= '    $error = $captcha->getError();'.PHP_EOL;
        $str .= '}'.PHP_EOL;
        $str .= '```'.PHP_EOL;

        if (in_array('notTrue', get_class_methods($class))) {
            $str .= "__{$this->getText(['example', 'nottrue'])}__".PHP_EOL;
            $str .= "{$this->getText(['example', 'nottrue', 'desc'])}".PHP_EOL;
            $str .= '```'.PHP_EOL;
            $str .= '$captcha->notTrue();'.PHP_EOL;
            $str .= '```'.PHP_EOL;
        }

        if (in_array('getBalance', get_class_methods($class))) {
            $str .= "__{$this->getText(['example', 'balance'])}__".PHP_EOL;
            $str .= '```'.PHP_EOL;
            $str .= '$balance = $captcha->getBalance();'.PHP_EOL;
            $str .= '```'.PHP_EOL;
        }

        if ($this->getText(['example', 'error', 'lang', 'if'])) {
            $str .= "__{$this->getText(['example', 'error', 'lang'])}__".PHP_EOL;
            $str .= "{$this->getText(['example', 'error', 'lang', 'desc'])}".PHP_EOL;
            $str .= '```'.PHP_EOL;
            $str .= '$captcha->setErrorLang(\jumper423\decaptcha\core\DeCaptchaErrors::LANG_RU);'.PHP_EOL;
            $str .= '```'.PHP_EOL;
        }

        $str .= "__{$this->getText(['example', 'error', 'interception'])}__".PHP_EOL;
        $str .= "{$this->getText(['example', 'error', 'interception', 'desc'])}".PHP_EOL;
        $str .= '```'.PHP_EOL;
        $str .= '$captcha->setCauseAnError(true);'.PHP_EOL;
        $str .= PHP_EOL;
        $str .= 'try {'.PHP_EOL;
        $str .= '    $captcha->recognize(';
        $str .= $this->getRecognizeFile();
        $str .= $this->getRecognizeData();
        $str .= ');'.PHP_EOL;
        $str .= '    $code = $captcha->getCode();'.PHP_EOL;
        $str .= '} catch (\jumper423\decaptcha\core\DeCaptchaErrors $e) {'.PHP_EOL;
        $str .= '    ...'.PHP_EOL;
        $str .= '}'.PHP_EOL;
        $str .= '```'.PHP_EOL;

        return $str;
    }

    protected function getRecognizeData()
    {
        $class = $this->class;
        $reflection = (new \ReflectionClass($class));
        $str = '';
        if ($this->texts['recognize_data']) {
            if ($this->texts['recognize_file']) {
                $str .= ', ';
            }
            $str .= '['.PHP_EOL;
            foreach ($this->texts['recognize_data'] as $key => $val) {
                $str .= "       {$reflection->getShortName()}::{$this->getNameConst('ACTION_FIELD_', $key)} => ";
                $str .= is_string($val) ? "'{$val}'" : $val;
                $str .= ','.PHP_EOL;
            }
            $str .= '    ]';
        }

        return $str;
    }

    protected function getRecognizeFile()
    {
        if (!$this->texts['recognize_file']) {
            return '';
        }

        return "'{$this->getText(['recognize', 'data', 'file'])}'";
    }

    protected function viewFields()
    {
        $class = $this->class;
        $str = " {$this->getText(['table', 'th', 'name'])} | {$this->getText(['table', 'th', 'code'])} | {$this->getText(['table', 'th', 'type'])} | {$this->getText(['table', 'th', 'req'])} | {$this->getText(['table', 'th', 'def'])} | {$this->getText(['table', 'th', 'enum'])} | {$this->getText(['table', 'th', 'desc'])} ".PHP_EOL;
        $str .= ' --- | --- | --- | --- | --- | --- | --- '.PHP_EOL;
        foreach ($this->class->actions[$class::ACTION_RECOGNIZE][$class::ACTION_FIELDS] as $param => $setting) {
            if (array_key_exists($class::ACTION_FIELDS, $setting) && is_array($setting[$class::ACTION_FIELDS])) {
                foreach ($setting[$class::ACTION_FIELDS] as $param1 => $setting1) {
                    if (array_key_exists($class::PARAM_SLUG_NOTWIKI, $setting1) && $setting1[$class::PARAM_SLUG_NOTWIKI] === true) {
                        continue;
                    }
                    $str .= $this->viewFieldLine($param1, $setting1);
                }
            }
            if (array_key_exists($class::PARAM_SLUG_NOTWIKI, $setting) && $setting[$class::PARAM_SLUG_NOTWIKI] === true) {
                continue;
            }
            $str .= $this->viewFieldLine($param, $setting);
        }

        return $str;
    }

    protected function viewFieldLine($param, $setting)
    {
        $class = $this->class;
        $str = " {$this->getText(['field', 'main', 'name', $param])} |";
        $str .= " {$this->getNameConst('ACTION_FIELD_', $param)} |";
        $str .= ' '.substr($this->getNameConst('PARAM_FIELD_TYPE_', $setting[$class::PARAM_SLUG_TYPE]), 17).' |';
        $str .= ' '.(array_key_exists($class::PARAM_SLUG_REQUIRE, $setting) ? '+' : '-').' |';
        $str .= ' '.(array_key_exists($class::PARAM_SLUG_DEFAULT, $setting) ? $setting[$class::PARAM_SLUG_DEFAULT] : '').' |';
        $str .= " {$this->getText(['field', 'slug', $class::PARAM_SLUG_ENUM, $param])} |";
        $str .= " {$this->getText(['field', 'main', 'desc', $param])} |";
        $str .= PHP_EOL;

        return $str;
    }

    protected function viewMenu()
    {
        $str = "+ [{$this->getText(['slug', 'menu', 'main'])}](../docs/README-{$this->lang}.md)".PHP_EOL;
        $str .= "+ [{$this->getText(['slug', 'menu', 'another'])}](../docs/".$this->getFileName($this->lang == 'ru' ? 'en' : 'ru').')'.PHP_EOL;
        $str .= "+ {$this->getText(['slug', 'menu', 'anchor'])}".PHP_EOL;
        foreach ([
                     ['slug', 'link'],
                     ['slug', 'service', 'desc'],
                     ['slug', 'price'],
                     ['slug', 'recognize', 'desc'],
                     ['install'],
                     ['example'],
                     ['slug', 'fields', 'desc'],
                 ] as $anchor) {
            $str .= "  + [{$this->getText($anchor)}](#".implode('-', explode(' ', ($this->lang === 'en' ? mb_strtolower($this->getText($anchor)) : $this->getText($anchor)))).')'.PHP_EOL;
        }
        if ($this->getText(['menu', 'from_service'])) {
            $str .= "+ {$this->getText(['slug', 'menu', 'from_service'])}".PHP_EOL;
            foreach ($this->texts['menu_from_service'] as $fromServiceClass) {
                $fromServiceObject = new $fromServiceClass([]);
                $fromServiceObjectWiki = $fromServiceObject->getWiki($this->lang);
                $str .= "  + [{$fromServiceObjectWiki->getText(['service', 'name'])}](../docs/{$fromServiceObjectWiki->getFileName()})".PHP_EOL;
            }
        }

        return $str;
    }

    protected function getNameConst($keyMask, $value)
    {
        $class = $this->class;
        $constants = (new \ReflectionClass($class))->getConstants();
        foreach ($constants as $key => $val) {
            if (stripos($key, $keyMask) !== false && $val === $value) {
                return $key;
            }
        }

        return null;
    }

    public function view()
    {
        $str = $this->getText(['service', 'name']).PHP_EOL;
        $str .= '=============='.PHP_EOL;
        $str .= "{$this->getText(['slug', 'menu'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= $this->viewMenu().PHP_EOL.PHP_EOL;
        $str .= "{$this->getText(['slug', 'link'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= "[{$this->getText(['slug', 'link', 'to_service'])} {$this->getText(['service', 'name'])}]({$this->getText(['service', 'href'])})".PHP_EOL.PHP_EOL;
        $str .= "{$this->getText(['slug', 'service', 'desc'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= "{$this->getText(['service', 'desc'])}".PHP_EOL.PHP_EOL;
        $str .= "{$this->getText(['slug', 'price'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= "{$this->getText(['recognize', 'price'])}".PHP_EOL.PHP_EOL;
        $str .= "{$this->getText(['slug', 'recognize', 'desc'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= "{$this->getText(['recognize', 'desc'])}".PHP_EOL.PHP_EOL;
        $str .= "{$this->getText(['install'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= "{$this->viewInstall()}".PHP_EOL.PHP_EOL;
        $str .= "{$this->getText(['example'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= "{$this->viewExamples()}".PHP_EOL.PHP_EOL;
        $str .= "{$this->getText(['slug', 'fields', 'desc'])}".PHP_EOL;
        $str .= '--------------'.PHP_EOL;
        $str .= $this->viewFields().PHP_EOL;

        return $str;
    }

    public function getFileName($lang = null)
    {
        if (is_null($lang)) {
            $lang = $this->lang;
        }
        $class = $this->class;

        return (new \ReflectionClass($class))->getShortName().'-'.$lang.'.md';
    }

    public function save()
    {
        file_put_contents(__DIR__.'/../../docs/'.$this->getFileName(), $this->view());
    }
}