Completed
Push — master ( 99e989...135640 )
by Владислав
02:11
created

DeCaptchaAbstract::getParamSpec()   C

Complexity

Conditions 10
Paths 9

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 0
cp 0
rs 6.1368
c 0
b 0
f 0
cc 10
eloc 16
nc 9
nop 2
crap 110

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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