Passed
Push — master ( 0deb8f...f54565 )
by Goffy
04:13
created

Api::expandUriTemplate()   C

Complexity

Conditions 14
Paths 1

Size

Total Lines 93
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 58
nc 1
nop 3
dl 0
loc 93
rs 6.2666
c 0
b 0
f 0

How to fix   Long Method    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 XoopsModules\Wggithub\Github;
4
5
6
/**
7
 * Github API client library. Read readme.md in repository {@link http://github.com/milo/github-api}
8
 *
9
 * @see https://developer.github.com/v3/
10
 *
11
 * @author  Miloslav Hůla (https://github.com/milo)
12
 */
13
class Api extends Sanity
14
{
15
	/** @var string */
16
	private $url = 'https://api.github.com';
17
18
	/** @var string */
19
	private $defaultAccept = 'application/vnd.github.v3+json';
20
21
	/** @var array|NULL */
22
	private $defaultParameters = [];
23
24
	/** @var Http\IClient */
25
	private $client;
26
27
	/** @var OAuth\Token|NULL */
28
	private $token;
29
30
31
	public function __construct(Http\IClient $client = NULL)
32
	{
33
		$this->client = $client ?: Helpers::createDefaultClient();
34
	}
35
36
37
	/**
38
	 * @return self
39
	 */
40
	public function setToken(OAuth\Token $token = NULL)
41
	{
42
		$this->token = $token;
43
		return $this;
44
	}
45
46
47
	/**
48
	 * @return OAuth\Token|NULL
49
	 */
50
	public function getToken()
51
	{
52
		return $this->token;
53
	}
54
55
56
	/**
57
	 * @param  array
58
	 * @return self
59
	 */
60
	public function setDefaultParameters(array $defaults = NULL)
61
	{
62
		$this->defaultParameters = $defaults ?: [];
63
		return $this;
64
	}
65
66
67
	/**
68
	 * @return array
69
	 */
70
	public function getDefaultParameters()
71
	{
72
		return $this->defaultParameters;
73
	}
74
75
76
	/**
77
	 * @param  string
78
	 * @return self
79
	 */
80
	public function setDefaultAccept($accept)
81
	{
82
		$this->defaultAccept = $accept;
83
		return $this;
84
	}
85
86
87
	/**
88
	 * @return string
89
	 */
90
	public function getDefaultAccept()
91
	{
92
		return $this->defaultAccept;
93
	}
94
95
96
	/**
97
	 * @see createRequest()
98
	 * @see request()
99
	 *
100
	 * @param  string
101
	 * @return Http\Response
102
	 *
103
	 * @throws MissingParameterException
104
	 */
105
	public function delete($urlPath, array $parameters = [], array $headers = [])
106
	{
107
		return $this->request(
108
			$this->createRequest(Http\Request::DELETE, $urlPath, $parameters, $headers)
109
		);
110
	}
111
112
113
	/**
114
	 * @see createRequest()
115
	 * @see request()
116
	 *
117
	 * @param  string
118
	 * @return Http\Response
119
	 *
120
	 * @throws MissingParameterException
121
	 */
122
	public function get($urlPath, array $parameters = [], array $headers = [])
123
	{
124
		return $this->request(
125
			$this->createRequest(Http\Request::GET, $urlPath, $parameters, $headers)
126
		);
127
	}
128
129
130
	/**
131
	 * @see createRequest()
132
	 * @see request()
133
	 *
134
	 * @param  string
135
	 * @return Http\Response
136
	 *
137
	 * @throws MissingParameterException
138
	 */
139
	public function head($urlPath, array $parameters = [], array $headers = [])
140
	{
141
		return $this->request(
142
			$this->createRequest(Http\Request::HEAD, $urlPath, $parameters, $headers)
143
		);
144
	}
145
146
147
	/**
148
	 * @see createRequest()
149
	 * @see request()
150
	 *
151
	 * @param  string
152
	 * @param  mixed
153
	 * @return Http\Response
154
	 *
155
	 * @throws MissingParameterException
156
	 * @throws JsonException
157
	 */
158
	public function patch($urlPath, $content, array $parameters = [], array $headers = [])
159
	{
160
		return $this->request(
161
			$this->createRequest(Http\Request::PATCH, $urlPath, $parameters, $headers, $content)
162
		);
163
	}
164
165
166
	/**
167
	 * @see createRequest()
168
	 * @see request()
169
	 *
170
	 * @param  string
171
	 * @param  mixed
172
	 * @return Http\Response
173
	 *
174
	 * @throws MissingParameterException
175
	 * @throws JsonException
176
	 */
177
	public function post($urlPath, $content, array $parameters = [], array $headers = [])
178
	{
179
		return $this->request(
180
			$this->createRequest(Http\Request::POST, $urlPath, $parameters, $headers, $content)
181
		);
182
	}
183
184
185
	/**
186
	 * @see createRequest()
187
	 * @see request()
188
	 *
189
	 * @param  string
190
	 * @param  mixed
191
	 * @return Http\Response
192
	 *
193
	 * @throws MissingParameterException
194
	 * @throws JsonException
195
	 */
196
	public function put($urlPath, $content = NULL, array $parameters = [], array $headers = [])
197
	{
198
		return $this->request(
199
			$this->createRequest(Http\Request::PUT, $urlPath, $parameters, $headers, $content)
200
		);
201
	}
202
203
204
	/**
205
	 * @return Http\Response
206
	 *
207
	 * @throws Http\BadResponseException
208
	 */
209
	public function request(Http\Request $request)
210
	{
211
		$request = clone $request;
212
213
		$request->addHeader('Accept', $this->defaultAccept);
214
		$request->addHeader('Time-Zone', date_default_timezone_get());
215
		$request->addHeader('User-Agent', 'milo/github-api');
216
217
		if ($this->token) {
218
			/** @todo Distinguish token type? */
219
			$request->addHeader('Authorization', "token {$this->token->getValue()}");
220
		}
221
222
		return $this->client->request($request);
223
	}
224
225
226
	/**
227
	 * @param  string  Http\Request::GET|POST|...
228
	 * @param  string  path like '/users/:user/repos' where ':user' is substitution
229
	 * @param  array[name => value]  replaces substitutions in $urlPath, the rest is appended as query string to URL
0 ignored issues
show
Documentation Bug introduced by
The doc comment => at position 0 could not be parsed: Unknown type name '=' at position 0 in =>.
Loading history...
230
	 * @param  array[name => value]  name is case-insensitive
231
	 * @param  mixed|NULL  arrays and objects are encoded to JSON and Content-Type is set
232
	 * @return Http\Request
233
	 *
234
	 * @throws MissingParameterException  when substitution is used in URL but parameter is missing
235
	 * @throws JsonException  when encoding to JSON fails
236
	 */
237
	public function createRequest($method, $urlPath, array $parameters = [], array $headers = [], $content = NULL)
238
	{
239
		if (stripos($urlPath, $this->url) === 0) {  # Allows non-HTTPS URLs
240
			$baseUrl = $this->url;
241
			$urlPath = substr($urlPath, strlen($this->url));
242
243
		} elseif (preg_match('#^(https://[^/]+)(/.*)?$#', $urlPath, $m)) {
244
			$baseUrl = $m[1];
245
			$urlPath = isset($m[2]) ? $m[2] : '';
246
247
		} else {
248
			$baseUrl = $this->url;
249
		}
250
251
		if (strpos($urlPath, '{') === FALSE) {
252
			$urlPath = $this->expandColonParameters($urlPath, $parameters, $this->defaultParameters);
0 ignored issues
show
Bug introduced by
It seems like $this->defaultParameters can also be of type null; however, parameter $defaultParameters of XoopsModules\Wggithub\Gi...expandColonParameters() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

252
			$urlPath = $this->expandColonParameters($urlPath, $parameters, /** @scrutinizer ignore-type */ $this->defaultParameters);
Loading history...
253
		} else {
254
			$urlPath = $this->expandUriTemplate($urlPath, $parameters, $this->defaultParameters);
0 ignored issues
show
Bug introduced by
It seems like $this->defaultParameters can also be of type null; however, parameter $defaultParameters of XoopsModules\Wggithub\Gi...pi::expandUriTemplate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

254
			$urlPath = $this->expandUriTemplate($urlPath, $parameters, /** @scrutinizer ignore-type */ $this->defaultParameters);
Loading history...
255
		}
256
257
		$url = rtrim($baseUrl, '/') . '/' . ltrim($urlPath, '/');
258
259
		if ($content !== NULL && (is_array($content) || is_object($content))) {
260
			$headers['Content-Type'] = 'application/json; charset=utf-8';
261
			$content = Helpers::jsonEncode($content);
262
		}
263
264
		return new Http\Request($method, $url, $headers, $content);
265
	}
266
267
268
	/**
269
	 * @param  Http\Response
270
	 * @param  array|NULL  these codes are treated as success; code < 300 if NULL
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Github\these was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
271
	 * @return mixed
272
	 *
273
	 * @throws ApiException
274
	 */
275
	public function decode(Http\Response $response, array $okCodes = NULL)
276
	{
277
		$content = $response->getContent();
278
		if (preg_match('~application/json~i', $response->getHeader('Content-Type', ''))) {
279
			try {
280
				$content = Helpers::jsonDecode($response->getContent(), true);
281
			} catch (JsonException $e) {
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Github\JsonException was not found. Did you mean JsonException? If so, make sure to prefix the type with \.
Loading history...
282
				throw new InvalidResponseException('JSON decoding failed.', 0, $e, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Gi...nvalidResponseException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
283
			}
284
285
			if (!is_array($content) && !is_object($content)) {
0 ignored issues
show
introduced by
The condition is_object($content) is always false.
Loading history...
introduced by
The condition is_array($content) is always false.
Loading history...
286
				throw new InvalidResponseException('Decoded JSON is not an array or object.', 0, NULL, $response);
287
			}
288
		}
289
290
		$code = $response->getCode();
291
		if (($okCodes === NULL && $code >= 300) || (is_array($okCodes) && !in_array($code, $okCodes))) {
292
			/** @var $content \stdClass */
293
			switch ($code) {
294
				case Http\Response::S400_BAD_REQUEST:
295
					throw new BadRequestException(self::errorMessage($content), $code, NULL, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Github\BadRequestException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
296
297
				case Http\Response::S401_UNAUTHORIZED:
298
					throw new UnauthorizedException(self::errorMessage($content), $code, NULL, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Github\UnauthorizedException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
299
300
				case Http\Response::S403_FORBIDDEN:
301
					if ($response->getHeader('X-RateLimit-Remaining') === '0') {
302
						throw new RateLimitExceedException(self::errorMessage($content), $code, NULL, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Gi...ateLimitExceedException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
303
					}
304
					throw new ForbiddenException(self::errorMessage($content), $code, NULL, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Github\ForbiddenException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
305
306
				case Http\Response::S404_NOT_FOUND:
307
					throw new NotFoundException('Resource not found or not authorized to access.', $code, NULL, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Github\NotFoundException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
308
309
				case Http\Response::S422_UNPROCESSABLE_ENTITY:
310
					throw new UnprocessableEntityException(self::errorMessage($content), $code, NULL, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Gi...cessableEntityException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
311
			}
312
313
			$message = $okCodes === NULL ? '< 300' : implode(' or ', $okCodes);
314
			throw new UnexpectedResponseException("Expected response with code $message.", $code, NULL, $response);
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Gi...pectedResponseException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
315
		}
316
317
		return $content;
318
	}
319
320
321
	/**
322
	 * Creates paginator for HTTP GET requests.
323
	 *
324
	 * @see get()
325
	 *
326
	 * @param  string
327
	 * @return Paginator
328
	 *
329
	 * @throws MissingParameterException
330
	 */
331
	public function paginator($urlPath, array $parameters = [], array $headers = [])
332
	{
333
		return new Paginator(
334
			$this,
335
			$this->createRequest(Http\Request::GET, $urlPath, $parameters, $headers)
336
		);
337
	}
338
339
340
	/**
341
	 * @return Http\IClient
342
	 */
343
	public function getClient()
344
	{
345
		return $this->client;
346
	}
347
348
349
	/**
350
	 * @param  string
351
	 * @return Api
352
	 */
353
	public function withUrl($url)
354
	{
355
		$api = clone $this;
356
		$api->setUrl($url);
357
		return $api;
358
	}
359
360
361
	/**
362
	 * @param  string
363
	 * @return self
364
	 */
365
	public function setUrl($url)
366
	{
367
		$this->url = $url;
368
		return $this;
369
	}
370
371
372
	/**
373
	 * @return string
374
	 */
375
	public function getUrl()
376
	{
377
		return $this->url;
378
	}
379
380
381
	/**
382
	 * @param  string
383
	 * @return string
384
	 *
385
	 * @throws MissingParameterException
386
	 */
387
	protected function expandColonParameters($url, array $parameters, array $defaultParameters)
388
	{
389
		$parameters += $defaultParameters;
390
391
		$url = preg_replace_callback('#(^|/|\.):([^/.]+)#', function($m) use ($url, & $parameters) {
392
			if (!isset($parameters[$m[2]])) {
393
				throw new MissingParameterException("Missing parameter '$m[2]' for URL path '$url'.");
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Gi...ssingParameterException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
394
			}
395
			$parameter = $parameters[$m[2]];
396
			unset($parameters[$m[2]]);
397
			return $m[1] . rawurlencode($parameter);
398
		}, $url);
399
400
		$url = rtrim($url, '/');
401
402
		if (count($parameters)) {
403
			$url .= '?' . http_build_query($parameters);
404
		}
405
406
		return $url;
407
	}
408
409
410
	/**
411
	 * Expands URI template (RFC 6570).
412
	 *
413
	 * @see http://tools.ietf.org/html/rfc6570
414
	 * @todo Inject remaining default parameters into query string?
415
	 *
416
	 * @param  string
417
	 * @return string
418
	 */
419
	protected function expandUriTemplate($url, array $parameters, array $defaultParameters)
420
	{
421
		$parameters += $defaultParameters;
422
423
		static $operatorFlags = [
424
			''  => ['prefix' => '',  'separator' => ',', 'named' => FALSE, 'ifEmpty' => '',  'reserved' => FALSE],
425
			'+' => ['prefix' => '',  'separator' => ',', 'named' => FALSE, 'ifEmpty' => '',  'reserved' => TRUE],
426
			'#' => ['prefix' => '#', 'separator' => ',', 'named' => FALSE, 'ifEmpty' => '',  'reserved' => TRUE],
427
			'.' => ['prefix' => '.', 'separator' => '.', 'named' => FALSE, 'ifEmpty' => '',  'reserved' => FALSE],
428
			'/' => ['prefix' => '/', 'separator' => '/', 'named' => FALSE, 'ifEmpty' => '',  'reserved' => FALSE],
429
			';' => ['prefix' => ';', 'separator' => ';', 'named' => TRUE,  'ifEmpty' => '',  'reserved' => FALSE],
430
			'?' => ['prefix' => '?', 'separator' => '&', 'named' => TRUE,  'ifEmpty' => '=', 'reserved' => FALSE],
431
			'&' => ['prefix' => '&', 'separator' => '&', 'named' => TRUE,  'ifEmpty' => '=', 'reserved' => FALSE],
432
		];
433
434
		return preg_replace_callback('~{([+#./;?&])?([^}]+?)}~', function($m) use ($url, & $parameters, $operatorFlags) {
0 ignored issues
show
Unused Code introduced by
The import $url is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
435
			$flags = $operatorFlags[$m[1]];
436
437
			$translated = [];
438
			foreach (explode(',', $m[2]) as $name) {
439
				$explode = FALSE;
440
				$maxLength = NULL;
441
				if (preg_match('~^(.+)(?:(\*)|:(\d+))$~', $name, $tmp)) { // TODO: Speed up?
442
					$name = $tmp[1];
443
					if (isset($tmp[3])) {
444
						$maxLength = (int) $tmp[3];
445
					} else {
446
						$explode = TRUE;
447
					}
448
				}
449
450
				if (!isset($parameters[$name])) {  // TODO: Throw exception?
451
					continue;
452
				}
453
454
				$value = $parameters[$name];
455
				if (is_scalar($value)) {
456
					$translated[] = $this->prefix($flags, $name, $this->escape($flags, $value, $maxLength));
457
458
				} else {
459
					$value = (array) $value;
460
					$isAssoc = key($value) !== 0;
461
462
					// The '*' (explode) modifier
463
					if ($explode) {
464
						$parts = [];
465
						if ($isAssoc) {
466
							$this->walk($value, function ($v, $k) use (& $parts, $flags, $maxLength) {
467
								$parts[] = $this->prefix(['named' => TRUE] + $flags, $k, $this->escape($flags, $v, $maxLength));
468
							});
469
470
						} elseif ($flags['named']) {
471
							$this->walk($value, function ($v) use (& $parts, $flags, $name, $maxLength) {
472
								$parts[] = $this->prefix($flags, $name, $this->escape($flags, $v, $maxLength));
473
							});
474
475
						} else {
476
							$this->walk($value, function ($v) use (& $parts, $flags, $maxLength) {
477
								$parts[] = $this->escape($flags, $v, $maxLength);
478
							});
479
						}
480
481
						if (isset($parts[0])) {
482
							if ($flags['named']) {
483
								$translated[] = implode($flags['separator'], $parts);
484
							} else {
485
								$translated[] = $this->prefix($flags, $name, implode($flags['separator'], $parts));
486
							}
487
						}
488
489
					} else {
490
						$parts = [];
491
						$this->walk($value, function($v, $k) use (& $parts, $isAssoc, $flags, $maxLength) {
492
							if ($isAssoc) {
493
								$parts[] = $this->escape($flags, $k);
494
							}
495
496
							$parts[] = $this->escape($flags, $v, $maxLength);
497
						});
498
499
						if (isset($parts[0])) {
500
							$translated[] = $this->prefix($flags, $name, implode(',', $parts));
501
						}
502
					}
503
				}
504
			}
505
506
			if (isset($translated[0])) {
507
				return $flags['prefix'] . implode($flags['separator'], $translated);
508
			}
509
510
			return '';
511
		}, $url);
512
	}
513
514
515
	/**
516
	 * @param  array
517
	 * @param  string
518
	 * @param  string  already escaped
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wggithub\Github\already was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
519
	 * @return string
520
	 */
521
	private function prefix(array $flags, $name, $value)
522
	{
523
		$prefix = '';
524
		if ($flags['named']) {
525
			$prefix .= $this->escape($flags, $name);
526
			if (isset($value[0])) {
527
				$prefix .= '=';
528
			} else {
529
				$prefix .= $flags['ifEmpty'];
530
			}
531
		}
532
533
		return $prefix . $value;
534
	}
535
536
537
	/**
538
	 * @param  array
539
	 * @param  mixed
540
	 * @param  int|NULL
541
	 * @return string
542
	 */
543
	private function escape(array $flags, $value, $maxLength = NULL)
544
	{
545
		$value = (string) $value;
546
547
		if ($maxLength !== NULL) {
548
			if (preg_match('~^(.{' . $maxLength . '}).~u', $value, $m)) {
549
				$value = $m[1];
550
			} elseif (strlen($value) > $maxLength) {  # when malformed UTF-8
551
				$value = substr($value, 0, $maxLength);
552
			}
553
		}
554
555
		if ($flags['reserved']) {
556
			$parts = preg_split('~(%[0-9a-fA-F]{2}|[:/?#[\]@!$&\'()*+,;=])~', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
557
			$parts[] = '';
558
559
			$escaped = '';
560
			for ($i = 0, $count = count($parts); $i < $count; $i += 2) {
0 ignored issues
show
Bug introduced by
It seems like $parts can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

560
			for ($i = 0, $count = count(/** @scrutinizer ignore-type */ $parts); $i < $count; $i += 2) {
Loading history...
561
				$escaped .= rawurlencode($parts[$i]) . $parts[$i + 1];
562
			}
563
564
			return $escaped;
565
		}
566
567
		return rawurlencode($value);
568
	}
569
570
571
	/**
572
	 * @param  array
573
	 * @param  callable
574
	 */
575
	private function walk(array $array, $cb)
576
	{
577
		foreach ($array as $k => $v) {
578
			if ($v === NULL) {
579
				continue;
580
			}
581
582
			$cb($v, $k);
583
		}
584
	}
585
586
587
	/**
588
	 * @param  \stdClass
589
	 * @return string
590
	 */
591
	private static function errorMessage($content)
592
	{
593
		$message = isset($content->message)
594
			? $content->message
595
			: 'Unknown error';
596
597
		if (isset($content->errors)) {
598
			$message .= implode(', ', array_map(function($error) {
599
				return '[' . implode(':', (array) $error) . ']';
600
			}, $content->errors));
601
		}
602
603
		return $message;
604
	}
605
606
}
607