Passed
Pull Request — 4 (#10222)
by Steve
07:01
created

HTTPRequestBuilder::extractRequestHeaders()   C

Complexity

Conditions 12
Paths 108

Size

Total Lines 41
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 24
nc 108
nop 1
dl 0
loc 41
rs 6.9
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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'));
0 ignored issues
show
Bug introduced by
It seems like @file_get_contents('php://input') can also be of type false; however, parameter $input of SilverStripe\Control\HTT...::createFromVariables() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

23
        return static::createFromVariables($variables, /** @scrutinizer ignore-type */ @file_get_contents('php://input'));
Loading history...
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((string) $url), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) {
43
                $url = substr((string) $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 = [];
90
        foreach ($server as $key => $value) {
91
            if (substr((string) $key, 0, 5) == 'HTTP_') {
92
                $key = substr((string) $key, 5);
93
                $key = strtolower((string) str_replace('_', ' ', $key ?: ''));
94
                $key = str_replace(' ', '-', ucwords((string) $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', (string) $authHeader, $matches)) {
122
            list($name, $password) = explode(':', (string) 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
        // Merge $_FILES into $_POST
139
        $variables['_POST'] = array_merge((array)$variables['_POST'], (array)$variables['_FILES']);
140
141
        // Merge $_POST, $_GET, and $_COOKIE into $_REQUEST
142
        $variables['_REQUEST'] = array_merge(
143
            (array)$variables['_GET'],
144
            (array)$variables['_POST'],
145
            (array)$variables['_COOKIE']
146
        );
147
148
        return $variables;
149
    }
150
}
151