Completed
Push — master ( 78d4e7...979ecb )
by smiley
02:57
created

message_helpers.php ➔ build_http_query()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 7
nop 4
dl 0
loc 44
rs 8.5937
c 0
b 0
f 0
1
<?php
2
/**
3
 * @filesource   message_helpers.php
4
 * @created      28.08.2018
5
 * @author       smiley <[email protected]>
6
 * @copyright    2018 smiley
7
 * @license      MIT
8
 */
9
10
namespace chillerlan\HTTP\Psr7;
11
12
use InvalidArgumentException;
13
use Psr\Http\Message\UploadedFileInterface;
14
15
const PSR7_INCLUDES = true;
16
17
/**
18
 * Normalizes an array of header lines to format "Name: Value"
19
 *
20
 * @param array $headers
21
 *
22
 * @return array
23
 */
24
function normalize_request_headers(array $headers):array{
25
	$normalized_headers = [];
26
27
	foreach($headers as $key => $val){
28
29
		if(is_numeric($key)){
30
31
			if(is_string($val)){
32
				$header = explode(':', $val, 2);
33
34
				if(count($header) !== 2){
35
					continue;
36
				}
37
38
				$key = $header[0];
39
				$val = $header[1];
40
			}
41
			elseif(is_array($val)){
42
				$key = array_keys($val)[0];
43
				$val = array_values($val)[0];
44
			}
45
			else{
46
				continue;
47
			}
48
		}
49
50
		$key = ucfirst(strtolower(trim($key)));
51
52
		$normalized_headers[$key] = trim($val);
53
	}
54
55
	return $normalized_headers;
56
}
57
58
/**
59
 * @param mixed $data
60
 *
61
 * @return mixed
62
 */
63
function raw_urlencode($data){
64
65
	if(is_array($data)){
66
		return array_map(__NAMESPACE__.'\\raw_urlencode', $data);
67
	}
68
69
	return rawurlencode($data);
70
}
71
72
/**
73
 * from https://github.com/abraham/twitteroauth/blob/master/src/Util.php
74
 *
75
 * @param array  $params
76
 * @param bool   $urlencode
77
 * @param string $delimiter
78
 * @param string $enclosure
79
 *
80
 * @return string
81
 */
82
function build_http_query(array $params, bool $urlencode = null, string $delimiter = null, string $enclosure = null):string{
83
84
	if(empty($params)){
85
		return '';
86
	}
87
88
	// urlencode both keys and values
89
	if($urlencode ?? true){
90
		$params = array_combine(
91
			raw_urlencode(array_keys($params)),
92
			raw_urlencode(array_values($params))
93
		);
94
	}
95
96
	// Parameters are sorted by name, using lexicographical byte value ordering.
97
	// Ref: Spec: 9.1.1 (1)
98
	uksort($params, 'strcmp');
99
100
	$pairs     = [];
101
	$enclosure = $enclosure ?? '';
102
103
	foreach($params as $parameter => $value){
104
105
		if(is_array($value)){
106
			// If two or more parameters share the same name, they are sorted by their value
107
			// Ref: Spec: 9.1.1 (1)
108
			// June 12th, 2010 - changed to sort because of issue 164 by hidetaka
109
			sort($value, SORT_STRING);
110
111
			foreach($value as $duplicateValue){
112
				$pairs[] = $parameter.'='.$enclosure.$duplicateValue.$enclosure;
113
			}
114
115
		}
116
		else{
117
			$pairs[] = $parameter.'='.$enclosure.$value.$enclosure;
118
		}
119
120
	}
121
122
	// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
123
	// Each name-value pair is separated by an '&' character (ASCII code 38)
124
	return implode($delimiter ?? '&', $pairs);
125
}
126
127
/**
128
 * @param iterable  $params
129
 * @param bool|null $booleans_as_string - converts booleans to "true"/"false" strings if set to true, "0"/"1" otherwise.
130
 *
131
 * @return array
132
 */
133
function clean_query_params(iterable $params, bool $booleans_as_string = null):array{
134
	$p = [];
135
136
	foreach($params as $key => $value){
137
138
		if(is_bool($value)){
139
			$p[$key] = $booleans_as_string === true
140
				? ($value ? 'true' : 'false')
141
				: (string)(int)$value;
142
		}
143
		elseif($value === null || (!is_numeric($value) && empty($value))){
144
			continue;
145
		}
146
		else{
147
			$p[$key] = $value;
148
		}
149
	}
150
151
	return $p;
152
}
153
154
/**
155
 * merges additional query parameters into an existing query string
156
 *
157
 * @param string $uri
158
 * @param array  $query
159
 *
160
 * @return string
161
 */
162
function merge_query(string $uri, array $query):string{
163
	parse_str(parse_url($uri, PHP_URL_QUERY), $parsedquery);
164
165
	$requestURI = explode('?', $uri)[0];
166
	$params     = array_merge($parsedquery, $query);
167
168
	if(!empty($params)){
169
		$requestURI .= '?'.build_http_query($params);
170
	}
171
172
	return $requestURI;
173
}
174
175
/**
176
 * Return an UploadedFile instance array.
177
 *
178
 * @param array $files A array which respect $_FILES structure
179
 *
180
 * @throws \InvalidArgumentException for unrecognized values
181
 * @return array
182
 */
183
function normalize_files(array $files):array{
184
	$normalized = [];
185
186
	foreach($files as $key => $value){
187
188
		if($value instanceof UploadedFileInterface){
189
			$normalized[$key] = $value;
190
		}
191
		elseif(is_array($value) && isset($value['tmp_name'])){
192
			$normalized[$key] = create_uploaded_file_from_spec($value);
193
		}
194
		elseif(is_array($value)){
195
			$normalized[$key] = normalize_files($value);
196
			continue;
197
		}
198
		else{
199
			throw new InvalidArgumentException('Invalid value in files specification');
200
		}
201
202
	}
203
204
	return $normalized;
205
}
206
207
/**
208
 * Create and return an UploadedFile instance from a $_FILES specification.
209
 *
210
 * If the specification represents an array of values, this method will
211
 * delegate to normalizeNestedFileSpec() and return that return value.
212
 *
213
 * @param array $value $_FILES struct
214
 *
215
 * @return array|\Psr\Http\Message\UploadedFileInterface
216
 */
217
function create_uploaded_file_from_spec(array $value){
218
219
	if(is_array($value['tmp_name'])){
220
		return normalize_nested_file_spec($value);
221
	}
222
223
	return new UploadedFile($value['tmp_name'], (int)$value['size'], (int)$value['error'], $value['name'], $value['type']);
224
}
225
226
/**
227
 * Normalize an array of file specifications.
228
 *
229
 * Loops through all nested files and returns a normalized array of
230
 * UploadedFileInterface instances.
231
 *
232
 * @param array $files
233
 *
234
 * @return \Psr\Http\Message\UploadedFileInterface[]
235
 */
236
function normalize_nested_file_spec(array $files = []):array{
237
	$normalizedFiles = [];
238
239
	foreach(array_keys($files['tmp_name']) as $key){
240
		$spec = [
241
			'tmp_name' => $files['tmp_name'][$key],
242
			'size'     => $files['size'][$key],
243
			'error'    => $files['error'][$key],
244
			'name'     => $files['name'][$key],
245
			'type'     => $files['type'][$key],
246
		];
247
248
		$normalizedFiles[$key] = create_uploaded_file_from_spec($spec);
249
	}
250
251
	return $normalizedFiles;
252
}
253