Passed
Pull Request — master (#303)
by Arman
02:44
created

HttpRequest::create()   C

Complexity

Conditions 9
Paths 256

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 18
c 0
b 0
f 0
nc 256
nop 4
dl 0
loc 34
rs 6.5222

2 Methods

Rating   Name   Duplication   Size   Complexity  
A HttpRequest::getMethod() 0 3 1
A HttpRequest::setUploadedFiles() 0 5 1
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.8
13
 */
14
15
namespace Quantum\Http\Request;
16
17
use Quantum\Config\Exceptions\ConfigException;
18
use Quantum\Http\Exceptions\HttpException;
19
use Quantum\App\Exceptions\BaseException;
20
use Quantum\Http\Traits\Request\RawInput;
21
use Quantum\Http\Traits\Request\Internal;
22
use Quantum\Http\Traits\Request\Header;
23
use Quantum\Http\Traits\Request\Params;
24
use Quantum\Di\Exceptions\DiException;
25
use Quantum\Http\Traits\Request\Query;
26
use Quantum\Http\Traits\Request\Body;
27
use Quantum\Http\Traits\Request\File;
28
use Quantum\Http\Traits\Request\Url;
29
use Quantum\Libraries\Csrf\Csrf;
30
use Quantum\Environment\Server;
31
use ReflectionException;
32
33
/**
34
 * Class HttpRequest
35
 * @package Quantum\Http
36
 */
37
abstract class HttpRequest
38
{
39
40
    use Header;
41
    use Body;
42
    use Url;
43
    use Query;
44
    use Params;
45
    use File;
46
    use RawInput;
47
    use Internal;
48
49
    /**
50
     * Available methods
51
     */
52
    const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
53
54
    /**
55
     * Default port for HTTP
56
     */
57
    const DEFAULT_HTTP_PORT = 80;
58
59
    /**
60
     * Default port for HTTPS
61
     */
62
    const DEFAULT_HTTPS_PORT = 443;
63
64
    /**
65
     * Request method
66
     * @var string
67
     */
68
    private static $__method = null;
69
70
    /**
71
     * @var Server
72
     */
73
    protected static $server;
74
75
    /**
76
     * @var bool
77
     */
78
    private static $initialized = false;
79
80
    /**
81
     * Initializes the request static properties using the server instance.
82
     * @param Server $server
83
     * @throws BaseException
84
     * @throws ConfigException
85
     * @throws DiException
86
     * @throws ReflectionException
87
     */
88
    public static function init(Server $server)
89
    {
90
        if (self::$initialized) {
91
            return;
92
        }
93
94
        self::flush();
95
96
        self::$server = $server;
97
98
        self::setServerInfo();
99
        self::setContentType();
100
        self::setRequestHeaders();
101
102
        list('params' => $rawInputParams, 'files' => $rawInputFiles) = self::getRawInputParams();
103
104
        self::setRequestParams($rawInputParams);
105
        self::setUploadedFiles($rawInputFiles);
106
107
        self::$initialized = true;
108
    }
109
110
111
    /**
112
     * Flushes the request header , body and files
113
     */
114
    public static function flush()
115
    {
116
        self::$__headers = [];
117
        self::$__request = [];
118
        self::$__files = [];
119
        self::$__protocol = null;
120
        self::$__host = null;
121
        self::$__port = null;
122
        self::$__uri = null;
123
        self::$__query = null;
124
125
        self::$initialized = false;
126
    }
127
128
    /**
129
     * Sets the merged request parameters
130
     * @param array $params
131
     */
132
    public static function setRequestParams(array $params)
133
    {
134
        self::$__request = array_merge(
135
            self::getParams(),
136
            self::postParams(),
137
            self::jsonPayloadParams(),
138
            self::urlEncodedParams(),
139
            $params
140
        );
141
    }
142
143
    /**
144
     * Sets the uploaded files array merging handled $_FILES and parsed files
145
     * @param array $rawInputFiles
146
     * @throws BaseException
147
     */
148
    public static function setUploadedFiles(array $files)
149
    {
150
        self::$__files = array_merge(
151
            self::handleFiles($_FILES),
152
            $files
153
        );
154
    }
155
156
    /**
157
     * Gets the request method
158
     * @return string|null
159
     */
160
    public static function getMethod(): ?string
161
    {
162
        return self::$__method;
163
    }
164
165
    /**
166
     * Sets the request method
167
     * @param string $method
168
     * @throws HttpException
169
     */
170
    public static function setMethod(string $method)
171
    {
172
        if (!in_array(strtoupper($method), self::METHODS)) {
173
            throw HttpException::methodNotAvailable($method);
174
        }
175
176
        self::$__method = $method;
177
    }
178
179
    /**
180
     * Checks if the current method matches the given method
181
     * @param string $method
182
     * @return bool
183
     */
184
    public static function isMethod(string $method): bool
185
    {
186
        return strcasecmp($method, self::$__method) == 0;
187
    }
188
189
    /**
190
     * Gets Cross Site Request Forgery Token
191
     * @return string|null
192
     */
193
    public static function getCsrfToken(): ?string
194
    {
195
        $csrfToken = null;
196
197
        if (self::has(Csrf::TOKEN_KEY)) {
198
            $csrfToken = (string)self::get(Csrf::TOKEN_KEY);
199
        } elseif (self::hasHeader('X-' . Csrf::TOKEN_KEY)) {
200
            $csrfToken = self::getHeader('X-' . Csrf::TOKEN_KEY);
201
        }
202
203
        return $csrfToken;
204
    }
205
206
    /**
207
     * Gets the base url
208
     * @param bool $withModulePrefix
209
     * @return string
210
     */
211
    public static function getBaseUrl(bool $withModulePrefix = false): string
212
    {
213
        $baseUrl = config()->get('base_url');
214
215
        $prefix = route_prefix();
216
        $modulePrefix = ($withModulePrefix && !empty($prefix)) ? '/' . $prefix : '';
217
218
        if ($baseUrl) {
219
            return $baseUrl . $modulePrefix;
220
        }
221
222
        return self::getHostPrefix() . $modulePrefix;
223
    }
224
225
    /**
226
     * Gets the current url
227
     * @return string
228
     */
229
    public static function getCurrentUrl(): string
230
    {
231
        $uri = self::getUri();
232
        $query = self::getQuery();
233
        $queryPart = $query ? '?' . $query : '';
234
235
        return self::getHostPrefix() . '/' . $uri . $queryPart;
236
    }
237
238
    /**
239
     * Gets the protocol, host, and optional port part of the URL.
240
     * @return string
241
     */
242
    private static function getHostPrefix(): string
243
    {
244
        $protocol = self::getProtocol();
245
        $host = self::getHost();
246
        $port = self::getPort();
247
248
        $defaultPort = $protocol === 'https' ? self::DEFAULT_HTTPS_PORT : self::DEFAULT_HTTP_PORT;
249
250
        $portPart = ($port && $port != $defaultPort) ? ':' . $port : '';
251
252
        return $protocol . '://' . $host . $portPart;
253
    }
254
255
    /**
256
     * Sets server data (method, protocol, host, port, uri, query).
257
     */
258
    private static function setServerInfo()
259
    {
260
        foreach (['method', 'protocol', 'host', 'port', 'uri', 'query'] as $name) {
261
            self::${"__{$name}"} = self::$server->$name();
262
        }
263
    }
264
265
    /**
266
     * Sets the normalized request content type.
267
     */
268
    private static function setContentType()
269
    {
270
        self::$__contentType = self::$server->contentType(true);
271
    }
272
273
    /**
274
     * Sets request headers, normalizing keys to lowercase.
275
     */
276
    private static function setRequestHeaders()
277
    {
278
        self::$__headers = array_change_key_case((array)getallheaders());
279
    }
280
}