Passed
Push — master ( 9b803d...bcd4f7 )
by Goffy
03:19
created
class/Github/Sanity.php 2 patches
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -8,21 +8,21 @@
 block discarded – undo
8 8
  */
9 9
 abstract class Sanity
10 10
 {
11
-	/**
12
-	 * @throws LogicException
13
-	 */
14
-	public function & __get($name)
15
-	{
16
-		throw new LogicException('Cannot read an undeclared property ' . get_class($this) . "::\$$name.");
17
-	}
11
+    /**
12
+     * @throws LogicException
13
+     */
14
+    public function & __get($name)
15
+    {
16
+        throw new LogicException('Cannot read an undeclared property ' . get_class($this) . "::\$$name.");
17
+    }
18 18
 
19 19
 
20
-	/**
21
-	 * @throws LogicException
22
-	 */
23
-	public function __set($name, $value)
24
-	{
25
-		throw new LogicException('Cannot write to an undeclared property ' . get_class($this) . "::\$$name.");
26
-	}
20
+    /**
21
+     * @throws LogicException
22
+     */
23
+    public function __set($name, $value)
24
+    {
25
+        throw new LogicException('Cannot write to an undeclared property ' . get_class($this) . "::\$$name.");
26
+    }
27 27
 
28 28
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -13,7 +13,7 @@  discard block
 block discarded – undo
13 13
 	 */
14 14
 	public function & __get($name)
15 15
 	{
16
-		throw new LogicException('Cannot read an undeclared property ' . get_class($this) . "::\$$name.");
16
+		throw new LogicException('Cannot read an undeclared property '.get_class($this)."::\$$name.");
17 17
 	}
18 18
 
19 19
 
@@ -22,7 +22,7 @@  discard block
 block discarded – undo
22 22
 	 */
23 23
 	public function __set($name, $value)
24 24
 	{
25
-		throw new LogicException('Cannot write to an undeclared property ' . get_class($this) . "::\$$name.");
25
+		throw new LogicException('Cannot write to an undeclared property '.get_class($this)."::\$$name.");
26 26
 	}
27 27
 
28 28
 }
Please login to merge, or discard this patch.
class/Github/Api.php 2 patches
Indentation   +590 added lines, -590 removed lines patch added patch discarded remove patch
@@ -12,595 +12,595 @@
 block discarded – undo
12 12
  */
13 13
 class Api extends Sanity
14 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
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);
253
-		} else {
254
-			$urlPath = $this->expandUriTemplate($urlPath, $parameters, $this->defaultParameters);
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
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) {
282
-				throw new InvalidResponseException('JSON decoding failed.', 0, $e, $response);
283
-			}
284
-
285
-			if (!is_array($content) && !is_object($content)) {
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);
296
-
297
-				case Http\Response::S401_UNAUTHORIZED:
298
-					throw new UnauthorizedException(self::errorMessage($content), $code, NULL, $response);
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);
303
-					}
304
-					throw new ForbiddenException(self::errorMessage($content), $code, NULL, $response);
305
-
306
-				case Http\Response::S404_NOT_FOUND:
307
-					throw new NotFoundException('Resource not found or not authorized to access.', $code, NULL, $response);
308
-
309
-				case Http\Response::S422_UNPROCESSABLE_ENTITY:
310
-					throw new UnprocessableEntityException(self::errorMessage($content), $code, NULL, $response);
311
-			}
312
-
313
-			$message = $okCodes === NULL ? '< 300' : implode(' or ', $okCodes);
314
-			throw new UnexpectedResponseException("Expected response with code $message.", $code, NULL, $response);
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'.");
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) {
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
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) {
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
-	}
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
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);
253
+        } else {
254
+            $urlPath = $this->expandUriTemplate($urlPath, $parameters, $this->defaultParameters);
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
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) {
282
+                throw new InvalidResponseException('JSON decoding failed.', 0, $e, $response);
283
+            }
284
+
285
+            if (!is_array($content) && !is_object($content)) {
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);
296
+
297
+                case Http\Response::S401_UNAUTHORIZED:
298
+                    throw new UnauthorizedException(self::errorMessage($content), $code, NULL, $response);
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);
303
+                    }
304
+                    throw new ForbiddenException(self::errorMessage($content), $code, NULL, $response);
305
+
306
+                case Http\Response::S404_NOT_FOUND:
307
+                    throw new NotFoundException('Resource not found or not authorized to access.', $code, NULL, $response);
308
+
309
+                case Http\Response::S422_UNPROCESSABLE_ENTITY:
310
+                    throw new UnprocessableEntityException(self::errorMessage($content), $code, NULL, $response);
311
+            }
312
+
313
+            $message = $okCodes === NULL ? '< 300' : implode(' or ', $okCodes);
314
+            throw new UnexpectedResponseException("Expected response with code $message.", $code, NULL, $response);
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'.");
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) {
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
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) {
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 605
 
606 606
 }
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
 			$urlPath = $this->expandUriTemplate($urlPath, $parameters, $this->defaultParameters);
255 255
 		}
256 256
 
257
-		$url = rtrim($baseUrl, '/') . '/' . ltrim($urlPath, '/');
257
+		$url = rtrim($baseUrl, '/').'/'.ltrim($urlPath, '/');
258 258
 
259 259
 		if ($content !== NULL && (is_array($content) || is_object($content))) {
260 260
 			$headers['Content-Type'] = 'application/json; charset=utf-8';
@@ -394,13 +394,13 @@  discard block
 block discarded – undo
394 394
 			}
395 395
 			$parameter = $parameters[$m[2]];
396 396
 			unset($parameters[$m[2]]);
397
-			return $m[1] . rawurlencode($parameter);
397
+			return $m[1].rawurlencode($parameter);
398 398
 		}, $url);
399 399
 
400 400
 		$url = rtrim($url, '/');
401 401
 
402 402
 		if (count($parameters)) {
403
-			$url .= '?' . http_build_query($parameters);
403
+			$url .= '?'.http_build_query($parameters);
404 404
 		}
405 405
 
406 406
 		return $url;
@@ -421,14 +421,14 @@  discard block
 block discarded – undo
421 421
 		$parameters += $defaultParameters;
422 422
 
423 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],
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 432
 		];
433 433
 
