1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the TYPO3 CMS project. |
5
|
|
|
* |
6
|
|
|
* It is free software; you can redistribute it and/or modify it under |
7
|
|
|
* the terms of the GNU General Public License, either version 2 |
8
|
|
|
* of the License, or any later version. |
9
|
|
|
* |
10
|
|
|
* For the full copyright and license information, please read the |
11
|
|
|
* LICENSE.txt file that was distributed with this source code. |
12
|
|
|
* |
13
|
|
|
* The TYPO3 project - inspiring people to share! |
14
|
|
|
*/ |
15
|
|
|
|
16
|
|
|
namespace TYPO3\CMS\Core\Utility; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* HTTP Utility class |
20
|
|
|
*/ |
21
|
|
|
class HttpUtility |
22
|
|
|
{ |
23
|
|
|
// HTTP Headers, see https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml |
24
|
|
|
// INFORMATIONAL CODES |
25
|
|
|
const HTTP_STATUS_100 = 'HTTP/1.1 100 Continue'; |
26
|
|
|
const HTTP_STATUS_101 = 'HTTP/1.1 101 Switching Protocols'; |
27
|
|
|
const HTTP_STATUS_102 = 'HTTP/1.1 102 Processing'; |
28
|
|
|
const HTTP_STATUS_103 = 'HTTP/1.1 103 Early Hints'; |
29
|
|
|
// SUCCESS CODES |
30
|
|
|
const HTTP_STATUS_200 = 'HTTP/1.1 200 OK'; |
31
|
|
|
const HTTP_STATUS_201 = 'HTTP/1.1 201 Created'; |
32
|
|
|
const HTTP_STATUS_202 = 'HTTP/1.1 202 Accepted'; |
33
|
|
|
const HTTP_STATUS_203 = 'HTTP/1.1 203 Non-Authoritative Information'; |
34
|
|
|
const HTTP_STATUS_204 = 'HTTP/1.1 204 No Content'; |
35
|
|
|
const HTTP_STATUS_205 = 'HTTP/1.1 205 Reset Content'; |
36
|
|
|
const HTTP_STATUS_206 = 'HTTP/1.1 206 Partial Content'; |
37
|
|
|
const HTTP_STATUS_207 = 'HTTP/1.1 207 Multi-status'; |
38
|
|
|
const HTTP_STATUS_208 = 'HTTP/1.1 208 Already Reported'; |
39
|
|
|
const HTTP_STATUS_226 = 'HTTP/1.1 226 IM Used'; |
40
|
|
|
// REDIRECTION CODES |
41
|
|
|
const HTTP_STATUS_300 = 'HTTP/1.1 300 Multiple Choices'; |
42
|
|
|
const HTTP_STATUS_301 = 'HTTP/1.1 301 Moved Permanently'; |
43
|
|
|
const HTTP_STATUS_302 = 'HTTP/1.1 302 Found'; |
44
|
|
|
const HTTP_STATUS_303 = 'HTTP/1.1 303 See Other'; |
45
|
|
|
const HTTP_STATUS_304 = 'HTTP/1.1 304 Not Modified'; |
46
|
|
|
const HTTP_STATUS_305 = 'HTTP/1.1 305 Use Proxy'; |
47
|
|
|
const HTTP_STATUS_306 = 'HTTP/1.1 306 Switch Proxy'; // Deprecated |
48
|
|
|
const HTTP_STATUS_307 = 'HTTP/1.1 307 Temporary Redirect'; |
49
|
|
|
const HTTP_STATUS_308 = 'HTTP/1.1 308 Permanent Redirect'; |
50
|
|
|
// CLIENT ERROR |
51
|
|
|
const HTTP_STATUS_400 = 'HTTP/1.1 400 Bad Request'; |
52
|
|
|
const HTTP_STATUS_401 = 'HTTP/1.1 401 Unauthorized'; |
53
|
|
|
const HTTP_STATUS_402 = 'HTTP/1.1 402 Payment Required'; |
54
|
|
|
const HTTP_STATUS_403 = 'HTTP/1.1 403 Forbidden'; |
55
|
|
|
const HTTP_STATUS_404 = 'HTTP/1.1 404 Not Found'; |
56
|
|
|
const HTTP_STATUS_405 = 'HTTP/1.1 405 Method Not Allowed'; |
57
|
|
|
const HTTP_STATUS_406 = 'HTTP/1.1 406 Not Acceptable'; |
58
|
|
|
const HTTP_STATUS_407 = 'HTTP/1.1 407 Proxy Authentication Required'; |
59
|
|
|
const HTTP_STATUS_408 = 'HTTP/1.1 408 Request Timeout'; |
60
|
|
|
const HTTP_STATUS_409 = 'HTTP/1.1 409 Conflict'; |
61
|
|
|
const HTTP_STATUS_410 = 'HTTP/1.1 410 Gone'; |
62
|
|
|
const HTTP_STATUS_411 = 'HTTP/1.1 411 Length Required'; |
63
|
|
|
const HTTP_STATUS_412 = 'HTTP/1.1 412 Precondition Failed'; |
64
|
|
|
const HTTP_STATUS_413 = 'HTTP/1.1 413 Request Entity Too Large'; |
65
|
|
|
const HTTP_STATUS_414 = 'HTTP/1.1 414 URI Too Long'; |
66
|
|
|
const HTTP_STATUS_415 = 'HTTP/1.1 415 Unsupported Media Type'; |
67
|
|
|
const HTTP_STATUS_416 = 'HTTP/1.1 416 Requested range not satisfiable'; |
68
|
|
|
const HTTP_STATUS_417 = 'HTTP/1.1 417 Expectation Failed'; |
69
|
|
|
const HTTP_STATUS_418 = 'HTTP/1.1 418 I\'m a teapot'; |
70
|
|
|
const HTTP_STATUS_422 = 'HTTP/1.1 422 Unprocessable Entity'; |
71
|
|
|
const HTTP_STATUS_423 = 'HTTP/1.1 423 Locked'; |
72
|
|
|
const HTTP_STATUS_424 = 'HTTP/1.1 424 Failed Dependency'; |
73
|
|
|
const HTTP_STATUS_425 = 'HTTP/1.1 425 Unordered Collection'; |
74
|
|
|
const HTTP_STATUS_426 = 'HTTP/1.1 426 Upgrade Required'; |
75
|
|
|
const HTTP_STATUS_428 = 'HTTP/1.1 428 Precondition Required'; |
76
|
|
|
const HTTP_STATUS_429 = 'HTTP/1.1 429 Too Many Requests'; |
77
|
|
|
const HTTP_STATUS_431 = 'HTTP/1.1 431 Request Header Fields Too Large'; |
78
|
|
|
const HTTP_STATUS_451 = 'HTTP/1.1 451 Unavailable For Legal Reasons'; |
79
|
|
|
// SERVER ERROR |
80
|
|
|
const HTTP_STATUS_500 = 'HTTP/1.1 500 Internal Server Error'; |
81
|
|
|
const HTTP_STATUS_501 = 'HTTP/1.1 501 Not Implemented'; |
82
|
|
|
const HTTP_STATUS_502 = 'HTTP/1.1 502 Bad Gateway'; |
83
|
|
|
const HTTP_STATUS_503 = 'HTTP/1.1 503 Service Unavailable'; |
84
|
|
|
const HTTP_STATUS_504 = 'HTTP/1.1 504 Gateway Time-out'; |
85
|
|
|
const HTTP_STATUS_505 = 'HTTP/1.1 505 Version not Supported'; |
86
|
|
|
const HTTP_STATUS_506 = 'HTTP/1.1 506 Variant Also Negotiates'; |
87
|
|
|
const HTTP_STATUS_507 = 'HTTP/1.1 507 Insufficient Storage'; |
88
|
|
|
const HTTP_STATUS_508 = 'HTTP/1.1 508 Loop Detected'; |
89
|
|
|
const HTTP_STATUS_509 = 'HTTP/1.1 509 Bandwidth Limit Exceeded'; |
90
|
|
|
const HTTP_STATUS_511 = 'HTTP/1.1 511 Network Authentication Required'; |
91
|
|
|
// URL Schemes |
92
|
|
|
const SCHEME_HTTP = 1; |
93
|
|
|
const SCHEME_HTTPS = 2; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Sends a redirect header response and exits. Additionally the URL is |
97
|
|
|
* checked and if needed corrected to match the format required for a |
98
|
|
|
* Location redirect header. By default the HTTP status code sent is |
99
|
|
|
* a 'HTTP/1.1 303 See Other'. |
100
|
|
|
* |
101
|
|
|
* @param string $url The target URL to redirect to |
102
|
|
|
* @param string $httpStatus An optional HTTP status header. Default is 'HTTP/1.1 303 See Other' |
103
|
|
|
* @deprecated since v11, will be removed in v12. |
104
|
|
|
*/ |
105
|
|
|
public static function redirect($url, $httpStatus = self::HTTP_STATUS_303) |
106
|
|
|
{ |
107
|
|
|
// Deprecation logged by setResponseCode() |
108
|
|
|
self::setResponseCode($httpStatus); |
109
|
|
|
header('Location: ' . GeneralUtility::locationHeaderUrl($url)); |
110
|
|
|
die; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Set a specific response code like 404. |
115
|
|
|
* |
116
|
|
|
* @param string $httpStatus One of the HTTP_STATUS_* class class constants, default to self::HTTP_STATUS_303 |
117
|
|
|
* @deprecated since v11, will be removed in v12. |
118
|
|
|
*/ |
119
|
|
|
public static function setResponseCode($httpStatus = self::HTTP_STATUS_303) |
120
|
|
|
{ |
121
|
|
|
trigger_error( |
122
|
|
|
'All methods in ' . __CLASS__ . ', manipulationg HTTP headers, are deprecated and will be removed in v12.', |
123
|
|
|
E_USER_DEPRECATED |
124
|
|
|
); |
125
|
|
|
|
126
|
|
|
header($httpStatus); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Set a specific response code and exit script execution. |
131
|
|
|
* |
132
|
|
|
* @param string $httpStatus One of the HTTP_STATUS_* class class constants, default to self::HTTP_STATUS_303 |
133
|
|
|
* @deprecated since v11, will be removed in v12. |
134
|
|
|
*/ |
135
|
|
|
public static function setResponseCodeAndExit($httpStatus = self::HTTP_STATUS_303) |
136
|
|
|
{ |
137
|
|
|
// Deprecation logged by setResponseCode() |
138
|
|
|
self::setResponseCode($httpStatus); |
139
|
|
|
die; |
|
|
|
|
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Builds a URL string from an array with the URL parts, as e.g. output by parse_url(). |
144
|
|
|
* |
145
|
|
|
* @param array $urlParts |
146
|
|
|
* @return string |
147
|
|
|
* @see http://www.php.net/parse_url |
148
|
|
|
*/ |
149
|
|
|
public static function buildUrl(array $urlParts) |
150
|
|
|
{ |
151
|
|
|
return (isset($urlParts['scheme']) ? $urlParts['scheme'] . '://' : '') . |
152
|
|
|
(isset($urlParts['user']) ? $urlParts['user'] . |
153
|
|
|
(isset($urlParts['pass']) ? ':' . $urlParts['pass'] : '') . '@' : '') . |
154
|
|
|
($urlParts['host'] ?? '') . |
155
|
|
|
(isset($urlParts['port']) ? ':' . $urlParts['port'] : '') . |
156
|
|
|
($urlParts['path'] ?? '') . |
157
|
|
|
(isset($urlParts['query']) ? '?' . $urlParts['query'] : '') . |
158
|
|
|
(isset($urlParts['fragment']) ? '#' . $urlParts['fragment'] : ''); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Implodes a multidimensional array of query parameters to a string of GET parameters (eg. param[key][key2]=value2¶m[key][key3]=value3) |
163
|
|
|
* and properly encodes parameter names as well as values. Spaces are encoded as %20 |
164
|
|
|
* |
165
|
|
|
* @param array $parameters The (multidimensional) array of query parameters with values |
166
|
|
|
* @param string $prependCharacter If the created query string is not empty, prepend this character "?" or "&" else no prepend |
167
|
|
|
* @param bool $skipEmptyParameters If true, empty parameters (blank string, empty array, null) are removed. |
168
|
|
|
* @return string Imploded result, for example param[key][key2]=value2¶m[key][key3]=value3 |
169
|
|
|
* @see explodeUrl2Array() |
170
|
|
|
*/ |
171
|
|
|
public static function buildQueryString(array $parameters, string $prependCharacter = '', bool $skipEmptyParameters = false): string |
172
|
|
|
{ |
173
|
|
|
if (empty($parameters)) { |
174
|
|
|
return ''; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
if ($skipEmptyParameters) { |
178
|
|
|
// This callback filters empty strings, array and null but keeps zero integers |
179
|
|
|
$parameters = ArrayUtility::filterRecursive( |
180
|
|
|
$parameters, |
181
|
|
|
function ($item) { |
182
|
|
|
return $item !== '' && $item !== [] && $item !== null; |
183
|
|
|
} |
184
|
|
|
); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
$queryString = http_build_query($parameters, '', '&', PHP_QUERY_RFC3986); |
188
|
|
|
$prependCharacter = $prependCharacter === '?' || $prependCharacter === '&' ? $prependCharacter : ''; |
189
|
|
|
|
190
|
|
|
return $queryString && $prependCharacter ? $prependCharacter . $queryString : $queryString; |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.