Completed
Pull Request — master (#2)
by
unknown
01:37
created

Http::setRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
3
namespace DominionEnterprises\Util;
4
5
use Exception;
6
use InvalidArgumentException;
7
8
/**
9
 * Static class with various HTTP related functions.
10
 */
11
final class Http
12
{
13
    /**
14
     * Parses HTTP headers into an associative array.
15
     *
16
     * Example:
17
     * <code>
18
     * $headers = "HTTP/1.1 200 OK\r\n".
19
     *            "content-type: text/html; charset=UTF-8\r\n".
20
     *            "Server: Funky/1.0\r\n".
21
     *            "Set-Cookie: foo=bar\r\n".
22
     *            "Set-Cookie: baz=quux\r\n".
23
     *            "Folds: are\r\n\treformatted\r\n";
24
     * print_r(\DominionEnterprises\HttpUtil::parseHeaders($headers));
25
     * </code>
26
     * The above example will output:
27
     * <pre>
28
     * Array
29
     * (
30
     *     [Response Code] => 200
31
     *     [Response Status] => OK
32
     *     [Content-Type] => text/html; charset=UTF-8
33
     *     [Server] => Funky/1.0
34
     *     [Set-Cookie] => Array
35
     *     (
36
     *       [0] => foo=bar
37
     *       [1] => baz=quux
38
     *     )
39
     *     [Folds] => are reformatted
40
     * )
41
     * </pre>
42
     *
43
     * @param string $rawHeaders string containing HTTP headers
44
     *
45
     * @return array the parsed headers
46
     *
47
     * @throws Exception Thrown if unable to parse the headers
48
     */
49
    public static function parseHeaders(string $rawHeaders) : array
50
    {
51
        if (!is_string($rawHeaders) || trim($rawHeaders) === '') {
52
            throw new InvalidArgumentException('$rawHeaders was not a string');
53
        }
54
55
        $headers = [];
56
        $rawHeaders = preg_replace("/\r\n[\t ]+/", ' ', trim($rawHeaders));
57
        $fields = explode("\r\n", $rawHeaders);
58
        foreach ($fields as $field) {
59
            $match = null;
60
            if (preg_match('/([^:]+): (.+)/m', $field, $match)) {
61
                $key = $match[1];
62
                // convert 'some-header' to 'Some-Header'
63
                $key = strtolower(trim($key));
64
                $key = ucwords(preg_replace('/[\s-]/', ' ', $key));
65
                $key = strtr($key, ' ', '-');
66
67
                $value = trim($match[2]);
68
69
                if (!array_key_exists($key, $headers)) {
70
                    $headers[$key] = $value;
71
                    continue;
72
                }
73
74
                if (!is_array($headers[$key])) {
75
                    $headers[$key] = [$headers[$key]];
76
                }
77
78
                $headers[$key][] = $value;
79
                continue;
80
            }
81
82
            if (self::isRequest($field, $match)) {
83
                $headers = self::setRequest($match, $headers);
84
                continue;
85
            }
86
87
            if (self::isResponse($field, $match)) {
88
                $headers = self::setResponse($match, $headers);
89
                continue;
90
            }
91
92
            throw new Exception("Unsupported header format: {$field}");
93
        }
94
95
        return $headers;
96
    }
97
98
    /**
99
     * Generate URL-encoded query string
100
     *
101
     * Example:
102
     * <code>
103
     * $parameters = [
104
     *   'param1' => ['value', 'another value'],
105
     *   'param2' => 'a value',
106
     *   'param3' => false,
107
     * ];
108
     *
109
     * $queryString = \DominionEnterprises\HttpUtil::buildQueryString($parameters);
110
     *
111
     * echo $queryString
112
     * </code>
113
     *
114
     * Output:
115
     * <pre>
116
     * param1=value&param1=another+value&param2=a+value&param3=false
117
     * </pre>
118
     *
119
     * @param array $parameters An associative array containing parameter key/value(s)
120
     *
121
     * @return string the built query string
122
     */
123
    public static function buildQueryString(array $parameters) : string
124
    {
125
        $queryStrings = [];
126
        foreach ($parameters as $parameterName => $parameterValue) {
127
            $parameterName = rawurlencode($parameterName);
128
129
            if (is_array($parameterValue)) {
130
                foreach ($parameterValue as $eachValue) {
131
                    $eachValue = rawurlencode($eachValue);
132
                    $queryStrings[] = "{$parameterName}={$eachValue}";
133
                }
134
            } elseif ($parameterValue === false) {
135
                $queryStrings[] = "{$parameterName}=false";
136
            } elseif ($parameterValue === true) {
137
                $queryStrings[] = "{$parameterName}=true";
138
            } else {
139
                $parameterValue = rawurlencode($parameterValue);
140
                $queryStrings[] = "{$parameterName}={$parameterValue}";
141
            }
142
        }
143
144
        return implode('&', $queryStrings);
145
    }
146
147
    /**
148
     * Get an array of all url parameters.
149
     *
150
     * @param string $url The url to parse such as http://foo.com/bar/?id=boo&another=wee&another=boo
151
     * @param array $collapsedParams Parameters to collapse. ex. 'id' => ['boo'] to just 'id' => 'boo'. Exception thrown
152
     *                               if more than 1 value
153
     *
154
     * @return array such as ['id' => ['boo'], 'another' => ['wee', 'boo']]
155
     *
156
     * @throws InvalidArgumentException if $url was not a string
157
     * @throws Exception if more than one value in a $collapsedParams param
158
     */
159
    public static function getQueryParams(string $url, array $collapsedParams = []) : array
160
    {
161
        if (!is_string($url)) {
162
            throw new InvalidArgumentException('$url was not a string');
163
        }
164
165
        $queryString = parse_url($url, PHP_URL_QUERY);
166
        if (!is_string($queryString)) {
167
            return [];
168
        }
169
170
        $collapsedParams = array_flip($collapsedParams);
171
172
        $result = [];
173
        foreach (explode('&', $queryString) as $arg) {
174
            $name = $arg;
175
            $value = '';
176
            $nameAndValue = explode('=', $arg);
177
            if (isset($nameAndValue[1])) {
178
                list($name, $value) = $nameAndValue;
179
            }
180
181
            $name = rawurldecode($name);
182
            $value = rawurldecode($value);
183
            $collapsed = isset($collapsedParams[$name]);
184
185
            if (!array_key_exists($name, $result)) {
186
                if ($collapsed) {
187
                    $result[$name] = $value;
188
                    continue;
189
                }
190
191
                $result[$name] = [];
192
            }
193
194
            if ($collapsed) {
195
                throw new Exception("Parameter '{$name}' had more than one value but in \$collapsedParams");
196
            }
197
198
            $result[$name][] = $value;
199
        }
200
201
        return $result;
202
    }
203
204
    /**
205
     * Get an array of all url parameters.
206
     *
207
     * @param string $url The url to parse such as http://foo.com/bar/?single=boo&multi=wee&multi=boo
208
     * @param array $expectedArrayParams List of parameter names which are not collapsed.
209
     *
210
     * @return array such as ['single' => 'boo', 'multi' => ['wee', 'boo']] if 'multi' is given in $expectedArrayParams
211
     *
212
     * @throws InvalidArgumentException if $url was not a string
213
     * @throws Exception if a parameter is given as array but not included in the expected array argument
214
     */
215
    public static function getQueryParamsCollapsed(string $url, array $expectedArrayParams = []) : array
216
    {
217
        if (!is_string($url)) {
218
            throw new InvalidArgumentException('$url was not a string');
219
        }
220
221
        $queryString = parse_url($url, PHP_URL_QUERY);
222
        if (!is_string($queryString)) {
223
            return [];
224
        }
225
226
        $result = [];
227
        foreach (explode('&', $queryString) as $arg) {
228
            $name = $arg;
229
            $value = '';
230
            $nameAndValue = explode('=', $arg);
231
            if (isset($nameAndValue[1])) {
232
                list($name, $value) = $nameAndValue;
233
            }
234
235
            $name = rawurldecode($name);
236
            $value = rawurldecode($value);
237
238
            if (!array_key_exists($name, $result)) {
239
                $result[$name] = $value;
240
                continue;
241
            }
242
243
            if (!in_array($name, $expectedArrayParams)) {
244
                throw new Exception("Parameter '{$name}' is not expected to be an array, but array given");
245
            }
246
247
            if (!is_array($result[$name])) {
248
                $result[$name] = [$result[$name]];
249
            }
250
251
            $result[$name][] = $value;
252
        }
253
254
        return $result;
255
    }
256
257
    private static function setRequest($match, array $headers) : array
258
    {
259
        $headers['Request Method'] = trim($match[1]);
260
        $headers['Request Url'] = trim($match[2]);
261
        return $headers;
262
    }
263
264
    private static function setResponse($match, array $headers) : array
265
    {
266
        $headers['Response Code'] = (int)$match[2];
267
        $headers['Response Status'] = trim($match[3]);
268
        return $headers;
269
    }
270
271
    private static function isResponse(string $field, array &$match)
272
    {
273
        return preg_match('#HTTP/([\d.]+) +(\d{3}) +(.*)#', $field, $match);
274
    }
275
276
    private static function isRequest(string $field, array &$match)
277
    {
278
        return preg_match('#([A-Za-z]+) +([^ ]+) +HTTP/([\d.]+)#', $field, $match);
279
    }
280
}
281