Completed
Push — master ( daed8c...cf758d )
by Damian
08:03
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
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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