1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Control; |
4
|
|
|
|
5
|
|
|
use SilverStripe\Core\Environment; |
6
|
|
|
|
7
|
|
|
class HTTPRequestBuilder |
8
|
|
|
{ |
9
|
|
|
/** |
10
|
|
|
* Create HTTPRequest instance from the current environment variables. |
11
|
|
|
* May throw errors if request is invalid. |
12
|
|
|
* |
13
|
|
|
* @throws HTTPResponse_Exception |
14
|
|
|
* @return HTTPRequest |
15
|
|
|
*/ |
16
|
|
|
public static function createFromEnvironment() |
17
|
|
|
{ |
18
|
|
|
// Clean and update live global variables |
19
|
|
|
$variables = static::cleanEnvironment(Environment::getVariables()); |
20
|
|
|
Environment::setVariables($variables); // Currently necessary for SSViewer, etc to work |
21
|
|
|
|
22
|
|
|
// Health-check prior to creating environment |
23
|
|
|
return static::createFromVariables($variables, @file_get_contents('php://input')); |
24
|
|
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Build HTTPRequest from given variables |
28
|
|
|
* |
29
|
|
|
* @param array $variables |
30
|
|
|
* @param string $input Request body |
31
|
|
|
* @return HTTPRequest |
32
|
|
|
*/ |
33
|
|
|
public static function createFromVariables(array $variables, $input) |
34
|
|
|
{ |
35
|
|
|
// Strip `url` out of querystring |
36
|
|
|
$url = $variables['_GET']['url']; |
37
|
|
|
unset($variables['_GET']['url']); |
38
|
|
|
|
39
|
|
|
// Build request |
40
|
|
|
$request = new HTTPRequest( |
41
|
|
|
$variables['_SERVER']['REQUEST_METHOD'], |
42
|
|
|
$url, |
43
|
|
|
$variables['_GET'], |
44
|
|
|
$variables['_POST'], |
45
|
|
|
$input |
46
|
|
|
); |
47
|
|
|
|
48
|
|
|
// Set the scheme to HTTPS if needed |
49
|
|
|
if ((!empty($variables['_SERVER']['HTTPS']) && $variables['_SERVER']['HTTPS'] != 'off') |
50
|
|
|
|| isset($variables['_SERVER']['SSL'])) { |
51
|
|
|
$request->setScheme('https'); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
// Set the client IP |
55
|
|
|
if (!empty($variables['_SERVER']['REMOTE_ADDR'])) { |
56
|
|
|
$request->setIP($variables['_SERVER']['REMOTE_ADDR']); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
// Add headers |
60
|
|
|
$headers = static::extractRequestHeaders($variables['_SERVER']); |
61
|
|
|
foreach ($headers as $header => $value) { |
62
|
|
|
$request->addHeader($header, $value); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
// Initiate an empty session - doesn't initialize an actual PHP session (see HTTPApplication) |
66
|
|
|
$session = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null); |
67
|
|
|
$request->setSession($session); |
68
|
|
|
|
69
|
|
|
return $request; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Takes a $_SERVER data array and extracts HTTP request headers. |
74
|
|
|
* |
75
|
|
|
* @param array $server |
76
|
|
|
* |
77
|
|
|
* @return array |
78
|
|
|
*/ |
79
|
|
|
public static function extractRequestHeaders(array $server) |
80
|
|
|
{ |
81
|
|
|
$headers = array(); |
82
|
|
|
foreach ($server as $key => $value) { |
83
|
|
|
if (substr($key, 0, 5) == 'HTTP_') { |
84
|
|
|
$key = substr($key, 5); |
85
|
|
|
$key = strtolower(str_replace('_', ' ', $key)); |
86
|
|
|
$key = str_replace(' ', '-', ucwords($key)); |
87
|
|
|
$headers[$key] = $value; |
88
|
|
|
} |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
if (isset($server['CONTENT_TYPE'])) { |
92
|
|
|
$headers['Content-Type'] = $server['CONTENT_TYPE']; |
93
|
|
|
} |
94
|
|
|
if (isset($server['CONTENT_LENGTH'])) { |
95
|
|
|
$headers['Content-Length'] = $server['CONTENT_LENGTH']; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
return $headers; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Clean up HTTP global vars for $_GET / $_REQUEST prior to bootstrapping |
103
|
|
|
* Will also populate the $_GET['url'] var safely |
104
|
|
|
* |
105
|
|
|
* @param array $variables |
106
|
|
|
* @return array Cleaned variables |
107
|
|
|
*/ |
108
|
|
|
public static function cleanEnvironment(array $variables) |
109
|
|
|
{ |
110
|
|
|
// IIS will sometimes generate this. |
111
|
|
|
if (!empty($variables['_SERVER']['HTTP_X_ORIGINAL_URL'])) { |
112
|
|
|
$variables['_SERVER']['REQUEST_URI'] = $variables['_SERVER']['HTTP_X_ORIGINAL_URL']; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
// Override REQUEST_METHOD |
116
|
|
|
if (isset($variables['_SERVER']['X-HTTP-Method-Override'])) { |
117
|
|
|
$variables['_SERVER']['REQUEST_METHOD'] = $variables['_SERVER']['X-HTTP-Method-Override']; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
// Prevent injection of url= querystring argument by prioritising any leading url argument |
121
|
|
|
if (isset($variables['_SERVER']['QUERY_STRING']) && |
122
|
|
|
preg_match('/^(?<url>url=[^&?]*)(?<query>.*[&?]url=.*)$/', $variables['_SERVER']['QUERY_STRING'], $results) |
123
|
|
|
) { |
124
|
|
|
$queryString = $results['query'].'&'.$results['url']; |
125
|
|
|
parse_str($queryString, $variables['_GET']); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// Decode url from REQUEST_URI if not passed via $_GET['url'] |
129
|
|
|
if (!isset($variables['_GET']['url'])) { |
130
|
|
|
$url = $variables['_SERVER']['REQUEST_URI']; |
131
|
|
|
|
132
|
|
|
// Querystring args need to be explicitly parsed |
133
|
|
|
if (strpos($url, '?') !== false) { |
134
|
|
|
list($url, $queryString) = explode('?', $url, 2); |
135
|
|
|
parse_str($queryString); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
// Ensure $_GET['url'] is set |
|
|
|
|
139
|
|
|
$variables['_GET']['url'] = urldecode($url); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
// Remove base folders from the URL if webroot is hosted in a subfolder |
143
|
|
|
if (substr(strtolower($variables['_GET']['url']), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) { |
144
|
|
|
$variables['_GET']['url'] = substr($variables['_GET']['url'], strlen(BASE_URL)); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// Merge $_FILES into $_POST |
148
|
|
|
$variables['_POST'] = array_merge((array)$variables['_POST'], (array)$variables['_FILES']); |
149
|
|
|
|
150
|
|
|
// Merge $_POST, $_GET, and $_COOKIE into $_REQUEST |
|
|
|
|
151
|
|
|
$variables['_REQUEST'] = array_merge( |
152
|
|
|
(array)$variables['_GET'], |
153
|
|
|
(array)$variables['_POST'], |
154
|
|
|
(array)$variables['_COOKIE'] |
155
|
|
|
); |
156
|
|
|
|
157
|
|
|
return $variables; |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
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.