Completed
Push — director-middleware ( 059969...3cff84 )
by Sam
08:52
created

HTTPRequestBuilder::createFromVariables()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 38
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 20
nc 8
nop 2
dl 0
loc 38
rs 6.7272
c 0
b 0
f 0
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