Completed
Push — master ( 96db75...825fc2 )
by Владислав
02:12
created

DeCaptchaAbstract   D

Complexity

Total Complexity 60

Size/Duplication

Total Lines 326
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 1

Test Coverage

Coverage 70.9%

Importance

Changes 0
Metric Value
wmc 60
lcom 4
cbo 1
dl 0
loc 326
ccs 39
cts 55
cp 0.709
rs 4.2857
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
C decodeResponse() 0 24 7
A resetLimits() 0 6 2
A limitHasNotYetEnded() 0 4 1
A notTrue() 0 4 1
A setErrorLang() 0 4 1
B getFilePath() 0 18 5
A getActionUrl() 0 4 1
A getBaseUrl() 0 4 1
A setParams() 0 8 3
A setParamSpec() 0 4 1
B getParamSpec() 0 17 9
D getParams() 0 37 17
A getResponse() 0 4 1
A executionDelayed() 0 11 3
C getCurlResponse() 0 27 7
getCode() 0 1 ?
getError() 0 1 ?

How to fix   Complexity   

Complex Class

Complex classes like DeCaptchaAbstract often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DeCaptchaAbstract, and based on these observations, apply Extract Interface, too.

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