Completed
Push — master ( 5001e8...bdb1b7 )
by dotzero
15s
created

Request::parseResponse()   D

Complexity

Conditions 9
Paths 11

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
ccs 17
cts 17
cp 1
rs 4.909
cc 9
eloc 18
nc 11
nop 2
crap 9
1
<?php
2
3
namespace AmoCRM\Request;
4
5
use DateTime;
6
use AmoCRM\Exception;
7
use AmoCRM\NetworkException;
8
9
/**
10
 * Class Request
11
 *
12
 * Класс отправляющий запросы к API amoCRM используя cURL
13
 *
14
 * @package AmoCRM\Request
15
 * @author dotzero <[email protected]>
16
 * @link http://www.dotzero.ru/
17
 * @link https://github.com/dotzero/amocrm-php
18
 *
19
 * For the full copyright and license information, please view the LICENSE
20
 * file that was distributed with this source code.
21
 */
22
class Request
23
{
24
    /**
25
     * @var bool Использовать устаревшую схему авторизации
26
     */
27
    protected $v1 = false;
28
29
    /**
30
     * @var bool Флаг вывода отладочной информации
31
     */
32
    private $debug = false;
33
34
    /**
35
     * @var ParamsBag|null Экземпляр ParamsBag для хранения аргументов
36
     */
37
    private $parameters = null;
38
39
    /**
40
     * @var int|null Последний полученный HTTP код
41
     */
42
    private $lastHttpCode = null;
43
44
    /**
45
     * @var string|null Последний полученный HTTP ответ
46
     */
47
    private $lastHttpResponse = null;
48
49
    /**
50
     * Request constructor
51
     *
52
     * @param ParamsBag $parameters Экземпляр ParamsBag для хранения аргументов
53
     * @throws NetworkException
54
     */
55 221
    public function __construct(ParamsBag $parameters)
56
    {
57 221
        if (!function_exists('curl_init')) {
58
            throw new NetworkException('The cURL PHP extension was not loaded');
59
        }
60
61 221
        $this->parameters = $parameters;
62 221
    }
63
64
    /**
65
     * Установка флага вывода отладочной информации
66
     *
67
     * @param bool $flag Значение флага
68
     * @return $this
69
     */
70 2
    public function debug($flag = false)
71
    {
72 2
        $this->debug = (bool)$flag;
73
74 2
        return $this;
75
    }
76
77
    /**
78
     * Возвращает последний полученный HTTP код
79
     *
80
     * @return int|null
81
     */
82 1
    public function getLastHttpCode()
83
    {
84 1
        return $this->lastHttpCode;
85
    }
86
87
    /**
88
     * Возвращает последний полученный HTTP ответ
89
     *
90
     * @return null|string
91
     */
92 1
    public function getLastHttpResponse()
93
    {
94 1
        return $this->lastHttpResponse;
95
    }
96
97
    /**
98
     * Возвращает экземпляр ParamsBag для хранения аргументов
99
     *
100
     * @return ParamsBag|null
101
     */
102 3
    protected function getParameters()
103
    {
104 3
        return $this->parameters;
105
    }
106
107
    /**
108
     * Выполнить HTTP GET запрос и вернуть тело ответа
109
     *
110
     * @param string $url Запрашиваемый URL
111
     * @param array $parameters Список GET параметров
112
     * @param null|string $modified Значение заголовка IF-MODIFIED-SINCE
113
     * @return mixed
114
     * @throws Exception
115
     * @throws NetworkException
116
     */
117 1
    protected function getRequest($url, $parameters = [], $modified = null)
118
    {
119 1
        if (!empty($parameters)) {
120 1
            $this->parameters->addGet($parameters);
121 1
        }
122
123 1
        return $this->request($url, $modified);
124
    }
125
126
    /**
127
     * Выполнить HTTP POST запрос и вернуть тело ответа
128
     *
129
     * @param string $url Запрашиваемый URL
130
     * @param array $parameters Список POST параметров
131
     * @return mixed
132
     * @throws Exception
133
     * @throws NetworkException
134
     */
135 1
    protected function postRequest($url, $parameters = [])
136
    {
137 1
        if (!empty($parameters)) {
138 1
            $this->parameters->addPost($parameters);
139 1
        }
140
141 1
        return $this->request($url);
142
    }
143
144
    /**
145
     * Подготавливает список заголовков HTTP
146
     *
147
     * @param mixed $modified Значение заголовка IF-MODIFIED-SINCE
148
     * @return array
149
     */
150 2
    protected function prepareHeaders($modified = null)
151
    {
152 2
        $headers = ['Content-Type: application/json'];
153
154 2
        if ($modified !== null) {
155 2
            if (is_int($modified)) {
156 1
                $headers[] = 'IF-MODIFIED-SINCE: ' . $modified;
157 1
            } else {
158 2
                $headers[] = 'IF-MODIFIED-SINCE: ' . (new DateTime($modified))->format(DateTime::RFC1123);
159
            }
160 1
        }
161
162 1
        return $headers;
163
    }
164
165
    /**
166
     * Подготавливает URL для HTTP запроса
167
     *
168
     * @param string $url Запрашиваемый URL
169
     * @return string
170
     */
171 2
    protected function prepareEndpoint($url)
172
    {
173 2
        if ($this->v1 === false) {
174 1
            $query = http_build_query(array_merge($this->parameters->getGet(), [
175 1
                'USER_LOGIN' => $this->parameters->getAuth('login'),
176 1
                'USER_HASH' => $this->parameters->getAuth('apikey'),
177 1
            ]), null, '&');
178 1
        } else {
179 1
            $query = http_build_query(array_merge($this->parameters->getGet(), [
180 1
                'login' => $this->parameters->getAuth('login'),
181 1
                'api_key' => $this->parameters->getAuth('apikey'),
182 1
            ]), null, '&');
183
        }
184
185 2
        return sprintf('https://%s%s?%s', $this->parameters->getAuth('domain'), $url, $query);
186
    }
187
188
    /**
189
     * Выполнить HTTP запрос и вернуть тело ответа
190
     *
191
     * @param string $url Запрашиваемый URL
192
     * @param null|string $modified Значение заголовка IF-MODIFIED-SINCE
193
     * @return mixed
194
     * @throws Exception
195
     * @throws NetworkException
196
     */
197
    protected function request($url, $modified = null)
198
    {
199
        $headers = $this->prepareHeaders($modified);
200
        $endpoint = $this->prepareEndpoint($url);
201
202
        $this->printDebug('url', $endpoint);
203
        $this->printDebug('headers', $headers);
204
205
        $ch = curl_init();
206
207
        curl_setopt($ch, CURLOPT_URL, $endpoint);
208
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
209
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
210
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
211
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
212
213
        if ($this->parameters->hasPost()) {
214
            $fields = json_encode([
215
                'request' => $this->parameters->getPost(),
216
            ]);
217
            curl_setopt($ch, CURLOPT_POST, true);
218
            curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
219
            $this->printDebug('post params', $fields);
220
        }
221
222
        $result = curl_exec($ch);
223
        $info = curl_getinfo($ch);
224
        $error = curl_error($ch);
225
        $errno = curl_errno($ch);
226
227
        curl_close($ch);
228
229
        $this->lastHttpCode = $info['http_code'];
230
        $this->lastHttpResponse = $result;
231
232
        $this->printDebug('curl_exec', $result);
233
        $this->printDebug('curl_getinfo', $info);
234
        $this->printDebug('curl_error', $error);
235
        $this->printDebug('curl_errno', $errno);
236
237
        if ($result === false && !empty($error)) {
238
            throw new NetworkException($error, $errno);
239
        }
240
241
        return $this->parseResponse($result, $info);
242
    }
243
244
    /**
245
     * Парсит HTTP ответ, проверяет на наличие ошибок и возвращает тело ответа
246
     *
247
     * @param string $response HTTP ответ
248
     * @param array $info Результат функции curl_getinfo
249
     * @return mixed
250
     * @throws Exception
251
     */
252 7
    protected function parseResponse($response, $info)
253
    {
254 7
        $result = json_decode($response, true);
255
256 7
        if (floor($info['http_code'] / 100) >= 3) {
257 5
            if (isset($result['response']['error_code']) && $result['response']['error_code'] > 0) {
258 1
                $code = $result['response']['error_code'];
259 5
            } elseif ($result !== null) {
260 2
                $code = 0;
261 2
            } else {
262 2
                $code = $info['http_code'];
263
            }
264 5
            if ($this->v1 === false && isset($result['response']['error'])) {
265 2
                throw new Exception($result['response']['error'], $code);
266 3
            } elseif (isset($result['response'])) {
267 1
                throw new Exception(json_encode($result['response']));
268
            } else {
269 2
                throw new Exception('Invalid response body.', $code);
270
            }
271 2
        } elseif (!isset($result['response'])) {
272 1
            return false;
273
        }
274
275 1
        return $result['response'];
276
    }
277
278
    /**
279
     * Вывода отладочной информации
280
     *
281
     * @param string $key Заголовок отладочной информации
282
     * @param mixed $value Значение отладочной информации
283
     * @param bool $return Возврат строки вместо вывода
284
     * @return mixed
285
     */
286 2
    protected function printDebug($key = '', $value = null, $return = false)
287
    {
288 2
        if ($this->debug !== true) {
289 1
            return false;
290
        }
291
292 1
        if (!is_string($value)) {
293 1
            $value = print_r($value, true);
294 1
        }
295
296 1
        $line = sprintf('[DEBUG] %s: %s', $key, $value);
297
298 1
        if ($return === false) {
299
            return print_r($line . PHP_EOL);
300
        }
301
302 1
        return $line;
303
    }
304
}
305