Passed
Push — main ( f2efe3...53dc0a )
by smiley
10:15
created

StreamClient::sendRequest()   A

Complexity

Conditions 3
Paths 6

Size

Total Lines 58
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 3
eloc 38
nc 6
nop 1
dl 0
loc 58
rs 9.312
c 3
b 0
f 0

How to fix   Long Method   

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
 * Class StreamClient
4
 *
5
 * @filesource   StreamClient.php
6
 * @created      23.02.2019
7
 * @package      chillerlan\HTTP\Psr18
8
 * @author       smiley <[email protected]>
9
 * @copyright    2019 smiley
10
 * @license      MIT
11
 *
12
 * @phan-file-suppress PhanTypeInvalidThrowsIsInterface
13
 */
14
15
namespace chillerlan\HTTP\Psr18;
16
17
use ErrorException, Exception;
18
use Psr\Http\Message\{RequestInterface, ResponseInterface};
19
20
use function explode, file_get_contents, get_headers, in_array, intval, restore_error_handler,
21
	set_error_handler, stream_context_create, strtolower, substr, trim;
22
23
class StreamClient extends HTTPClientAbstract{
24
25
	/**
26
	 * @inheritDoc
27
	 * @throws \Psr\Http\Client\ClientExceptionInterface
28
	 *
29
	 */
30
	public function sendRequest(RequestInterface $request):ResponseInterface{
31
		$uri     = $request->getUri();
32
		$method  = $request->getMethod();
33
		$headers = $this->getRequestHeaders($request);
34
35
		$body = in_array($method, ['DELETE', 'PATCH', 'POST', 'PUT'], true)
36
			? $request->getBody()->getContents()
37
			: null;
38
39
		$context = stream_context_create([
40
			'http' => [
41
				'method'           => $method,
42
				'header'           => $headers,
43
				'content'          => $body,
44
				'protocol_version' => '1.1',
45
				'user_agent'       => $this->options->user_agent,
46
				'max_redirects'    => 0,
47
				'timeout'          => 5,
48
			],
49
			'ssl'  => [
50
				'cafile'              => $this->options->ca_info,
51
				'verify_peer'         => $this->options->ssl_verifypeer,
52
				'verify_depth'        => 3,
53
				'peer_name'           => $uri->getHost(),
54
				'ciphers'             => 'HIGH:!SSLv2:!SSLv3',
55
				'disable_compression' => true,
56
			],
57
		]);
58
59
		$requestUri = (string)$uri->withFragment('');
60
61
		/** @phan-suppress-next-line PhanTypeMismatchArgumentInternal */
62
		set_error_handler(function(int $severity, string $msg, string $file, int $line):void{
63
			throw new ErrorException($msg, 0, $severity, $file, $line);
64
		});
65
66
		try{
67
			$responseBody    = file_get_contents($requestUri, false, $context);
68
			/** @phan-suppress-next-line PhanTypeMismatchArgumentInternal https://github.com/phan/phan/issues/3273 */
69
			$responseHeaders = $this->parseResponseHeaders(get_headers($requestUri, 1, $context));
70
		}
71
		catch(Exception $e){
72
			$this->logger->error('StreamClient error #'.$e->getCode().': '.$e->getMessage());
73
74
			throw new ClientException($e->getMessage(), $e->getCode());
75
		}
76
77
		restore_error_handler();
78
79
		$response = $this->responseFactory
80
			->createResponse($responseHeaders['statuscode'], $responseHeaders['statustext'])
81
			->withProtocolVersion($responseHeaders['httpversion'])
82
		;
83
84
		$response->getBody()->write($responseBody);
85
		$response->getBody()->rewind();
86
87
		return $response;
88
	}
89
90
	/**
91
	 *
92
	 */
93
	protected function getRequestHeaders(RequestInterface $request):array{
94
		$headers = [];
95
96
		foreach($request->getHeaders() as $name => $values){
97
			$name = strtolower($name);
98
99
			foreach($values as $value){
100
				$value = (string)$value;
101
102
				// cURL requires a special format for empty headers.
103
				// See https://github.com/guzzle/guzzle/issues/1882 for more details.
104
				$headers[] = $value === '' ? $name.';' : $name.': '.$value;
105
			}
106
		}
107
108
		return $headers;
109
	}
110
111
	/**
112
	 * @param string[] $headers
113
	 */
114
	protected function parseResponseHeaders(array $headers):array{
115
		$h = [];
116
117
		foreach($headers as $k => $v){
118
119
			if($k === 0 && substr($v, 0, 4) === 'HTTP'){
120
				$status = explode(' ', $v, 3);
121
122
				$h['httpversion'] = explode('/', $status[0], 2)[1];
123
				$h['statuscode']  = intval($status[1]);
124
				$h['statustext']  = trim($status[2]);
125
126
				continue;
127
			}
128
129
			$h[strtolower($k)] = $v;
130
		}
131
132
		return $h;
133
	}
134
135
}
136