Completed
Push — master ( 0fa021...08bd3f )
by smiley
01:32
created

HTTPClientAbstract   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 25
lcom 1
cbo 3
dl 0
loc 175
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
getResponse() 0 1 ?
A request() 0 20 4
B normalizeRequestHeaders() 0 23 4
A rawurlencode() 0 11 3
B buildQuery() 0 44 6
B checkQueryParams() 0 17 7
1
<?php
2
/**
3
 * Class HTTPClientAbstract
4
 *
5
 * @filesource   HTTPClientAbstract.php
6
 * @created      09.07.2017
7
 * @package      chillerlan\HTTP
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2017 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\HTTP;
14
15
use chillerlan\Traits\ContainerInterface;
16
use Psr\Log\{
17
	LoggerAwareInterface, LoggerAwareTrait, LoggerInterface, NullLogger
18
};
19
20
abstract class HTTPClientAbstract implements HTTPClientInterface, LoggerAwareInterface{
21
	use LoggerAwareTrait;
22
23
	/**
24
	 * @var mixed
25
	 */
26
	protected $http;
27
28
	/**
29
	 * @var \chillerlan\Traits\ContainerInterface|mixed
30
	 */
31
	protected $options;
32
33
	protected $requestURL;
34
	protected $parsedURL;
35
	protected $requestParams;
36
	protected $requestMethod;
37
	protected $requestBody;
38
	protected $requestHeaders;
39
40
	/** @inheritdoc */
41
	public function __construct(ContainerInterface $options, LoggerInterface $logger = null){
42
		$this->options = $options;
43
		$this->logger  = $logger ?? new NullLogger;
44
	}
45
46
	/**
47
	 * @return \chillerlan\HTTP\HTTPResponseInterface
48
	 */
49
	abstract protected function getResponse():HTTPResponseInterface;
50
51
	/**
52
	 * @param string      $url
53
	 * @param array|null  $params
54
	 * @param string|null $method
55
	 * @param null        $body
56
	 * @param array|null  $headers
57
	 *
58
	 * @return \chillerlan\HTTP\HTTPResponseInterface
59
	 * @throws \chillerlan\HTTP\HTTPClientException
60
	 */
61
	public function request(string $url, array $params = null, string $method = null, $body = null, array $headers = null):HTTPResponseInterface{
62
		$this->requestURL    = $url;
63
		$this->parsedURL     = parse_url($this->requestURL);
64
		$this->requestParams = $params ?? [];
65
		$this->requestMethod = strtoupper($method ?? 'POST');
66
		$this->requestBody   = $body;
67
		$this->requestHeaders = $headers ?? [];
68
69
		if(!isset($this->parsedURL['host']) || !in_array($this->parsedURL['scheme'], $this::ALLOWED_SCHEMES, true)){
70
			throw new HTTPClientException('invalid URL');
71
		}
72
73
		try{
74
			return $this->getResponse();
75
		}
76
		catch(\Exception $e){
77
			throw new HTTPClientException('fetch error: '.$e->getMessage());
78
		}
79
80
	}
81
82
	/** @inheritdoc */
83
	public function normalizeRequestHeaders(array $headers):array {
84
		$normalized_headers = [];
85
86
		foreach($headers as $key => $val){
87
88
			if(is_numeric($key)){
89
				$header = explode(':', $val, 2);
90
91
				if(count($header) !== 2){
92
					continue;
93
				}
94
95
				$key = $header[0];
96
				$val = $header[1];
97
			}
98
99
			$key = ucfirst(strtolower($key));
100
101
			$normalized_headers[$key] = trim($key).': '.trim($val);
102
		}
103
104
		return $normalized_headers;
105
	}
106
107
	/** @inheritdoc */
108
	public function rawurlencode($data){
109
110
		if(is_array($data)){
111
			return array_map([$this, 'rawurlencode'], $data);
112
		}
113
		elseif(is_scalar($data)){
114
			return rawurlencode($data);
115
		}
116
117
		return $data; // @codeCoverageIgnore
118
	}
119
120
	/**
121
	 * from https://github.com/abraham/twitteroauth/blob/master/src/Util.php
122
	 *
123
	 * @param array  $params
124
	 * @param bool   $urlencode
0 ignored issues
show
Documentation introduced by
Should the type for parameter $urlencode not be null|boolean?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
125
	 * @param string $delimiter
0 ignored issues
show
Documentation introduced by
Should the type for parameter $delimiter not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
126
	 * @param string $enclosure
0 ignored issues
show
Documentation introduced by
Should the type for parameter $enclosure not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
127
	 *
128
	 * @return string
129
	 */
130
	public function buildQuery(array $params, bool $urlencode = null, string $delimiter = null, string $enclosure = null):string {
131
132
		if(empty($params)) {
133
			return '';
134
		}
135
136
		// urlencode both keys and values
137
		if($urlencode ?? true){
138
			$params = array_combine(
139
				$this->rawurlencode(array_keys($params)),
140
				$this->rawurlencode(array_values($params))
141
			);
142
		}
143
144
		// Parameters are sorted by name, using lexicographical byte value ordering.
145
		// Ref: Spec: 9.1.1 (1)
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
146
		uksort($params, 'strcmp');
147
148
		$pairs     = [];
149
		$enclosure = $enclosure ?? '';
150
151
		foreach($params as $parameter => $value){
152
153
			if(is_array($value)) {
154
				// If two or more parameters share the same name, they are sorted by their value
155
				// Ref: Spec: 9.1.1 (1)
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
156
				// June 12th, 2010 - changed to sort because of issue 164 by hidetaka
157
				sort($value, SORT_STRING);
158
159
				foreach ($value as $duplicateValue) {
160
					$pairs[] = $parameter.'='.$enclosure.$duplicateValue.$enclosure;
161
				}
162
163
			}
164
			else{
165
				$pairs[] = $parameter.'='.$enclosure.$value.$enclosure;
166
			}
167
168
		}
169
170
		// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
171
		// Each name-value pair is separated by an '&' character (ASCII code 38)
172
		return implode($delimiter ?? '&', $pairs);
173
	}
174
175
	/** @inheritdoc */
176
	public function checkQueryParams(array $params, bool $booleans_as_string = null):array{
177
178
		foreach($params as $key => $value){
179
180
			if(is_bool($value)){
181
				$params[$key] = $booleans_as_string === true
182
					? ($value ? 'true' : 'false')
183
					: (string)(int)$value;
184
			}
185
			elseif(is_null($value) || empty($value)){
186
				unset($params[$key]);
187
			}
188
189
		}
190
191
		return $params;
192
	}
193
194
}
195