Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
apiclient.php
1<?php
2
4
6use Bitrix\Calendar\Sync\Office365\Util\ObjectStatusTrait;
11use Bitrix\Calendar\Sync\Internals\HasContextTrait;
18use Exception;
19
24{
25 use ObjectStatusTrait, HasContextTrait;
26
31
38 {
39 $this->httpClient = $httpClient;
40 $this->context = $context;
41 }
42
57 public function request(string $method, string $uri, array $params): array
58 {
59 $getLogContext = static function (int $statusCode, $response, string $error = '' )
60 use ($method, $uri, $params): array
61 {
62 return [
63 'serviceName' => Helper::ACCOUNT_TYPE,
64 'host' => Helper::SERVER_PATH,
65 'method' => $method,
66 'url' => $uri,
67 'requestParams' => $params,
68 'statusCode' => $statusCode,
69 'error' => $error,
70 'response' => $response,
71 ];
72 };
73 $this->getStatus()->resetErrors();
74 try
75 {
76 $response = [];
77 $paramString = $this->prepareParams($params);
78
79 $uri = Helper::SERVER_PATH . $uri;
80
81 $this->httpClient->waitResponse(true);
82 $this->httpClient->query($method, $uri, $paramString);
83
84 if ($this->httpClient->getStatus() < 300)
85 {
86 $response = $this->prepareResponse();
87 $this->context->getLogger()
88 ->debug("API office365 success" . $this->httpClient->getStatus()
89 , $getLogContext(
90 $this->httpClient->getStatus(),
91 $this->httpClient->getResult(),
92 )
93 );
94 }
95 else
96 {
97 try
98 {
99 $error = Json::decode($this->httpClient->getResult());
100 $this->getStatus()->addError(
101 "CONNECTION",
102 "[" . $error['error']['code'] . "] " . $error['error']['message'],
103 );
104 $this->context->getLogger()
105 ->warning("API office365 returned error code "
106 . $this->httpClient->getStatus()
107 . ": " . $error['error']['message'],
108 $getLogContext(
109 $this->httpClient->getStatus(),
110 $this->httpClient->getResult(),
111 $error['error']['message']
112 )
113 );
114 switch ($this->httpClient->getStatus())
115 {
116 case 401:
117 throw new AuthException(
118 $error['error']['code'],
119 401,
120 __FILE__,
121 __LINE__
122 );
123 case 404:
124 throw new NotFoundException(
125 $error['error']['code'],
126 404,
127 __FILE__,
128 __LINE__
129 );
130 case 409:
131 throw new ConflictException(
132 $error['error']['code'],
133 409,
134 __FILE__,
135 __LINE__
136 );
137 case 410:
138 throw new GoneException(
139 $error['error']['code'],
140 410,
141 __FILE__,
142 __LINE__
143 );
144 case 412:
146 $error['error']['code'],
147 412,
148 __FILE__,
149 __LINE__
150 );
151 default:
152 throw new ApiException(
153 $error['error']['code'],
154 $this->httpClient->getStatus(),
155 __FILE__,
156 __LINE__
157 );
158 }
159 }
160 catch (ArgumentException $exception)
161 {
162 $this->context->getLogger()
163 ->error("ArgumentException from office365", $getLogContext(
164 $this->httpClient->getStatus(),
165 $this->httpClient->getResult(),
166 $exception->getMessage()
167 ));
168 foreach($this->httpClient->getError() as $code => $error)
169 {
170 $this->getStatus()->addError($code, $error);
171 }
172 }
173 }
174 }
175 catch (ApiException $e)
176 {
177 $this->context->getLogger()
178 ->error("ApiException from office365", $getLogContext(
179 $e->getCode(),
180 '',
181 $e->getMessage()
182 )
183 );
184 throw $e;
185 }
186 catch (Exception $e)
187 {
188 $this->context->getLogger()
189 ->error("Exception from office365: " . $e->getMessage(), $getLogContext(
190 $e->getCode(),
191 '',
192 $e->getMessage()
193 )
194 );
195 throw $e;
196 }
197
198 return $response;
199 }
200
207 protected function multipartDecode(string $response, string $boundary): array
208 {
209 $events = [];
210
211 $response = str_replace("--$boundary--", "--$boundary", $response);
212 $parts = explode("--$boundary\r\n", $response);
213
214 foreach ($parts as $part)
215 {
216 $part = trim($part);
217 if (!empty($part))
218 {
219 $partEvent = explode("\r\n\r\n", $part);
220 $data = $this->getMetaInfo($partEvent[1]);
221 $id = $this->getId($partEvent[0]);
222
223 if ($data['status'] === 200)
224 {
225 if ($id === null)
226 {
227 continue;
228 }
229
230 try
231 {
232 $event = Json::decode($partEvent[2]);
233 }
234 catch(Exception $exception)
235 {
236 continue;
237 }
238
239 $event['etag'] = $data['etag'];
240 $events[$id] = $event;
241 }
242 else
243 {
244 AddMessage2Log('Event sync error. ID: ' . ($id ?? 'unknown'));
245 }
246 }
247 }
248
249 return $events;
250 }
251
252 private function getMetaInfo($headers): array
253 {
254 $data = [];
255 foreach (explode("\n", $headers) as $k => $header)
256 {
257 if($k === 0)
258 {
259 if(preg_match('#HTTP\S+ (\d+)#', $header, $find))
260 {
261 $data['status'] = (int)$find[1];
262 }
263 }
264 elseif(mb_strpos($header, ':') !== false)
265 {
266 [$headerName, $headerValue] = explode(':', $header, 2);
267 if(mb_strtolower($headerName) === 'etag')
268 {
269 $data['etag'] = trim($headerValue);
270 }
271 }
272 }
273
274 return $data;
275 }
276
282 private function getId(string $headers): ?int
283 {
284 $id = null;
285 foreach (explode("\n", $headers) as $header)
286 {
287 if(mb_strpos($header, ':') !== false)
288 {
289 [$headerName, $headerValue] = explode(':', $header, 2);
290 if(mb_strtolower($headerName) === 'content-id')
291 {
292 $part = explode(':', $headerValue);
293 $id = (int) rtrim($part[1], ">");
294 break;
295 }
296 }
297 }
298
299 return $id;
300 }
301
302
310 protected function prepareParams(array $params): ?string
311 {
312 return $this->formatParams($params);
313 }
314
322 protected function formatParams(array $params): ?string
323 {
324 return $params ? Json::encode($params, JSON_UNESCAPED_SLASHES) : null;
325 }
326
340 public function get(string $uri, array $params = []): array
341 {
342 if ($params)
343 {
344 $uri .= (strpos($uri, '?') ? '&' : '?')
345 . http_build_query($params)
346 ;
347 }
348 return $this->request(HttpClient::HTTP_GET, $uri, $params);
349 }
350
364 public function post(string $uri, array $params = []): array
365 {
366 return $this->request(HttpClient::HTTP_POST, $uri, $params);
367 }
368
382 public function delete(string $uri, array $params = []): array
383 {
384 return $this->request(HttpClient::HTTP_DELETE, $uri, $params);
385 }
386
400 public function put(string $uri, array $params = []): array
401 {
402 return $this->request(HttpClient::HTTP_PUT, $uri, $params);
403 }
404
418 public function patch(string $uri, array $params = []): array
419 {
420 return $this->request(HttpClient::HTTP_PATCH, $uri, $params);
421 }
422
428 protected function prepareResponse()
429 {
430 $contentType = $this->httpClient->getHeaders()->getContentType();
431
432 if ($contentType === 'multipart/mixed')
433 {
434 $response = $this->multipartDecode(
435 $this->httpClient->getResult(),
436 $this->httpClient->getHeaders()->getBoundary()
437 );
438 }
439 else
440 {
441 $response = $this->httpClient->getResult()
442 ? Json::decode($this->httpClient->getResult())
443 : [];
444 }
445
446 return $response;
447 }
448}
multipartDecode(string $response, string $boundary)
post(string $uri, array $params=[])
put(string $uri, array $params=[])
request(string $method, string $uri, array $params)
Definition apiclient.php:57
__construct(HttpClient $httpClient, ContextInterface $context)
Definition apiclient.php:37
patch(string $uri, array $params=[])