434 434
 		return preg_replace_callback('~{([+#./;?&])?([^}]+?)}~', function($m) use ($url, & $parameters, $operatorFlags) {
@@ -463,17 +463,17 @@  discard block
 block discarded – undo
463 463
 					if ($explode) {
464 464
 						$parts = [];
465 465
 						if ($isAssoc) {
466
-							$this->walk($value, function ($v, $k) use (& $parts, $flags, $maxLength) {
466
+							$this->walk($value, function($v, $k) use (& $parts, $flags, $maxLength) {
467 467
 								$parts[] = $this->prefix(['named' => TRUE] + $flags, $k, $this->escape($flags, $v, $maxLength));
468 468
 							});
469 469
 
470 470
 						} elseif ($flags['named']) {
471
-							$this->walk($value, function ($v) use (& $parts, $flags, $name, $maxLength) {
471
+							$this->walk($value, function($v) use (& $parts, $flags, $name, $maxLength) {
472 472
 								$parts[] = $this->prefix($flags, $name, $this->escape($flags, $v, $maxLength));
473 473
 							});
474 474
 
475 475
 						} else {
476
-							$this->walk($value, function ($v) use (& $parts, $flags, $maxLength) {
476
+							$this->walk($value, function($v) use (& $parts, $flags, $maxLength) {
477 477
 								$parts[] = $this->escape($flags, $v, $maxLength);
478 478
 							});
479 479
 						}
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
 			}
505 505
 
506 506
 			if (isset($translated[0])) {
507
-				return $flags['prefix'] . implode($flags['separator'], $translated);
507
+				return $flags['prefix'].implode($flags['separator'], $translated);
508 508
 			}
509 509
 
510 510
 			return '';
@@ -530,7 +530,7 @@  discard block
 block discarded – undo
530 530
 			}
531 531
 		}
532 532
 
533
-		return $prefix . $value;
533
+		return $prefix.$value;
534 534
 	}
535 535
 
536 536
 
@@ -545,7 +545,7 @@  discard block
 block discarded – undo
545 545
 		$value = (string) $value;
546 546
 
547 547
 		if ($maxLength !== NULL) {
548
-			if (preg_match('~^(.{' . $maxLength . '}).~u', $value, $m)) {
548
+			if (preg_match('~^(.{'.$maxLength.'}).~u', $value, $m)) {
549 549
 				$value = $m[1];
550 550
 			} elseif (strlen($value) > $maxLength) {  # when malformed UTF-8
551 551
 				$value = substr($value, 0, $maxLength);
@@ -558,7 +558,7 @@  discard block
 block discarded – undo
558 558
 
559 559
 			$escaped = '';
560 560
 			for ($i = 0, $count = count($parts); $i < $count; $i += 2) {
561
-				$escaped .= rawurlencode($parts[$i]) . $parts[$i + 1];
561
+				$escaped .= rawurlencode($parts[$i]).$parts[$i + 1];
562 562
 			}
563 563
 
564 564
 			return $escaped;
@@ -596,7 +596,7 @@  discard block
 block discarded – undo
596 596
 
597 597
 		if (isset($content->errors)) {
598 598
 			$message .= implode(', ', array_map(function($error) {
599
-				return '[' . implode(':', (array) $error) . ']';
599
+				return '['.implode(':', (array) $error).']';
600 600
 			}, $content->errors));
601 601
 		}
602 602
 
Please login to merge, or discard this patch.
class/Github/Http/CachedClient.php 1 patch
Indentation   +126 added lines, -126 removed lines patch added patch discarded remove patch
@@ -13,131 +13,131 @@
 block discarded – undo
13 13
  */
14 14
 class CachedClient extends Github\Sanity implements IClient
15 15
 {
16
-	/** @var Storages\ICache|NULL */
17
-	private $cache;
18
-
19
-	/** @var IClient */
20
-	private $client;
21
-
22
-	/** @var bool */
23
-	private $forbidRecheck;
24
-
25
-	/** @var callable|NULL */
26
-	private $onResponse;
27
-
28
-
29
-	/**
30
-	 * @param Storages\ICache
31
-	 * @param IClient
32
-	 * @param bool  forbid checking Github for new data; more or less development purpose only
33
-	 */
34
-	public function __construct(Storages\ICache $cache, IClient $client = NULL, $forbidRecheck = FALSE)
35
-	{
36
-		$this->cache = $cache;
37
-		$this->client = $client ?: Github\Helpers::createDefaultClient();
38
-		$this->forbidRecheck = (bool) $forbidRecheck;
39
-	}
40
-
41
-
42
-	/**
43
-	 * @return IClient
44
-	 */
45
-	public function getInnerClient()
46
-	{
47
-		return $this->client;
48
-	}
49
-
50
-
51
-	/**
52
-	 * @return Response
53
-	 *
54
-	 * @throws BadResponseException
55
-	 */
56
-	public function request(Request $request)
57
-	{
58
-		$request = clone $request;
59
-
60
-		$cacheKey = implode('.', [
61
-			$request->getMethod(),
62
-			$request->getUrl(),
63
-
64
-			/** @todo This should depend on Vary: header */
65
-			$request->getHeader('Accept'),
66
-			$request->getHeader('Accept-Encoding'),
67
-			$request->getHeader('Authorization')
68
-		]);
69
-
70
-		if ($cached = $this->cache->load($cacheKey)) {
71
-			if ($this->forbidRecheck) {
72
-				$cached = clone $cached;
73
-				$this->onResponse && call_user_func($this->onResponse, $cached);
74
-				return $cached;
75
-			}
76
-
77
-			/** @var $cached Response */
78
-			if ($cached->hasHeader('Last-Modified')) {
79
-				$request->addHeader('If-Modified-Since', $cached->getHeader('Last-Modified'));
80
-			}
81
-			if ($cached->hasHeader('ETag')) {
82
-				$request->addHeader('If-None-Match', $cached->getHeader('ETag'));
83
-			}
84
-		}
85
-
86
-		$response = $this->client->request($request);
87
-
88
-		if ($this->isCacheable($response)) {
89
-			$this->cache->save($cacheKey, clone $response);
90
-		}
91
-
92
-		if (isset($cached) && $response->getCode() === Response::S304_NOT_MODIFIED) {
93
-			$cached = clone $cached;
94
-
95
-			/** @todo Should be responses somehow combined into one? */
96
-			$response = $cached->setPrevious($response);
97
-		}
98
-
99
-		$this->onResponse && call_user_func($this->onResponse, $response);
100
-
101
-		return $response;
102
-	}
103
-
104
-
105
-	/**
106
-	 * @param  callable|NULL function(Request $request)
107
-	 * @return self
108
-	 */
109
-	public function onRequest($callback)
110
-	{
111
-		$this->client->onRequest($callback);
112
-		return $this;
113
-	}
114
-
115
-
116
-	/**
117
-	 * @param  callable|NULL function(Response $response)
118
-	 * @return self
119
-	 */
120
-	public function onResponse($callback)
121
-	{
122
-		$this->client->onResponse(NULL);
123
-		$this->onResponse = $callback;
124
-		return $this;
125
-	}
126
-
127
-
128
-	/**
129
-	 * @return bool
130
-	 */
131
-	protected function isCacheable(Response $response)
132
-	{
133
-		/** @todo Do it properly. Vary:, Pragma:, TTL...  */
134
-		if (!$response->isCode(200)) {
135
-			return FALSE;
136
-		} elseif (preg_match('#max-age=0|must-revalidate#i', $response->getHeader('Cache-Control', ''))) {
137
-			return FALSE;
138
-		}
139
-
140
-		return $response->hasHeader('ETag') || $response->hasHeader('Last-Modified');
141
-	}
16
+    /** @var Storages\ICache|NULL */
17
+    private $cache;
18
+
19
+    /** @var IClient */
20
+    private $client;
21
+
22
+    /** @var bool */
23
+    private $forbidRecheck;
24
+
25
+    /** @var callable|NULL */
26
+    private $onResponse;
27
+
28
+
29
+    /**
30
+     * @param Storages\ICache
31
+     * @param IClient
32
+     * @param bool  forbid checking Github for new data; more or less development purpose only
33
+     */
34
+    public function __construct(Storages\ICache $cache, IClient $client = NULL, $forbidRecheck = FALSE)
35
+    {
36
+        $this->cache = $cache;
37
+        $this->client = $client ?: Github\Helpers::createDefaultClient();
38
+        $this->forbidRecheck = (bool) $forbidRecheck;
39
+    }
40
+
41
+
42
+    /**
43
+     * @return IClient
44
+     */
45
+    public function getInnerClient()
46
+    {
47
+        return $this->client;
48
+    }
49
+
50
+
51
+    /**
52
+     * @return Response
53
+     *
54
+     * @throws BadResponseException
55
+     */
56
+    public function request(Request $request)
57
+    {
58
+        $request = clone $request;
59
+
60
+        $cacheKey = implode('.', [
61
+            $request->getMethod(),
62
+            $request->getUrl(),
63
+
64
+            /** @todo This should depend on Vary: header */
65
+            $request->getHeader('Accept'),
66
+            $request->getHeader('Accept-Encoding'),
67
+            $request->getHeader('Authorization')
68
+        ]);
69
+
70
+        if ($cached = $this->cache->load($cacheKey)) {
71
+            if ($this->forbidRecheck) {
72
+                $cached = clone $cached;
73
+                $this->onResponse && call_user_func($this->onResponse, $cached);
74
+                return $cached;
75
+            }
76
+
77
+            /** @var $cached Response */
78
+            if ($cached->hasHeader('Last-Modified')) {
79
+                $request->addHeader('If-Modified-Since', $cached->getHeader('Last-Modified'));
80
+            }
81
+            if ($cached->hasHeader('ETag')) {
82
+                $request->addHeader('If-None-Match', $cached->getHeader('ETag'));
83
+            }
84
+        }
85
+
86
+        $response = $this->client->request($request);
87
+
88
+        if ($this->isCacheable($response)) {
89
+            $this->cache->save($cacheKey, clone $response);
90
+        }
91
+
92
+        if (isset($cached) && $response->getCode() === Response::S304_NOT_MODIFIED) {
93
+            $cached = clone $cached;
94
+
95
+            /** @todo Should be responses somehow combined into one? */
96
+            $response = $cached->setPrevious($response);
97
+        }
98
+
99
+        $this->onResponse && call_user_func($this->onResponse, $response);
100
+
101
+        return $response;
102
+    }
103
+
104
+
105
+    /**
106
+     * @param  callable|NULL function(Request $request)
107
+     * @return self
108
+     */
109
+    public function onRequest($callback)
110
+    {
111
+        $this->client->onRequest($callback);
112
+        return $this;
113
+    }
114
+
115
+
116
+    /**
117
+     * @param  callable|NULL function(Response $response)
118
+     * @return self
119
+     */
120
+    public function onResponse($callback)
121
+    {
122
+        $this->client->onResponse(NULL);
123
+        $this->onResponse = $callback;
124
+        return $this;
125
+    }
126
+
127
+
128
+    /**
129
+     * @return bool
130
+     */
131
+    protected function isCacheable(Response $response)
132
+    {
133
+        /** @todo Do it properly. Vary:, Pragma:, TTL...  */
134
+        if (!$response->isCode(200)) {
135
+            return FALSE;
136
+        } elseif (preg_match('#max-age=0|must-revalidate#i', $response->getHeader('Cache-Control', ''))) {
137
+            return FALSE;
138
+        }
139
+
140
+        return $response->hasHeader('ETag') || $response->hasHeader('Last-Modified');
141
+    }
142 142
 
143 143
 }
Please login to merge, or discard this patch.
class/Github/Http/StreamClient.php 2 patches
Indentation   +103 added lines, -103 removed lines patch added patch discarded remove patch
@@ -10,108 +10,108 @@
 block discarded – undo
10 10
  */
11 11
 class StreamClient extends AbstractClient
12 12
 {
13
-	/** @var array|NULL */
14
-	private $sslOptions;
15
-
16
-
17
-	/**
18
-	 * @param  array  SSL context options {@link http://php.net/manual/en/context.ssl.php}
19
-	 */
20
-	public function __construct(array $sslOptions = NULL)
21
-	{
22
-		$this->sslOptions = $sslOptions;
23
-	}
24
-
25
-
26
-	protected function setupRequest(Request $request)
27
-	{
28
-		parent::setupRequest($request);
29
-		$request->setHeader('Connection', 'close');
30
-	}
31
-
32
-
33
-	/**
34
-	 * @return Response
35
-	 *
36
-	 * @throws BadResponseException
37
-	 */
38
-	protected function process(Request $request)
39
-	{
40
-		$headerStr = [];
41
-		foreach ($request->getHeaders() as $name => $value) {
42
-			foreach ((array) $value as $v) {
43
-				$headerStr[] = "$name: $v";
44
-			}
45
-		}
46
-
47
-		$options = [
48
-			'http' => [
49
-				'method' => $request->getMethod(),
50
-				'header' => implode("\r\n", $headerStr) . "\r\n",
51
-				'follow_location' => 0,  # Github sets the Location header for 201 code too and redirection is not required for us
52
-				'protocol_version' => 1.1,
53
-				'ignore_errors' => TRUE,
54
-			],
55
-			'ssl' => [
56
-				'verify_peer' => TRUE,
57
-				'cafile' => realpath(__DIR__ . '/../../ca-chain.crt'),
58
-				'disable_compression' => TRUE,  # Effective since PHP 5.4.13
59
-			],
60
-		];
61
-
62
-		if (($content = $request->getContent()) !== NULL) {
63
-			$options['http']['content'] = $content;
64
-		}
65
-
66
-		if ($this->sslOptions) {
67
-			$options['ssl'] = $this->sslOptions + $options['ssl'];
68
-		}
69
-
70
-		list($code, $headers, $content) = $this->fileGetContents($request->getUrl(), $options);
71
-		return new Response($code, $headers, $content);
72
-	}
73
-
74
-
75
-	/**
76
-	 * @internal
77
-	 * @param  string
78
-	 * @param  array
79
-	 * @return array
80
-	 *
81
-	 * @throws BadResponseException
82
-	 */
83
-	protected function fileGetContents($url, array $contextOptions)
84
-	{
85
-		$context = stream_context_create($contextOptions);
86
-
87
-		$e = NULL;
88
-		set_error_handler(function($severity, $message, $file, $line) use (& $e) {
89
-			$e = new \ErrorException($message, 0, $severity, $file, $line, $e);
90
-		}, E_WARNING);
91
-
92
-		$content = file_get_contents($url, FALSE, $context);
93
-		restore_error_handler();
94
-
95
-		if (!isset($http_response_header)) {
96
-			throw new BadResponseException('Missing HTTP headers, request failed.', 0, $e);
97
-		}
98
-
99
-		if (!isset($http_response_header[0]) || !preg_match('~^HTTP/1[.]. (\d{3})~i', $http_response_header[0], $m)) {
100
-			throw new BadResponseException('HTTP status code is missing.', 0, $e);
101
-		}
102
-		unset($http_response_header[0]);
103
-
104
-		$headers = [];
105
-		foreach ($http_response_header as $header) {
106
-			if (in_array(substr($header, 0, 1), [' ', "\t"], TRUE)) {
107
-				$headers[$last] .= ' ' . trim($header);  # RFC2616, 2.2
108
-			} else {
109
-				list($name, $value) = explode(':', $header, 2) + [NULL, NULL];
110
-				$headers[$last = trim($name)] = trim($value);
111
-			}
112
-		}
113
-
114
-		return [$m[1], $headers, $content];
115
-	}
13
+    /** @var array|NULL */
14
+    private $sslOptions;
15
+
16
+
17
+    /**
18
+     * @param  array  SSL context options {@link http://php.net/manual/en/context.ssl.php}
19
+     */
20
+    public function __construct(array $sslOptions = NULL)
21
+    {
22
+        $this->sslOptions = $sslOptions;
23
+    }
24
+
25
+
26
+    protected function setupRequest(Request $request)
27
+    {
28
+        parent::setupRequest($request);
29
+        $request->setHeader('Connection', 'close');
30
+    }
31
+
32
+
33
+    /**
34
+     * @return Response
35
+     *
36
+     * @throws BadResponseException
37
+     */
38
+    protected function process(Request $request)
39
+    {
40
+        $headerStr = [];
41
+        foreach ($request->getHeaders() as $name => $value) {
42
+            foreach ((array) $value as $v) {
43
+                $headerStr[] = "$name: $v";
44
+            }
45
+        }
46
+
47
+        $options = [
48
+            'http' => [
49
+                'method' => $request->getMethod(),
50
+                'header' => implode("\r\n", $headerStr) . "\r\n",
51
+                'follow_location' => 0,  # Github sets the Location header for 201 code too and redirection is not required for us
52
+                'protocol_version' => 1.1,
53
+                'ignore_errors' => TRUE,
54
+            ],
55
+            'ssl' => [
56
+                'verify_peer' => TRUE,
57
+                'cafile' => realpath(__DIR__ . '/../../ca-chain.crt'),
58
+                'disable_compression' => TRUE,  # Effective since PHP 5.4.13
59
+            ],
60
+        ];
61
+
62
+        if (($content = $request->getContent()) !== NULL) {
63
+            $options['http']['content'] = $content;
64
+        }
65
+
66
+        if ($this->sslOptions) {
67
+            $options['ssl'] = $this->sslOptions + $options['ssl'];
68
+        }
69
+
70
+        list($code, $headers, $content) = $this->fileGetContents($request->getUrl(), $options);
71
+        return new Response($code, $headers, $content);
72
+    }
73
+
74
+
75
+    /**
76
+     * @internal
77
+     * @param  string
78
+     * @param  array
79
+     * @return array
80
+     *
81
+     * @throws BadResponseException
82
+     */
83
+    protected function fileGetContents($url, array $contextOptions)
84
+    {
85
+        $context = stream_context_create($contextOptions);
86
+
87
+        $e = NULL;
88
+        set_error_handler(function($severity, $message, $file, $line) use (& $e) {
89
+            $e = new \ErrorException($message, 0, $severity, $file, $line, $e);
90
+        }, E_WARNING);
91
+
92
+        $content = file_get_contents($url, FALSE, $context);
93
+        restore_error_handler();
94
+
95
+        if (!isset($http_response_header)) {
96
+            throw new BadResponseException('Missing HTTP headers, request failed.', 0, $e);
97
+        }
98
+
99
+        if (!isset($http_response_header[0]) || !preg_match('~^HTTP/1[.]. (\d{3})~i', $http_response_header[0], $m)) {
100
+            throw new BadResponseException('HTTP status code is missing.', 0, $e);
101
+        }
102
+        unset($http_response_header[0]);
103
+
104
+        $headers = [];
105
+        foreach ($http_response_header as $header) {
106
+            if (in_array(substr($header, 0, 1), [' ', "\t"], TRUE)) {
107
+                $headers[$last] .= ' ' . trim($header);  # RFC2616, 2.2
108
+            } else {
109
+                list($name, $value) = explode(':', $header, 2) + [NULL, NULL];
110
+                $headers[$last = trim($name)] = trim($value);
111
+            }
112
+        }
113
+
114
+        return [$m[1], $headers, $content];
115
+    }
116 116
 
117 117
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -47,15 +47,15 @@  discard block
 block discarded – undo
47 47
 		$options = [
48 48
 			'http' => [
49 49
 				'method' => $request->getMethod(),
50
-				'header' => implode("\r\n", $headerStr) . "\r\n",
51
-				'follow_location' => 0,  # Github sets the Location header for 201 code too and redirection is not required for us
50
+				'header' => implode("\r\n", $headerStr)."\r\n",
51
+				'follow_location' => 0, # Github sets the Location header for 201 code too and redirection is not required for us
52 52
 				'protocol_version' => 1.1,
53 53
 				'ignore_errors' => TRUE,
54 54
 			],
55 55
 			'ssl' => [
56 56
 				'verify_peer' => TRUE,
57
-				'cafile' => realpath(__DIR__ . '/../../ca-chain.crt'),
58
-				'disable_compression' => TRUE,  # Effective since PHP 5.4.13
57
+				'cafile' => realpath(__DIR__.'/../../ca-chain.crt'),
58
+				'disable_compression' => TRUE, # Effective since PHP 5.4.13
59 59
 			],
60 60
 		];
61 61
 
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
 		$headers = [];
105 105
 		foreach ($http_response_header as $header) {
106 106
 			if (in_array(substr($header, 0, 1), [' ', "\t"], TRUE)) {
107
-				$headers[$last] .= ' ' . trim($header);  # RFC2616, 2.2
107
+				$headers[$last] .= ' '.trim($header); # RFC2616, 2.2
108 108
 			} else {
109 109
 				list($name, $value) = explode(':', $header, 2) + [NULL, NULL];
110 110
 				$headers[$last = trim($name)] = trim($value);
Please login to merge, or discard this patch.
class/Github/Http/Request.php 1 patch
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -12,84 +12,84 @@
 block discarded – undo
12 12
  */
13 13
 class Request extends Message
14 14
 {
15
-	/** HTTP request method */
16
-	const
17
-		DELETE = 'DELETE',
18
-		GET = 'GET',
19
-		HEAD = 'HEAD',
20
-		PATCH = 'PATCH',
21
-		POST = 'POST',
22
-		PUT = 'PUT';
23
-
24
-
25
-	/** @var string */
26
-	private $method;
27
-
28
-	/** @var string */
29
-	private $url;
30
-
31
-
32
-	/**
33
-	 * @param  string
34
-	 * @param  string
35
-	 * @param  array
36
-	 * @param  string|NULL
37
-	 */
38
-	public function __construct($method, $url, array $headers = [], $content = NULL)
39
-	{
40
-		$this->method = $method;
41
-		$this->url = $url;
42
-		parent::__construct($headers, $content);
43
-	}
44
-
45
-
46
-	/**
47
-	 * @param  string
48
-	 * @return bool
49
-	 */
50
-	public function isMethod($method)
51
-	{
52
-		return strcasecmp($this->method, $method) === 0;
53
-	}
54
-
55
-
56
-	/**
57
-	 * @return string
58
-	 */
59
-	public function getMethod()
60
-	{
61
-		return $this->method;
62
-	}
63
-
64
-
65
-	/**
66
-	 * @return string
67
-	 */
68
-	public function getUrl()
69
-	{
70
-		return $this->url;
71
-	}
72
-
73
-
74
-	/**
75
-	 * @param  string
76
-	 * @param  string
77
-	 * @return self
78
-	 */
79
-	public function addHeader($name, $value)
80
-	{
81
-		return parent::addHeader($name, $value);
82
-	}
83
-
84
-
85
-	/**
86
-	 * @param  string
87
-	 * @param  string|NULL
88
-	 * @return self
89
-	 */
90
-	public function setHeader($name, $value)
91
-	{
92
-		return parent::setHeader($name, $value);
93
-	}
15
+    /** HTTP request method */
16
+    const
17
+        DELETE = 'DELETE',
18
+        GET = 'GET',
19
+        HEAD = 'HEAD',
20
+        PATCH = 'PATCH',
21
+        POST = 'POST',
22
+        PUT = 'PUT';
23
+
24
+
25
+    /** @var string */
26
+    private $method;
27
+
28
+    /** @var string */
29
+    private $url;
30
+
31
+
32
+    /**
33
+     * @param  string
34
+     * @param  string
35
+     * @param  array
36
+     * @param  string|NULL
37
+     */
38
+    public function __construct($method, $url, array $headers = [], $content = NULL)
39
+    {
40
+        $this->method = $method;
41
+        $this->url = $url;
42
+        parent::__construct($headers, $content);
43
+    }
44
+
45
+
46
+    /**
47
+     * @param  string
48
+     * @return bool
49
+     */
50
+    public function isMethod($method)
51
+    {
52
+        return strcasecmp($this->method, $method) === 0;
53
+    }
54
+
55
+
56
+    /**
57
+     * @return string
58
+     */
59
+    public function getMethod()
60
+    {
61
+        return $this->method;
62
+    }
63
+
64
+
65
+    /**
66
+     * @return string
67
+     */
68
+    public function getUrl()
69
+    {
70
+        return $this->url;
71
+    }
72
+
73
+
74
+    /**
75
+     * @param  string
76
+     * @param  string
77
+     * @return self
78
+     */
79
+    public function addHeader($name, $value)
80
+    {
81
+        return parent::addHeader($name, $value);
82
+    }
83
+
84
+
85
+    /**
86
+     * @param  string
87
+     * @param  string|NULL
88
+     * @return self
89
+     */
90
+    public function setHeader($name, $value)
91
+    {
92
+        return parent::setHeader($name, $value);
93
+    }
94 94
 
95 95
 }
Please login to merge, or discard this patch.
class/Github/Http/Message.php 1 patch
Indentation   +92 added lines, -92 removed lines patch added patch discarded remove patch
@@ -12,97 +12,97 @@
 block discarded – undo
12 12
  */
13 13
 abstract class Message extends Github\Sanity
14 14
 {
15
-	/** @var array[name => value] */
16
-	private $headers = [];
17
-
18
-	/** @var string|NULL */
19
-	private $content;
20
-
21
-
22
-	/**
23
-	 * @param  array
24
-	 * @param  string|NULL
25
-	 */
26
-	public function __construct(array $headers = [], $content = NULL)
27
-	{
28
-		$this->headers = array_change_key_case($headers, CASE_LOWER);
29
-		$this->content = $content;
30
-	}
31
-
32
-
33
-	/**
34
-	 * @param  string
35
-	 * @return bool
36
-	 */
37
-	public function hasHeader($name)
38
-	{
39
-		return array_key_exists(strtolower($name), $this->headers);
40
-	}
41
-
42
-
43
-	/**
44
-	 * @param  string
45
-	 * @param  mixed
46
-	 * @return mixed
47
-	 */
48
-	public function getHeader($name, $default = NULL)
49
-	{
50
-		$name = strtolower($name);
51
-		return array_key_exists($name, $this->headers)
52
-			? $this->headers[$name]
53
-			: $default;
54
-	}
55
-
56
-
57
-	/**
58
-	 * @param  string
59
-	 * @param  string
60
-	 * @return self
61
-	 */
62
-	protected function addHeader($name, $value)
63
-	{
64
-		$name = strtolower($name);
65
-		if (!array_key_exists($name, $this->headers) && $value !== NULL) {
66
-			$this->headers[$name] = $value;
67
-		}
68
-
69
-		return $this;
70
-	}
71
-
72
-
73
-	/**
74
-	 * @param  string
75
-	 * @param  string|NULL
76
-	 * @return self
77
-	 */
78
-	protected function setHeader($name, $value)
79
-	{
80
-		$name = strtolower($name);
81
-		if ($value === NULL) {
82
-			unset($this->headers[$name]);
83
-		} else {
84
-			$this->headers[$name] = $value;
85
-		}
86
-
87
-		return $this;
88
-	}
89
-
90
-
91
-	/**
92
-	 * @return array
93
-	 */
94
-	public function getHeaders()
95
-	{
96
-		return $this->headers;
97
-	}
98
-
99
-
100
-	/**
101
-	 * @return string|NULL
102
-	 */
103
-	public function getContent()
104
-	{
105
-		return $this->content;
106
-	}
15
+    /** @var array[name => value] */
16
+    private $headers = [];
17
+
18
+    /** @var string|NULL */
19
+    private $content;
20
+
21
+
22
+    /**
23
+     * @param  array
24
+     * @param  string|NULL
25
+     */
26
+    public function __construct(array $headers = [], $content = NULL)
27
+    {
28
+        $this->headers = array_change_key_case($headers, CASE_LOWER);
29
+        $this->content = $content;
30
+    }
31
+
32
+
33
+    /**
34
+     * @param  string
35
+     * @return bool
36
+     */
37
+    public function hasHeader($name)
38
+    {
39
+        return array_key_exists(strtolower($name), $this->headers);
40
+    }
41
+
42
+
43
+    /**
44
+     * @param  string
45
+     * @param  mixed
46
+     * @return mixed
47
+     */
48
+    public function getHeader($name, $default = NULL)
49
+    {
50
+        $name = strtolower($name);
51
+        return array_key_exists($name, $this->headers)
52
+            ? $this->headers[$name]
53
+            : $default;
54
+    }
55
+
56
+
57
+    /**
58
+     * @param  string
59
+     * @param  string
60
+     * @return self
61
+     */
62
+    protected function addHeader($name, $value)
63
+    {
64
+        $name = strtolower($name);
65
+        if (!array_key_exists($name, $this->headers) && $value !== NULL) {
66
+            $this->headers[$name] = $value;
67
+        }
68
+
69
+        return $this;
70
+    }
71
+
72
+
73
+    /**
74
+     * @param  string
75
+     * @param  string|NULL
76
+     * @return self
77
+     */
78
+    protected function setHeader($name, $value)
79
+    {
80
+        $name = strtolower($name);
81
+        if ($value === NULL) {
82
+            unset($this->headers[$name]);
83
+        } else {
84
+            $this->headers[$name] = $value;
85
+        }
86
+
87
+        return $this;
88
+    }
89
+
90
+
91
+    /**
92
+     * @return array
93
+     */
94
+    public function getHeaders()
95
+    {
96
+        return $this->headers;
97
+    }
98
+
99
+
100
+    /**
101
+     * @return string|NULL
102
+     */
103
+    public function getContent()
104
+    {
105
+        return $this->content;
106
+    }
107 107
 
108 108
 }
Please login to merge, or discard this patch.
class/Github/Http/CurlClient.php 2 patches
Indentation   +105 added lines, -105 removed lines patch added patch discarded remove patch
@@ -12,110 +12,110 @@
 block discarded – undo
12 12
  */
13 13
 class CurlClient extends AbstractClient
14 14
 {
15
-	/** @var array|NULL */
16
-	private $options;
17
-
18
-	/** @var resource */
19
-	private $curl;
20
-
21
-
22
-	/**
23
-	 * @param  array  cURL options {@link http://php.net/manual/en/function.curl-setopt.php}
24
-	 *
25
-	 * @throws Github\LogicException
26
-	 */
27
-	public function __construct(array $options = NULL)
28
-	{
29
-		if (!extension_loaded('curl')) {
30
-			throw new Github\LogicException('cURL extension is not loaded.');
31
-		}
32
-
33
-		$this->options = $options;
34
-	}
35
-
36
-
37
-	protected function setupRequest(Request $request)
38
-	{
39
-		parent::setupRequest($request);
40
-		$request->addHeader('Connection', 'keep-alive');
41
-	}
42
-
43
-
44
-	/**
45
-	 * @return Response
46
-	 *
47
-	 * @throws BadResponseException
48
-	 */
49
-	protected function process(Request $request)
50
-	{
51
-		$headers = [];
52
-		foreach ($request->getHeaders() as $name => $value) {
53
-			$headers[] = "$name: $value";
54
-		}
55
-
56
-		$responseHeaders = [];
57
-		$softOptions = [
58
-			CURLOPT_CONNECTTIMEOUT => 10,
59
-			CURLOPT_SSL_VERIFYHOST => 2,
60
-			CURLOPT_SSL_VERIFYPEER => 1,
61
-			CURLOPT_CAINFO => realpath(__DIR__ . '/../ca-chain.crt'),
62
-		];
63
-
64
-		$hardOptions = [
65
-			CURLOPT_FOLLOWLOCATION => FALSE, # Github sets the Location header for 201 code too and redirection is not required for us
66
-			CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
67
-			CURLOPT_CUSTOMREQUEST => $request->getMethod(),
68
-			CURLOPT_NOBODY => $request->isMethod(Request::HEAD),
69
-			CURLOPT_URL => $request->getUrl(),
70
-			CURLOPT_HTTPHEADER => $headers,
71
-			CURLOPT_RETURNTRANSFER => TRUE,
72
-			CURLOPT_POSTFIELDS => $request->getContent(),
73
-			CURLOPT_HEADER => FALSE,
74
-			CURLOPT_HEADERFUNCTION => function($curl, $line) use (& $responseHeaders, & $last) {
75
-				if (strncasecmp($line, 'HTTP/', 5) === 0) {
76
-					/** @todo Set proxy response as Response::setPrevious($proxyResponse)? */
77
-					# The HTTP/x.y may occur multiple times with proxy (HTTP/1.1 200 Connection Established)
78
-					$responseHeaders = [];
79
-
80
-				} elseif (in_array(substr($line, 0, 1), [' ', "\t"], TRUE)) {
81
-					$responseHeaders[$last] .= ' ' . trim($line);  # RFC2616, 2.2
82
-
83
-				} elseif ($line !== "\r\n") {
84
-					list($name, $value) = explode(':', $line, 2);
85
-					$responseHeaders[$last = trim($name)] = trim($value);
86
-				}
87
-
88
-				return strlen($line);
89
-			},
90
-		];
91
-
92
-		if (defined('CURLOPT_PROTOCOLS')) {  # HHVM issue. Even cURL v7.26.0, constants are missing.
93
-			$hardOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
94
-		}
95
-
96
-		if (!$this->curl) {
97
-			$this->curl = curl_init();
98
-			if ($this->curl === FALSE) {
99
-				throw new BadResponseException('Cannot init cURL handler.');
100
-			}
101
-		}
102
-
103
-		$result = curl_setopt_array($this->curl, $hardOptions + ($this->options ?: []) + $softOptions);
104
-		if ($result === FALSE) {
105
-			throw new BadResponseException('Setting cURL options failed: ' . curl_error($this->curl), curl_errno($this->curl));
106
-		}
107
-
108
-		$content = curl_exec($this->curl);
109
-		if ($content === FALSE) {
110
-			throw new BadResponseException(curl_error($this->curl), curl_errno($this->curl));
111
-		}
112
-
113
-		$code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
114
-		if ($code === FALSE) {
115
-			throw new BadResponseException('HTTP status code is missing:' . curl_error($this->curl), curl_errno($this->curl));
116
-		}
117
-
118
-		return new Response($code, $responseHeaders, $content);
119
-	}
15
+    /** @var array|NULL */
16
+    private $options;
17
+
18
+    /** @var resource */
19
+    private $curl;
20
+
21
+
22
+    /**
23
+     * @param  array  cURL options {@link http://php.net/manual/en/function.curl-setopt.php}
24
+     *
25
+     * @throws Github\LogicException
26
+     */
27
+    public function __construct(array $options = NULL)
28
+    {
29
+        if (!extension_loaded('curl')) {
30
+            throw new Github\LogicException('cURL extension is not loaded.');
31
+        }
32
+
33
+        $this->options = $options;
34
+    }
35
+
36
+
37
+    protected function setupRequest(Request $request)
38
+    {
39
+        parent::setupRequest($request);
40
+        $request->addHeader('Connection', 'keep-alive');
41
+    }
42
+
43
+
44
+    /**
45
+     * @return Response
46
+     *
47
+     * @throws BadResponseException
48
+     */
49
+    protected function process(Request $request)
50
+    {
51
+        $headers = [];
52
+        foreach ($request->getHeaders() as $name => $value) {
53
+            $headers[] = "$name: $value";
54
+        }
55
+
56
+        $responseHeaders = [];
57
+        $softOptions = [
58
+            CURLOPT_CONNECTTIMEOUT => 10,
59
+            CURLOPT_SSL_VERIFYHOST => 2,
60
+            CURLOPT_SSL_VERIFYPEER => 1,
61
+            CURLOPT_CAINFO => realpath(__DIR__ . '/../ca-chain.crt'),
62
+        ];
63
+
64
+        $hardOptions = [
65
+            CURLOPT_FOLLOWLOCATION => FALSE, # Github sets the Location header for 201 code too and redirection is not required for us
66
+            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
67
+            CURLOPT_CUSTOMREQUEST => $request->getMethod(),
68
+            CURLOPT_NOBODY => $request->isMethod(Request::HEAD),
69
+            CURLOPT_URL => $request->getUrl(),
70
+            CURLOPT_HTTPHEADER => $headers,
71
+            CURLOPT_RETURNTRANSFER => TRUE,
72
+            CURLOPT_POSTFIELDS => $request->getContent(),
73
+            CURLOPT_HEADER => FALSE,
74
+            CURLOPT_HEADERFUNCTION => function($curl, $line) use (& $responseHeaders, & $last) {
75
+                if (strncasecmp($line, 'HTTP/', 5) === 0) {
76
+                    /** @todo Set proxy response as Response::setPrevious($proxyResponse)? */
77
+                    # The HTTP/x.y may occur multiple times with proxy (HTTP/1.1 200 Connection Established)
78
+                    $responseHeaders = [];
79
+
80
+                } elseif (in_array(substr($line, 0, 1), [' ', "\t"], TRUE)) {
81
+                    $responseHeaders[$last] .= ' ' . trim($line);  # RFC2616, 2.2
82
+
83
+                } elseif ($line !== "\r\n") {
84
+                    list($name, $value) = explode(':', $line, 2);
85
+                    $responseHeaders[$last = trim($name)] = trim($value);
86
+                }
87
+
88
+                return strlen($line);
89
+            },
90
+        ];
91
+
92
+        if (defined('CURLOPT_PROTOCOLS')) {  # HHVM issue. Even cURL v7.26.0, constants are missing.
93
+            $hardOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
94
+        }
95
+
96
+        if (!$this->curl) {
97
+            $this->curl = curl_init();
98
+            if ($this->curl === FALSE) {
99
+                throw new BadResponseException('Cannot init cURL handler.');
100
+            }
101
+        }
102
+
103
+        $result = curl_setopt_array($this->curl, $hardOptions + ($this->options ?: []) + $softOptions);
104
+        if ($result === FALSE) {
105
+            throw new BadResponseException('Setting cURL options failed: ' . curl_error($this->curl), curl_errno($this->curl));
106
+        }
107
+
108
+        $content = curl_exec($this->curl);
109
+        if ($content === FALSE) {
110
+            throw new BadResponseException(curl_error($this->curl), curl_errno($this->curl));
111
+        }
112
+
113
+        $code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
114
+        if ($code === FALSE) {
115
+            throw new BadResponseException('HTTP status code is missing:' . curl_error($this->curl), curl_errno($this->curl));
116
+        }
117
+
118
+        return new Response($code, $responseHeaders, $content);
119
+    }
120 120
 
121 121
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -58,7 +58,7 @@  discard block
 block discarded – undo
58 58
 			CURLOPT_CONNECTTIMEOUT => 10,
59 59
 			CURLOPT_SSL_VERIFYHOST => 2,
60 60
 			CURLOPT_SSL_VERIFYPEER => 1,
61
-			CURLOPT_CAINFO => realpath(__DIR__ . '/../ca-chain.crt'),
61
+			CURLOPT_CAINFO => realpath(__DIR__.'/../ca-chain.crt'),
62 62
 		];
63 63
 
64 64
 		$hardOptions = [
@@ -78,7 +78,7 @@  discard block
 block discarded – undo
78 78
 					$responseHeaders = [];
79 79
 
80 80
 				} elseif (in_array(substr($line, 0, 1), [' ', "\t"], TRUE)) {
81
-					$responseHeaders[$last] .= ' ' . trim($line);  # RFC2616, 2.2
81
+					$responseHeaders[$last] .= ' '.trim($line); # RFC2616, 2.2
82 82
 
83 83
 				} elseif ($line !== "\r\n") {
84 84
 					list($name, $value) = explode(':', $line, 2);
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 
103 103
 		$result = curl_setopt_array($this->curl, $hardOptions + ($this->options ?: []) + $softOptions);
104 104
 		if ($result === FALSE) {
105
-			throw new BadResponseException('Setting cURL options failed: ' . curl_error($this->curl), curl_errno($this->curl));
105
+			throw new BadResponseException('Setting cURL options failed: '.curl_error($this->curl), curl_errno($this->curl));
106 106
 		}
107 107
 
108 108
 		$content = curl_exec($this->curl);
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
 
113 113
 		$code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
114 114
 		if ($code === FALSE) {
115
-			throw new BadResponseException('HTTP status code is missing:' . curl_error($this->curl), curl_errno($this->curl));
115
+			throw new BadResponseException('HTTP status code is missing:'.curl_error($this->curl), curl_errno($this->curl));
116 116
 		}
117 117
 
118 118
 		return new Response($code, $responseHeaders, $content);
Please login to merge, or discard this patch.
class/Github/Http/Response.php 1 patch
Indentation   +75 added lines, -75 removed lines patch added patch discarded remove patch
@@ -12,80 +12,80 @@
 block discarded – undo
12 12
  */
13 13
 class Response extends Message
14 14
 {
15
-	/** HTTP 1.1 code */
16
-	const
17
-		S200_OK = 200,
18
-		S301_MOVED_PERMANENTLY = 301,
19
-		S302_FOUND = 302,
20
-		S304_NOT_MODIFIED = 304,
21
-		S307_TEMPORARY_REDIRECT = 307,
22
-		S400_BAD_REQUEST = 400,
23
-		S401_UNAUTHORIZED = 401,
24
-		S403_FORBIDDEN = 403,
25
-		S404_NOT_FOUND = 404,
26
-		S422_UNPROCESSABLE_ENTITY = 422;
27
-
28
-	/** @var int */
29
-	private $code;
30
-
31
-	/** @var Response */
32
-	private $previous;
33
-
34
-
35
-	/**
36
-	 * @param  int
37
-	 * @param  array
38
-	 * @param  string
39
-	 */
40
-	public function __construct($code, array $headers, $content)
41
-	{
42
-		$this->code = (int) $code;
43
-		parent::__construct($headers, $content);
44
-	}
45
-
46
-
47
-	/**
48
-	 * HTTP code.
49
-	 * @return int
50
-	 */
51
-	public function getCode()
52
-	{
53
-		return $this->code;
54
-	}
55
-
56
-
57
-	/**
58
-	 * @param  int
59
-	 * @return bool
60
-	 */
61
-	public function isCode($code)
62
-	{
63
-		return $this->code === (int) $code;
64
-	}
65
-
66
-
67
-	/**
68
-	 * @return Response|NULL
69
-	 */
70
-	public function getPrevious()
71
-	{
72
-		return $this->previous;
73
-	}
74
-
75
-
76
-	/**
77
-	 * @return self
78
-	 *
79
-	 * @throws Github\LogicException
80
-	 */
81
-	public function setPrevious(Response $previous = NULL)
82
-	{
83
-		if ($this->previous) {
84
-			throw new Github\LogicException('Previous response is already set.');
85
-		}
86
-		$this->previous = $previous;
87
-
88
-		return $this;
89
-	}
15
+    /** HTTP 1.1 code */
16
+    const
17
+        S200_OK = 200,
18
+        S301_MOVED_PERMANENTLY = 301,
19
+        S302_FOUND = 302,
20
+        S304_NOT_MODIFIED = 304,
21
+        S307_TEMPORARY_REDIRECT = 307,
22
+        S400_BAD_REQUEST = 400,
23
+        S401_UNAUTHORIZED = 401,
24
+        S403_FORBIDDEN = 403,
25
+        S404_NOT_FOUND = 404,
26
+        S422_UNPROCESSABLE_ENTITY = 422;
27
+
28
+    /** @var int */
29
+    private $code;
30
+
31
+    /** @var Response */
32
+    private $previous;
33
+
34
+
35
+    /**
36
+     * @param  int
37
+     * @param  array
38
+     * @param  string
39
+     */
40
+    public function __construct($code, array $headers, $content)
41
+    {
42
+        $this->code = (int) $code;
43
+        parent::__construct($headers, $content);
44
+    }
45
+
46
+
47
+    /**
48
+     * HTTP code.
49
+     * @return int
50
+     */
51
+    public function getCode()
52
+    {
53
+        return $this->code;
54
+    }
55
+
56
+
57
+    /**
58
+     * @param  int
59
+     * @return bool
60
+     */
61
+    public function isCode($code)
62
+    {
63
+        return $this->code === (int) $code;
64
+    }
65
+
66
+
67
+    /**
68
+     * @return Response|NULL
69
+     */
70
+    public function getPrevious()
71
+    {
72
+        return $this->previous;
73
+    }
74
+
75
+
76
+    /**
77
+     * @return self
78
+     *
79
+     * @throws Github\LogicException
80
+     */
81
+    public function setPrevious(Response $previous = NULL)
82
+    {
83
+        if ($this->previous) {
84
+            throw new Github\LogicException('Previous response is already set.');
85
+        }
86
+        $this->previous = $previous;
87
+
88
+        return $this;
89
+    }
90 90
 
91 91
 }
Please login to merge, or discard this patch.
class/Github/Http/AbstractClient.php 1 patch
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -12,98 +12,98 @@
 block discarded – undo
12 12
  */
13 13
 abstract class AbstractClient extends Github\Sanity implements IClient
14 14
 {
15
-	/** @var int[]  will follow Location header on these response codes */
16
-	public $redirectCodes = [
17
-		Response::S301_MOVED_PERMANENTLY,
18
-		Response::S302_FOUND,
19
-		Response::S307_TEMPORARY_REDIRECT,
20
-	];
21
-
22
-	/** @var int  maximum redirects per request*/
23
-	public $maxRedirects = 5;
24
-
25
-	/** @var callable|NULL */
26
-	private $onRequest;
27
-
28
-	/** @var callable|NULL */
29
-	private $onResponse;
30
-
31
-
32
-	/**
33
-	 * @see https://developer.github.com/v3/#http-redirects
34
-	 *
35
-	 * @return Response
36
-	 *
37
-	 * @throws BadResponseException
38
-	 */
39
-	public function request(Request $request)
40
-	{
41
-		$request = clone $request;
42
-
43
-		$counter = $this->maxRedirects;
44
-		$previous = NULL;
45
-		do {
46
-			$this->setupRequest($request);
47
-
48
-			$this->onRequest && call_user_func($this->onRequest, $request);
49
-			$response = $this->process($request);
50
-			$this->onResponse && call_user_func($this->onResponse, $response);
51
-
52
-			$previous = $response->setPrevious($previous);
53
-
54
-			if ($counter > 0 && in_array($response->getCode(), $this->redirectCodes) && $response->hasHeader('Location')) {
55
-				/** @todo Use the same HTTP $method for redirection? Set $content to NULL? */
56
-				$request = new Request(
57
-					$request->getMethod(),
58
-					$response->getHeader('Location'),
59
-					$request->getHeaders(),
60
-					$request->getContent()
61
-				);
62
-
63
-				$counter--;
64
-				continue;
65
-			}
66
-			break;
67
-
68
-		} while (TRUE);
69
-
70
-		return $response;
71
-	}
72
-
73
-
74
-	/**
75
-	 * @param  callable|NULL function(Request $request)
76
-	 * @return self
77
-	 */
78
-	public function onRequest($callback)
79
-	{
80
-		$this->onRequest = $callback;
81
-		return $this;
82
-	}
83
-
84
-
85
-	/**
86
-	 * @param  callable|NULL function(Response $response)
87
-	 * @return self
88
-	 */
89
-	public function onResponse($callback)
90
-	{
91
-		$this->onResponse = $callback;
92
-		return $this;
93
-	}
94
-
95
-
96
-	protected function setupRequest(Request $request)
97
-	{
98
-		$request->addHeader('Expect', '');
99
-	}
100
-
101
-
102
-	/**
103
-	 * @return Response
104
-	 *
105
-	 * @throws BadResponseException
106
-	 */
107
-	abstract protected function process(Request $request);
15
+    /** @var int[]  will follow Location header on these response codes */
16
+    public $redirectCodes = [
17
+        Response::S301_MOVED_PERMANENTLY,
18
+        Response::S302_FOUND,
19
+        Response::S307_TEMPORARY_REDIRECT,
20
+    ];
21
+
22
+    /** @var int  maximum redirects per request*/
23
+    public $maxRedirects = 5;
24
+
25
+    /** @var callable|NULL */
26
+    private $onRequest;
27
+
28
+    /** @var callable|NULL */
29
+    private $onResponse;
30
+
31
+
32
+    /**
33
+     * @see https://developer.github.com/v3/#http-redirects
34
+     *
35
+     * @return Response
36
+     *
37
+     * @throws BadResponseException
38
+     */
39
+    public function request(Request $request)
40
+    {
41
+        $request = clone $request;
42
+
43
+        $counter = $this->maxRedirects;
44
+        $previous = NULL;
45
+        do {
46
+            $this->setupRequest($request);
47
+
48
+            $this->onRequest && call_user_func($this->onRequest, $request);
49
+            $response = $this->process($request);
50
+            $this->onResponse && call_user_func($this->onResponse, $response);
51
+
52
+            $previous = $response->setPrevious($previous);
53
+
54
+            if ($counter > 0 && in_array($response->getCode(), $this->redirectCodes) && $response->hasHeader('Location')) {
55
+                /** @todo Use the same HTTP $method for redirection? Set $content to NULL? */
56
+                $request = new Request(
57
+                    $request->getMethod(),
58
+                    $response->getHeader('Location'),
59
+                    $request->getHeaders(),
60
+                    $request->getContent()
61
+                );
62
+
63
+                $counter--;
64
+                continue;
65
+            }
66
+            break;
67
+
68
+        } while (TRUE);
69
+
70
+        return $response;
71
+    }
72
+
73
+
74
+    /**
75
+     * @param  callable|NULL function(Request $request)
76
+     * @return self
77
+     */
78
+    public function onRequest($callback)
79
+    {
80
+        $this->onRequest = $callback;
81
+        return $this;
82
+    }
83
+
84
+
85
+    /**
86
+     * @param  callable|NULL function(Response $response)
87
+     * @return self
88
+     */
89
+    public function onResponse($callback)
90
+    {
91
+        $this->onResponse = $callback;
92
+        return $this;
93
+    }
94
+
95
+
96
+    protected function setupRequest(Request $request)
97
+    {
98
+        $request->addHeader('Expect', '');
99
+    }
100
+
101
+
102
+    /**
103
+     * @return Response
104
+     *
105
+     * @throws BadResponseException
106
+     */
107
+    abstract protected function process(Request $request);
108 108
 
109 109
 }
Please login to merge, or discard this patch.