1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Asymptix\web; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Http protocol functionality and other connected tools. |
7
|
|
|
* |
8
|
|
|
* @category Asymptix PHP Framework |
9
|
|
|
* @author Dmytro Zarezenko <[email protected]> |
10
|
|
|
* @copyright (c) 2009 - 2017, Dmytro Zarezenko |
11
|
|
|
* |
12
|
|
|
* @git https://github.com/Asymptix/Framework |
13
|
|
|
* @license http://opensource.org/licenses/MIT |
14
|
|
|
*/ |
15
|
|
|
class Http { |
16
|
|
|
|
17
|
|
|
const POST = "POST"; |
18
|
|
|
const GET = "GET"; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Redirect to the given url. |
22
|
|
|
* |
23
|
|
|
* @param string $url The URL to redirect to. |
24
|
|
|
* @param array<mixed> $params Associative array of query parameters. |
25
|
|
|
* @param bool $session Whether to append session information. |
26
|
|
|
*/ |
27
|
|
View Code Duplication |
public static function http_redirect($url, $params = [], $session = false) { |
|
|
|
|
28
|
|
|
$paramsString = ""; |
29
|
|
|
foreach ($params as $key => $value) { |
30
|
|
|
$paramsString.= "&" . $key . "=" . $value; |
31
|
|
|
} |
32
|
|
|
if ($session) { |
33
|
|
|
$paramsString.= "&" . session_name() . "=" . session_id(); |
34
|
|
|
} |
35
|
|
|
$paramsString = substr($paramsString, 1); |
36
|
|
|
if ($paramsString) { |
37
|
|
|
$paramsString = "?" . $paramsString; |
38
|
|
|
} |
39
|
|
|
header("Location: " . $url . $paramsString); |
40
|
|
|
exit(); |
|
|
|
|
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Perform HTTP redirect with saving POST params in session. |
45
|
|
|
* |
46
|
|
|
* @param string $url URL redirect to. |
47
|
|
|
* @param array<mixed> $postData List of post params to save. |
48
|
|
|
* @param bool $serialize Serialize transmitted POST data values or not. |
49
|
|
|
*/ |
50
|
|
|
public static function httpRedirect($url = "", $postData = [], $serialize = true) { |
|
|
|
|
51
|
|
|
if (preg_match("#^http[s]?://.+#", $url)) { // absolute url |
52
|
|
|
if (function_exists("http_redirect")) { |
53
|
|
|
http_redirect($url); |
54
|
|
|
} else { |
55
|
|
|
self::http_redirect($url); |
56
|
|
|
} |
57
|
|
|
} else { // same domain (relative url) |
58
|
|
|
if (!empty($postData)) { |
59
|
|
|
if (is_array($postData)) { |
60
|
|
|
if (!Session::exists('_post') || !is_array($_SESSION['_post'])) { |
61
|
|
|
Session::set('_post', []); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
foreach ($postData as $fieldName => $fieldValue) { |
65
|
|
|
Session::set("_post[{$fieldName}]", $serialize ? serialize($fieldValue) : $fieldValue); |
66
|
|
|
} |
67
|
|
|
} else { |
68
|
|
|
throw new HttpException("Wrong POST data."); |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
if (function_exists("http_redirect")) { |
72
|
|
|
http_redirect("http://" . $_SERVER['SERVER_NAME'] . "/" . $url); |
73
|
|
|
} else { |
74
|
|
|
self::http_redirect("http://" . $_SERVER['SERVER_NAME'] . "/" . $url); |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Perform HTTP redirect with saving POST params in session. |
81
|
|
|
* |
82
|
|
|
* @param string $url URL redirect to. |
83
|
|
|
* @param array<mixed> $postData List of post params to save. |
84
|
|
|
* @param bool $serialize Serialize transmitted POST data values or not. |
85
|
|
|
*/ |
86
|
|
|
public static function redirect($url = "", $postData = [], $serialize = true) { |
87
|
|
|
self::httpRedirect($url, $postData, $serialize); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Returns clients IP-address. |
92
|
|
|
* |
93
|
|
|
* @return string |
94
|
|
|
*/ |
95
|
|
|
public static function getIP() { |
|
|
|
|
96
|
|
|
if (!empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet |
97
|
|
|
return $_SERVER['HTTP_CLIENT_IP']; |
98
|
|
|
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy |
99
|
|
|
return $_SERVER['HTTP_X_FORWARDED_FOR']; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
return $_SERVER['REMOTE_ADDR']; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Returns HTTP referrer. |
107
|
|
|
* |
108
|
|
|
* @return string |
109
|
|
|
*/ |
110
|
|
|
public static function getReferrer() { |
|
|
|
|
111
|
|
|
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ""; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Gets the address that the provided URL redirects to, |
116
|
|
|
* or FALSE if there's no redirect. |
117
|
|
|
* |
118
|
|
|
* @param string $url URL. |
119
|
|
|
* |
120
|
|
|
* @return mixed String with redirect URL or FALSE if no redirect. |
121
|
|
|
* @throws HttpException |
122
|
|
|
*/ |
123
|
|
|
public static function getRedirectUrl($url) { |
124
|
|
|
$urlParts = @parse_url($url); |
125
|
|
|
if (!$urlParts) { |
126
|
|
|
return false; |
127
|
|
|
} |
128
|
|
|
if (!isset($urlParts['host'])) { //can't process relative URLs |
129
|
|
|
return false; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
if (!isset($urlParts['path'])) { |
133
|
|
|
$urlParts['path'] = '/'; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
$sock = fsockopen($urlParts['host'], (isset($urlParts['port']) ? (int) $urlParts['port'] : 80), $errno, $errstr, 30); |
137
|
|
|
if (!$sock) { |
138
|
|
|
throw new HttpException("$errstr ($errno)"); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
$request = "HEAD " . $urlParts['path'] . (isset($urlParts['query']) ? '?' . $urlParts['query'] : '') . " HTTP/1.1\r\n"; |
142
|
|
|
$request.= 'Host: ' . $urlParts['host'] . "\r\n"; |
143
|
|
|
$request.= "Connection: Close\r\n\r\n"; |
144
|
|
|
fwrite($sock, $request); |
145
|
|
|
$response = ''; |
146
|
|
|
while (!feof($sock)) { |
147
|
|
|
$response.= fread($sock, 8192); |
148
|
|
|
} |
149
|
|
|
fclose($sock); |
150
|
|
|
|
151
|
|
|
if (preg_match('/^Location: (.+?)$/m', $response, $matches)) { |
152
|
|
|
if (substr($matches[1], 0, 1) == "/") { |
153
|
|
|
return $urlParts['scheme'] . "://" . $urlParts['host'] . trim($matches[1]); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return trim($matches[1]); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
return false; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Follows and collects all redirects, in order, for the given URL. |
164
|
|
|
* |
165
|
|
|
* @param string $url |
166
|
|
|
* @return array |
167
|
|
|
*/ |
168
|
|
|
public static function getAllRedirects($url) { |
169
|
|
|
$redirects = []; |
170
|
|
|
while ($newurl = self::getRedirectUrl($url)) { |
171
|
|
|
if (in_array($newurl, $redirects)) { |
172
|
|
|
break; |
173
|
|
|
} |
174
|
|
|
$redirects[] = $newurl; |
175
|
|
|
$url = $newurl; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
return $redirects; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Gets the address that the URL ultimately leads to. |
183
|
|
|
* Returns $url itself if it isn't a redirect. |
184
|
|
|
* |
185
|
|
|
* @param string $url |
186
|
|
|
* @return string |
187
|
|
|
*/ |
188
|
|
|
public static function getFinalUrl($url) { |
189
|
|
|
$redirects = self::getAllRedirects($url); |
190
|
|
|
if (count($redirects) > 0) { |
191
|
|
|
return array_pop($redirects); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
return $url; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Executes CURL async request. |
199
|
|
|
* |
200
|
|
|
* @param string $url URL. |
201
|
|
|
* @param array $params List of request params. |
202
|
|
|
* @param string $type Type of the request (GET, POST, ...). |
203
|
|
|
* @param int $timeout Timeout in seconds. |
204
|
|
|
* |
205
|
|
|
* @return type |
206
|
|
|
*/ |
207
|
|
|
public static function curlRequestAsync($url, $params, $type = self::POST, $timeout = 30) { |
208
|
|
|
$postParams = []; |
209
|
|
|
foreach ($params as $key => &$val) { |
210
|
|
|
if (is_array($val)) { |
211
|
|
|
$val = implode(',', $val); |
212
|
|
|
} |
213
|
|
|
$postParams[] = $key . '=' . urlencode($val); |
214
|
|
|
} |
215
|
|
|
$postString = implode('&', $postParams); |
216
|
|
|
|
217
|
|
|
$parts = parse_url($url); |
218
|
|
|
|
219
|
|
|
$port = isset($parts['port']) ? (int)$parts['port'] : 80; |
220
|
|
|
|
221
|
|
|
$sock = fsockopen($parts['host'], $port, $errno, $errstr, $timeout); |
222
|
|
|
|
223
|
|
|
// Data goes in the path for a GET request |
224
|
|
|
if ($type == self::GET) { |
225
|
|
|
$parts['path'].= '?' . $postString; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
$request = "$type " . $parts['path'] . " HTTP/1.1\r\n"; |
229
|
|
|
$request.= "Host: " . $parts['host'] . "\r\n"; |
230
|
|
|
|
231
|
|
|
if ($type == self::POST) { |
232
|
|
|
$request.= "Content-Type: application/x-www-form-urlencoded\r\n"; |
233
|
|
|
$request.= "Content-Length: " . strlen($postString) . "\r\n"; |
234
|
|
|
} |
235
|
|
|
$request.= "Connection: Close\r\n"; |
236
|
|
|
$request.= "\r\n"; |
237
|
|
|
|
238
|
|
|
// Data goes in the request body for a POST request |
239
|
|
|
if ($type == self::POST && isset($postString)) { |
240
|
|
|
$request.= $postString; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
fwrite($sock, $request); |
244
|
|
|
|
245
|
|
|
$response = ""; |
246
|
|
|
while (!feof($sock) && $result = fgets($sock)) { |
247
|
|
|
$response.= $result; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
fclose($sock); |
251
|
|
|
|
252
|
|
|
list($respHeader, $respBody) = preg_split("/\R\R/", $response, 2); |
253
|
|
|
|
254
|
|
|
$headers = array_map(['self', "pair"], explode("\r\n", $respHeader)); |
255
|
|
|
$headerList = []; |
256
|
|
|
foreach ($headers as $value) { |
257
|
|
|
$headerList[$value['key']] = $value['value']; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
return [ |
261
|
|
|
'request' => $request, |
262
|
|
|
'response' => [ |
263
|
|
|
'header' => $respHeader, |
264
|
|
|
'headerList' => $headerList, |
265
|
|
|
'body' => trim(http_chunked_decode($respBody)) |
266
|
|
|
], |
267
|
|
|
'errno' => $errno, |
268
|
|
|
'errstr' => $errstr |
269
|
|
|
]; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Validates URL existence with cURL request. |
274
|
|
|
* |
275
|
|
|
* @param string $url URL. |
276
|
|
|
* |
277
|
|
|
* @return bool |
278
|
|
|
*/ |
279
|
|
|
public static function urlExists($url) { |
280
|
|
|
$ch = curl_init($url); |
281
|
|
|
|
282
|
|
|
curl_setopt($ch, CURLOPT_NOBODY, true); |
283
|
|
|
curl_setopt($ch, CURLOPT_FAILONERROR, true); |
284
|
|
|
|
285
|
|
|
$result = curl_exec($ch); |
286
|
|
|
curl_close($ch); |
287
|
|
|
|
288
|
|
|
return $result; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Force HTTP status code. |
293
|
|
|
* |
294
|
|
|
* @param int $code Status code. |
295
|
|
|
* @param string $path Include path if needed. |
296
|
|
|
* |
297
|
|
|
* @throws HttpException If invalid HTTP status provided. |
298
|
|
|
*/ |
299
|
|
|
public static function forceHttpStatus($code, $path = null) { |
300
|
|
|
switch ($code) { |
301
|
|
|
//TODO: implement other statuses |
302
|
|
|
case (404): |
303
|
|
|
header('HTTP/1.0 404 Not Found', true, 404); |
304
|
|
|
break; |
305
|
|
|
default: |
306
|
|
|
throw new HttpException("Invalid HTTP status code '" . $code . "'"); |
307
|
|
|
} |
308
|
|
|
if (!is_null($path)) { |
309
|
|
|
include($path); |
310
|
|
|
} |
311
|
|
|
exit(); |
|
|
|
|
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Magic methods: |
316
|
|
|
* force404($path = null) - force HTTP status codes. |
317
|
|
|
* |
318
|
|
|
* @param string $name The name of the method being called. |
319
|
|
|
* @param type $arguments Enumerated array containing the parameters passed |
320
|
|
|
* to the method. |
321
|
|
|
* @return mixed |
322
|
|
|
* |
323
|
|
|
* @throws HttpException If invalid method name provided. |
324
|
|
|
*/ |
325
|
|
|
public static function __callStatic($name, $arguments) { |
326
|
|
|
if (substr($name, 0, 5) === "force") { |
327
|
|
|
$code = (int)substr($name, 5); |
328
|
|
|
$path = isset($arguments[0]) ? $arguments[0] : null; |
329
|
|
|
|
330
|
|
|
return self::forceHttpStatus($code, $path); |
331
|
|
|
} |
332
|
|
|
throw new HttpException("Invalid HTTP class method '" . $name . "'"); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Service exception class. |
339
|
|
|
*/ |
340
|
|
|
class HttpException extends \Exception {} |
|
|
|
|
341
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.