Completed
Pull Request — master (#7037)
by Damian
09:19
created

HTTPRequestBuilder::createFromEnvironment()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 9
rs 9.6666
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
        // Add headers
49
        $headers = static::extractRequestHeaders($variables['_SERVER']);
50
        foreach ($headers as $header => $value) {
51
            $request->addHeader($header, $value);
52
        }
53
54
        // Initiate an empty session - doesn't initialize an actual PHP session (see HTTPApplication)
55
        $session = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null);
56
        $request->setSession($session);
57
58
        return $request;
59
    }
60
61
    /**
62
     * Takes a $_SERVER data array and extracts HTTP request headers.
63
     *
64
     * @param array $server
65
     *
66
     * @return array
67
     */
68
    public static function extractRequestHeaders(array $server)
69
    {
70
        $headers = array();
71
        foreach ($server as $key => $value) {
72
            if (substr($key, 0, 5) == 'HTTP_') {
73
                $key = substr($key, 5);
74
                $key = strtolower(str_replace('_', ' ', $key));
75
                $key = str_replace(' ', '-', ucwords($key));
76
                $headers[$key] = $value;
77
            }
78
        }
79
80
        if (isset($server['CONTENT_TYPE'])) {
81
            $headers['Content-Type'] = $server['CONTENT_TYPE'];
82
        }
83
        if (isset($server['CONTENT_LENGTH'])) {
84
            $headers['Content-Length'] = $server['CONTENT_LENGTH'];
85
        }
86
87
        return $headers;
88
    }
89
90
    /**
91
     * Clean up HTTP global vars for $_GET / $_REQUEST prior to bootstrapping
92
     * Will also populate the $_GET['url'] var safely
93
     *
94
     * @param array $variables
95
     * @return array Cleaned variables
96
     */
97
    public static function cleanEnvironment(array $variables)
98
    {
99
        // IIS will sometimes generate this.
100
        if (!empty($variables['_SERVER']['HTTP_X_ORIGINAL_URL'])) {
101
            $variables['_SERVER']['REQUEST_URI'] = $variables['_SERVER']['HTTP_X_ORIGINAL_URL'];
102
        }
103
104
        // Override REQUEST_METHOD
105
        if (isset($variables['_SERVER']['X-HTTP-Method-Override'])) {
106
            $variables['_SERVER']['REQUEST_METHOD'] = $variables['_SERVER']['X-HTTP-Method-Override'];
107
        }
108
109
        // Prevent injection of url= querystring argument by prioritising any leading url argument
110
        if (isset($variables['_SERVER']['QUERY_STRING']) &&
111
            preg_match('/^(?<url>url=[^&?]*)(?<query>.*[&?]url=.*)$/', $variables['_SERVER']['QUERY_STRING'], $results)
112
        ) {
113
            $queryString = $results['query'].'&'.$results['url'];
114
            parse_str($queryString, $variables['_GET']);
115
        }
116
117
        // Decode url from REQUEST_URI if not passed via $_GET['url']
118
        if (!isset($variables['_GET']['url'])) {
119
            $url = $variables['_SERVER']['REQUEST_URI'];
120
121
            // Querystring args need to be explicitly parsed
122
            if (strpos($url, '?') !== false) {
123
                list($url, $queryString) = explode('?', $url, 2);
124
                parse_str($queryString);
125
            }
126
127
            // 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...
128
            $variables['_GET']['url'] = urldecode($url);
129
        }
130
131
        // Remove base folders from the URL if webroot is hosted in a subfolder
132
        if (substr(strtolower($variables['_GET']['url']), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) {
133
            $variables['_GET']['url'] = substr($variables['_GET']['url'], strlen(BASE_URL));
134
        }
135
136
        // Merge $_FILES into $_POST
137
        $variables['_POST'] = array_merge((array)$variables['_POST'], (array)$variables['_FILES']);
138
139
        // 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...
140
        $variables['_REQUEST'] = array_merge(
141
            (array)$variables['_GET'],
142
            (array)$variables['_POST'],
143
            (array)$variables['_COOKIE']
144
        );
145
146
        return $variables;
147
    }
148
}
149