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
|
|
|
* @param string|null $url Provide specific url (relative to base) |
32
|
|
|
* @return HTTPRequest |
33
|
|
|
*/ |
34
|
|
|
public static function createFromVariables(array $variables, $input, $url = null) |
35
|
|
|
{ |
36
|
|
|
// Infer URL from REQUEST_URI unless explicitly provided |
37
|
|
|
if (!isset($url)) { |
38
|
|
|
// Remove query parameters (they're retained separately through $server['_GET'] |
39
|
|
|
$url = parse_url($variables['_SERVER']['REQUEST_URI'], PHP_URL_PATH); |
40
|
|
|
|
41
|
|
|
// Remove base folders from the URL if webroot is hosted in a subfolder |
42
|
|
|
if (substr(strtolower($url), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) { |
43
|
|
|
$url = substr($url, strlen(BASE_URL)); |
44
|
|
|
} |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
// Build request |
48
|
|
|
$request = new HTTPRequest( |
49
|
|
|
$variables['_SERVER']['REQUEST_METHOD'], |
50
|
|
|
$url, |
51
|
|
|
$variables['_GET'], |
52
|
|
|
$variables['_POST'], |
53
|
|
|
$input |
54
|
|
|
); |
55
|
|
|
|
56
|
|
|
// Set the scheme to HTTPS if needed |
57
|
|
|
if ((!empty($variables['_SERVER']['HTTPS']) && $variables['_SERVER']['HTTPS'] != 'off') |
58
|
|
|
|| isset($variables['_SERVER']['SSL'])) { |
59
|
|
|
$request->setScheme('https'); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
// Set the client IP |
63
|
|
|
if (!empty($variables['_SERVER']['REMOTE_ADDR'])) { |
64
|
|
|
$request->setIP($variables['_SERVER']['REMOTE_ADDR']); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
// Add headers |
68
|
|
|
$headers = static::extractRequestHeaders($variables['_SERVER']); |
69
|
|
|
foreach ($headers as $header => $value) { |
70
|
|
|
$request->addHeader($header, $value); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
// Initiate an empty session - doesn't initialize an actual PHP session (see HTTPApplication) |
74
|
|
|
$session = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null); |
75
|
|
|
$request->setSession($session); |
76
|
|
|
|
77
|
|
|
return $request; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Takes a $_SERVER data array and extracts HTTP request headers. |
82
|
|
|
* |
83
|
|
|
* @param array $server |
84
|
|
|
* |
85
|
|
|
* @return array |
86
|
|
|
*/ |
87
|
|
|
public static function extractRequestHeaders(array $server) |
88
|
|
|
{ |
89
|
|
|
$headers = array(); |
90
|
|
|
foreach ($server as $key => $value) { |
91
|
|
|
if (substr($key, 0, 5) == 'HTTP_') { |
92
|
|
|
$key = substr($key, 5); |
93
|
|
|
$key = strtolower(str_replace('_', ' ', $key)); |
94
|
|
|
$key = str_replace(' ', '-', ucwords($key)); |
95
|
|
|
$headers[$key] = $value; |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
if (isset($server['CONTENT_TYPE'])) { |
100
|
|
|
$headers['Content-Type'] = $server['CONTENT_TYPE']; |
101
|
|
|
} |
102
|
|
|
if (isset($server['CONTENT_LENGTH'])) { |
103
|
|
|
$headers['Content-Length'] = $server['CONTENT_LENGTH']; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
// Enable HTTP Basic authentication workaround for PHP running in CGI mode with Apache |
107
|
|
|
// Depending on server configuration the auth header may be in HTTP_AUTHORIZATION or |
108
|
|
|
// REDIRECT_HTTP_AUTHORIZATION |
109
|
|
|
$authHeader = null; |
110
|
|
|
if (isset($headers['Authorization'])) { |
111
|
|
|
$authHeader = $headers['Authorization']; |
112
|
|
|
} elseif (isset($server['REDIRECT_HTTP_AUTHORIZATION'])) { |
113
|
|
|
$authHeader = $server['REDIRECT_HTTP_AUTHORIZATION']; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
// Ensure basic auth is available via headers |
117
|
|
|
if (isset($server['PHP_AUTH_USER']) && isset($server['PHP_AUTH_PW'])) { |
118
|
|
|
// Shift PHP_AUTH_* into headers so they are available via request |
119
|
|
|
$headers['PHP_AUTH_USER'] = $server['PHP_AUTH_USER']; |
120
|
|
|
$headers['PHP_AUTH_PW'] = $server['PHP_AUTH_PW']; |
121
|
|
|
} elseif ($authHeader && preg_match('/Basic\s+(?<token>.*)$/i', $authHeader, $matches)) { |
122
|
|
|
list($name, $password) = explode(':', base64_decode($matches['token'])); |
123
|
|
|
$headers['PHP_AUTH_USER'] = $name; |
124
|
|
|
$headers['PHP_AUTH_PW'] = $password; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
return $headers; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Clean up HTTP global vars for $_GET / $_REQUEST prior to bootstrapping |
132
|
|
|
* |
133
|
|
|
* @param array $variables |
134
|
|
|
* @return array Cleaned variables |
135
|
|
|
*/ |
136
|
|
|
public static function cleanEnvironment(array $variables) |
137
|
|
|
{ |
138
|
|
|
// IIS will sometimes generate this. |
139
|
|
|
if (!empty($variables['_SERVER']['HTTP_X_ORIGINAL_URL'])) { |
140
|
|
|
$variables['_SERVER']['REQUEST_URI'] = $variables['_SERVER']['HTTP_X_ORIGINAL_URL']; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
// Override REQUEST_METHOD |
144
|
|
|
if (isset($variables['_SERVER']['X-HTTP-Method-Override'])) { |
145
|
|
|
$variables['_SERVER']['REQUEST_METHOD'] = $variables['_SERVER']['X-HTTP-Method-Override']; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
// Merge $_FILES into $_POST |
149
|
|
|
$variables['_POST'] = array_merge((array)$variables['_POST'], (array)$variables['_FILES']); |
150
|
|
|
|
151
|
|
|
// Merge $_POST, $_GET, and $_COOKIE into $_REQUEST |
|
|
|
|
152
|
|
|
$variables['_REQUEST'] = array_merge( |
153
|
|
|
(array)$variables['_GET'], |
154
|
|
|
(array)$variables['_POST'], |
155
|
|
|
(array)$variables['_COOKIE'] |
156
|
|
|
); |
157
|
|
|
|
158
|
|
|
return $variables; |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
|
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.