Completed
Push — master ( 259b7d...23a921 )
by Владислав
02:16
created

DeCaptchaAbstract::decodeResponse()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 6
cts 6
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 5
nop 2
crap 7
1
<?php
2
3
namespace jumper423\decaptcha\core;
4
5
use Exception;
6
7
/**
8
 * Class DeCaptchaAbstract.
9
 */
10
abstract class DeCaptchaAbstract implements DeCaptchaInterface
11
{
12
    const RESPONSE_TYPE_STRING = 0;
13
    const RESPONSE_TYPE_JSON = 1;
14
15
    const ACTION_FIELDS = 0;
16
    const ACTION_URI = 1;
17
    const ACTION_METHOD = 2;
18
19
    const ACTION_METHOD_POST = 0;
20
    const ACTION_METHOD_GET = 1;
21
22
    const DECODE_FORMAT = 0;
23
    const DECODE_ACTION = 1;
24
    const DECODE_SEPARATOR = 2;
25
    const DECODE_PARAMS = 3;
26
    const DECODE_PARAM_SETTING_MARKER = 4;
27
28
    const PARAM_FIELD_TYPE_STRING = 0;
29
    const PARAM_FIELD_TYPE_INTEGER = 1;
30
    const PARAM_FIELD_TYPE_MIX = 2;
31
32
    const PARAM_SLUG_DEFAULT = 0;
33
    const PARAM_SLUG_TYPE = 1;
34
    const PARAM_SLUG_REQUIRE = 2;
35
    const PARAM_SLUG_SPEC = 3;
36
    const PARAM_SLUG_VARIABLE = 4;
37
38
    const PARAM_SPEC_API_KEY = -1;
39
    const PARAM_SPEC_FILE = -2;
40
    const PARAM_SPEC_CAPTCHA = -3;
41
    const PARAM_SPEC_CODE = -4;
42
43
    /**
44
     * Сервис на который будем загружать капчу.
45
     *
46
     * @var string
47
     */
48
    protected $host;
49
    protected $scheme = 'http';
50
    protected $errorLang = DeCaptchaErrors::LANG_EN;
51
    protected $lastRunTime = null;
52
    /** @var DeCaptchaErrors */
53
    protected $errorObject;
54
    protected $causeAnError = false;
55
56
    protected $limit = [];
57
    protected $paramsSpec = [];
58
    protected $params = [];
59
    protected $limitSettings = [];
60
    protected $decodeSettings = [];
61
    protected $actions = [];
62
    protected $paramsNames = [];
63
64
    protected function resetLimits()
65
    {
66
        foreach ($this->limitSettings as $action => $value) {
67
            $this->limit[$action] = $value;
68
        }
69
    }
70
71
    /**
72
     * @param $action
73
     *
74
     * @return bool
75
     */
76
    protected function limitHasNotYetEnded($action)
77
    {
78
        return $this->limit[$action]-- > 0;
79
    }
80
81
    /**
82
     * @param $action
83
     * @param $data
84
     *
85
     * @throws DeCaptchaErrors
86
     *
87
     * @return array
88
     */
89
    protected function decodeResponse($action, $data)
90
    {
91
        if (!array_key_exists($action, $this->decodeSettings[static::DECODE_ACTION])) {
92 3
            throw new DeCaptchaErrors('нет action');
93
        }
94 3
        $decodeSetting = $this->decodeSettings[static::DECODE_ACTION][$action];
95 1
        $decodeFormat = array_key_exists(static::DECODE_FORMAT, $decodeSetting) ?
96 1
            $decodeSetting[static::DECODE_FORMAT] :
97 3
            $this->decodeSettings[static::DECODE_FORMAT];
98
        $values = [];
99 3
        switch ($decodeFormat) {
100
            case static::RESPONSE_TYPE_STRING:
101
                foreach (explode($decodeSetting[static::DECODE_SEPARATOR], $data) as $key => $value) {
102
                    foreach ($decodeSetting[static::DECODE_PARAMS] as $param => $paramSetting) {
103
                        if ($key === $paramSetting[static::DECODE_PARAM_SETTING_MARKER]) {
104
                            $values[$param] = $value;
105
                        }
106
                    }
107
                }
108
                break;
109
        }
110
111
        return $values;
112
    }
113
114 3
    /**
115 3
     * @param $errorLang
116 2
     */
117 2
    public function setErrorLang($errorLang)
118 1
    {
119 1
        $this->errorLang = $errorLang;
120 1
    }
121 1
122
    /**
123
     * Узнаём путь до файла
124 1
     * Если передана ссылка, то скачиваем и кладём во временную директорию.
125 1
     *
126
     * @param string $fileName
127 2
     *
128 1
     * @throws Exception
129
     *
130 1
     * @return string
131
     */
132
    protected function getFilePath($fileName)
133
    {
134
        if (strpos($fileName, 'http://') !== false || strpos($fileName, 'https://') !== false) {
135
            try {
136 4
                $current = file_get_contents($fileName);
137 4
            } catch (\Exception $e) {
138
                throw new DeCaptchaErrors(DeCaptchaErrors::ERROR_FILE_IS_NOT_LOADED, $fileName, $this->errorLang);
139
            }
140
            $path = tempnam(sys_get_temp_dir(), 'captcha');
141
            file_put_contents($path, $current);
142
143
            return $path;
144 2
        }
145 2
        if (file_exists($fileName)) {
146
            return $fileName;
147
        }
148
        throw new DeCaptchaErrors(DeCaptchaErrors::ERROR_FILE_NOT_FOUND, $fileName, $this->errorLang);
149
    }
150
151
    /**
152 1
     * @param $action
153 1
     *
154
     * @return string
155
     */
156
    protected function getActionUrl($action)
157
    {
158
        return $this->getBaseUrl().$this->actions[$action][static::ACTION_URI];
159 1
    }
160 1
161
    /**
162
     * @return string
163
     */
164
    protected function getBaseUrl()
165
    {
166
        return "{$this->scheme}://{$this->host}/";
167
    }
168
169 2
    /**
170
     * @param $params
171 2
     */
172 1
    public function setParams($params)
173
    {
174 1
        if (is_array($params)) {
175
            foreach ($params as $param => $value) {
176
                $this->params[$param] = $value;
177
            }
178
        }
179
    }
180
181
    /**
182
     * @param $param
183
     * @param $value
184
     */
185 1
    public function setParam($param, $value)
186 1
    {
187 1
        $this->params[$param] = $value;
188 1
    }
189 1
190 1
    /**
191 1
     * @param $param
192 1
     *
193
     * @return \CURLFile|mixed|null|string
194
     */
195
    public function getParamSpec($param)
196
    {
197
        if (!array_key_exists($param, $this->params) || is_null($this->params[$param])) {
198
            return null;
199
        }
200
        switch ($param) {
201
            case static::PARAM_SPEC_FILE:
202
                return (version_compare(PHP_VERSION, '5.5.0') >= 0) ? new \CURLFile($this->getFilePath($this->params[$param])) : '@'.$this->getFilePath($this->params[$param]);
203
            case static::PARAM_SPEC_API_KEY:
204
                return is_callable($this->params[$param]) ? $this->params[$param]() : $this->params[$param];
205
            case static::PARAM_SPEC_CAPTCHA:
206
                return (int) $this->params[$param];
207
            case static::PARAM_SPEC_CODE:
208
                return (string) $this->params[$param];
209
        }
210
211
        return null;
212
    }
213
214
    /**
215
     * @param $action
216
     *
217
     * @throws DeCaptchaErrors
218
     *
219
     * @return array
220
     */
221
    protected function getParams($action)
222
    {
223
        if (empty($this->actions[$action])) {
224
            return [];
225
        }
226
        $params = [];
227
        foreach ($this->actions[$action][static::ACTION_FIELDS] as $field => $settings) {
228
            $value = null;
229
            if (array_key_exists($field, $this->params) && (!array_key_exists(self::PARAM_SLUG_VARIABLE, $settings) ^ (array_key_exists(self::PARAM_SLUG_VARIABLE, $settings) && $settings[self::PARAM_SLUG_VARIABLE] === false))) {
230
                $value = $this->params[$field];
231
            }
232
            if (array_key_exists(self::PARAM_SLUG_DEFAULT, $settings)) {
233
                $value = $settings[self::PARAM_SLUG_DEFAULT];
234
            }
235
            if (array_key_exists(self::PARAM_SLUG_SPEC, $settings) && array_key_exists($settings[self::PARAM_SLUG_SPEC], $this->params)) {
236
                $value = $this->getParamSpec($settings[self::PARAM_SLUG_SPEC]);
237
            }
238
            if (is_null($value)) {
239
                if (array_key_exists(self::PARAM_SLUG_REQUIRE, $settings) && $settings[self::PARAM_SLUG_REQUIRE] === true) {
240
                    throw new DeCaptchaErrors(DeCaptchaErrors::ERROR_PARAM_REQUIRE, array_key_exists($field, $this->paramsNames) ? $this->paramsNames[$field] : $field, $this->errorLang);
241
                }
242
                continue;
243
            }
244
            if (array_key_exists($field, $this->paramsNames)) {
245
                switch ($settings[self::PARAM_SLUG_TYPE]) {
246
                    case self::PARAM_FIELD_TYPE_INTEGER:
247
                        $params[$this->paramsNames[$field]] = (int) $value;
248
                        break;
249
                    case self::PARAM_FIELD_TYPE_STRING:
250
                        $params[$this->paramsNames[$field]] = (string) $value;
251
                        break;
252
                    case self::PARAM_FIELD_TYPE_MIX:
253
                        $params[$this->paramsNames[$field]] = $value;
254
                        break;
255
                }
256
            }
257
        }
258
259
        return $params;
260
    }
261
262
    /**
263
     * @param string $action
264
     *
265
     * @return string
266
     */
267
    protected function getResponse($action)
268
    {
269
        return $this->getCurlResponse($this->getActionUrl($action), $this->getParams($action), $this->actions[$action][static::ACTION_METHOD] === static::ACTION_METHOD_POST);
270
    }
271
272
    /**
273
     * Задержка выполнения.
274
     *
275
     * @param int           $delay    Количество секунд
276
     * @param \Closure|null $callback
277
     *
278
     * @return mixed
279
     */
280
    protected function executionDelayed($delay = 0, $callback = null)
281
    {
282
        $time = microtime(true);
283
        $timePassed = $time - $this->lastRunTime;
284
        if ($timePassed < $delay) {
285
            usleep(($delay - $timePassed) * 1000000);
286
        }
287
        $this->lastRunTime = microtime(true);
288
289
        return $callback instanceof \Closure ? $callback($this) : $callback;
290
    }
291
292
    /**
293
     * @param string $url
294
     * @param $data
295
     * @param bool $isPost
296
     * @param bool $isJson
297
     *
298
     * @throws DeCaptchaErrors
299
     *
300
     * @return mixed
301
     */
302
    protected function getCurlResponse($url, $data, $isPost = true, $isJson = false)
303
    {
304
        $ch = curl_init();
305
        if ($isJson) {
306
            $data = json_encode($data);
307
        } elseif (!$isPost) {
308
            $uri = [];
309
            foreach ($data as $key => $value) {
310
                $uri[] = "$key=$value";
311
            }
312
            $url .= '?'.implode('&', $uri);
313
        }
314
        curl_setopt($ch, CURLOPT_URL, $url);
315
        if (!$isJson && version_compare(PHP_VERSION, '5.5.0') >= 0 && version_compare(PHP_VERSION, '7.0') < 0 && defined('CURLOPT_SAFE_UPLOAD')) {
316
            curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
317
        }
318
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
319
        curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate");
320
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
321
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
322
        curl_setopt($ch, CURLOPT_POST, $isPost);
323
        if ($isPost) {
324
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
325
        }
326
        if ($isJson) {
327
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
328
                'Content-Type: application/json; charset=utf-8',
329
                'Accept: application/json',
330
                'Content-Length: ' . strlen($data)
331
            ]);
332
        }
333
        $result = curl_exec($ch);
334
        if (curl_errno($ch)) {
335
            throw new DeCaptchaErrors(DeCaptchaErrors::ERROR_CURL, curl_error($ch), $this->errorLang);
336
        }
337
        curl_close($ch);
338
339
        return $result;
340
    }
341
342
    abstract public function getCode();
343
344
    abstract public function getError();
345
}
346