Passed
Push — master ( d2743c...7dbeaf )
by Nícollas
01:44
created

Request::getParsedRoute()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 4
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace MinasRouter\Http;
4
5
class Request
6
{
7
    private $fullUrl;
8
9
    private $httpMethod;
10
11
    private $data = [];
12
13
    private $queryStrings;
14
15
    private $params;
16
17
    private $headers;
18
19
    public function __construct(
20
        String $fullUrl,
21
        String $route,
22
        array $routeParams
23
    ) {
24
        $this->fullUrl = $fullUrl . ($_SERVER['REQUEST_URI'] ?? '/');
25
26
        $this->httpMethod = $_SERVER['REQUEST_METHOD'] ?? '';
27
        $this->queryStrings = $_GET;
28
29
        if (isset($this->queryStrings["route"])) {
30
            unset($this->queryStrings["route"]);
31
        }
32
33
        $this->headers = $this->resolveHeaders();
34
35
        $this->resolveRouteData($route, $routeParams);
36
37
        if ($this->httpMethod != 'GET') {
38
            $this->setData();
39
        }
40
    }
41
42
    /**
43
     * Method responsible for bringing
44
     * all request headers.
45
     * 
46
     * @return array
47
     */
48
    protected function resolveHeaders()
49
    {
50
        $headers = [];
51
52
        foreach ($_SERVER as $name => $value) {
53
            if (substr($name, 0, 5) == 'HTTP_') {
54
                $index = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
55
                $headers[$index] = $value;
56
            }
57
        }
58
59
        return $headers;
60
    }
61
62
    /**
63
     * Method responsible for returning the
64
     * current route in parts.
65
     * 
66
     * @return array|string
67
     */
68
    protected function getParsedRoute(?String $data = null)
69
    {
70
        $parsedRoute = parse_url($this->fullUrl ?? "/");
71
72
        if (!is_array($parsedRoute)) {
0 ignored issues
show
introduced by
The condition is_array($parsedRoute) is always true.
Loading history...
73
            return null;
74
        }
75
76
        if (empty($data)) {
77
            return $parsedRoute;
78
        }
79
80
        return isset($parsedRoute[$data]) ? $parsedRoute[$data] : null;
81
    }
82
83
    /**
84
     * Method responsible for returning
85
     * the current route path.
86
     * 
87
     * @return string
88
     */
89
    public function path()
90
    {
91
        $path = $this->getParsedRoute("path");
92
93
        return $path ?? "/";
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path ?? '/' also could return the type array which is incompatible with the documented return type string.
Loading history...
94
    }
95
96
    /**
97
     * Method responsible for returning
98
     * the current route without query strings.
99
     * 
100
     * @return null|string
101
     */
102
    public function url()
103
    {
104
        $path = $this->getParsedRoute();
105
106
        if (!$path) return null;
107
108
        return "{$path['scheme']}://{$path['host']}{$this->path()}";
109
    }
110
111
    /**
112
     * Method responsible for returning
113
     * the full current route (with query strings).
114
     * 
115
     * @return null|string
116
     */
117
    public function fullUrl()
118
    {
119
        $query = $this->getParsedRoute("query");
120
121
        return "{$this->url()}?{$query}";
122
    }
123
124
    /**
125
     * Method responsible for returning the
126
     * route's dynamic parameters.
127
     * 
128
     * @return array
129
     */
130
    public function getParams()
131
    {
132
        return $this->params;
133
    }
134
135
    /**
136
     * Method responsible for returning one or all
137
     * data coming from the params query ($_GET).
138
     * 
139
     * @param null|string $name = null
140
     * @param null|string $asDefault = null
141
     * 
142
     * @return array|string
143
     */
144
    public function query(?String $name = null, ?String $asDefault = null)
145
    {
146
        if(!$name) {
147
            return $this->queryStrings;
148
        }
149
150
        if (isset($this->queryStrings[$name])) {
151
            return $this->queryStrings[$name];
152
        }
153
154
        return $asDefault;
155
    }
156
157
    /**
158
     * Returns a property that does not exist in the class,
159
     * usually they are indexes of the route array. Returns null
160
     * if this index/property does not exist.
161
     * 
162
     * @param string $data
163
     * 
164
     * @return string|null|array
165
     */
166
    public function __get(String $data)
167
    {
168
        if (isset($this->data[$data])) {
169
            return $this->data[$data];
170
        }
171
172
        if (isset($this->queryStrings[$data])) {
173
            return $this->queryStrings[$data];
174
        }
175
176
        if (isset($this->params[$data])) {
177
            return $this->params[$data];
178
        }
179
180
        return null;
181
    }
182
183
    /**
184
     * Method responsible for defining route data
185
     * 
186
     * @param string $route
187
     * @param array $routeParams
188
     * 
189
     * @return void
190
     */
191
    protected function resolveRouteData(String $route, array $routeParams): void
192
    {
193
        $params = $this->getParsedRoute("path");
194
195
        $diff = array_diff(explode('/', $params), explode('/', $route));
0 ignored issues
show
Bug introduced by
It seems like $params can also be of type array; however, parameter $string of explode() 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

195
        $diff = array_diff(explode('/', /** @scrutinizer ignore-type */ $params), explode('/', $route));
Loading history...
196
197
        $diff = array_values($diff);
198
199
        if (!empty($diff)) {
200
            foreach ($routeParams as $index => $param) {
201
                if (!isset($diff[$index])) return;
202
203
                if ($this->httpMethod != 'GET') {
204
                    $this->params[$param] = rawurldecode($diff[$index]);
205
                    continue;
206
                }
207
208
                $this->data[$param] = rawurldecode($diff[$index]);
209
            }
210
        }
211
212
        if (empty($this->data)) {
213
            $this->data = [];
214
        }
215
216
        if (empty($this->params)) {
217
            $this->params = [];
218
        }
219
    }
220
221
    /**
222
     * Method responsible for assigning and handling
223
     * the data coming from the web form.
224
     * 
225
     * @return void
226
     */
227
    protected function setData()
228
    {
229
        $enableFormSpoofing = ["PUT", "PATCH", "DELETE"];
230
231
        $post = filter_input_array(INPUT_POST, FILTER_DEFAULT);
232
233
        if (!empty($post['_method']) && in_array($post['_method'], $enableFormSpoofing)) {
234
            $this->httpMethod = $post['_method'];
235
            $this->data = $post;
236
237
            unset($this->data["_method"]);
238
            return;
239
        }
240
        if ($this->httpMethod == "POST") {
241
            $this->data = filter_input_array(INPUT_POST, FILTER_DEFAULT);
242
243
            unset($this->data["_method"]);
244
            return;
245
        }
246
247
        if (in_array($this->httpMethod, $enableFormSpoofing) && !empty($_SERVER['CONTENT_LENGTH'])) {
248
            parse_str(file_get_contents('php://input', false, null, 0, $_SERVER['CONTENT_LENGTH']), $putPatch);
249
            $this->data = $putPatch;
250
251
            unset($this->data["_method"]);
252
            return;
253
        }
254
255
        $this->data = [];
256
        return;
257
    }
258
259
    /**
260
     * Return the httpMethod.
261
     * 
262
     * @return string
263
     */
264
    public function getMethod()
265
    {
266
        return $this->httpMethod;
267
    }
268
269
    /**
270
     * Method responsible for checking if the current http method
271
     * is the expected.
272
     * 
273
     * @param string $expectedMethod
274
     * 
275
     * @return bool
276
     */
277
    public function isMethod(String $expectedMethod): bool
278
    {
279
        return $this->getMethod() === $expectedMethod;
280
    }
281
282
    /**
283
     * Method responsible for returning all request data.
284
     * 
285
     * @param null|string $except = null
286
     * 
287
     * @return array|null
288
     */
289
    public function all(?String $except = null)
290
    {
291
        if (!$except) {
292
            return $this->data;
293
        }
294
295
        $allWithExcession = $this->data;
296
        $except = explode(',', $except);
297
298
        $except = array_map(function ($excession) {
299
            return trim(rtrim($excession));
300
        }, $except);
301
302
        foreach ($except as $excession) {
303
            if (!isset($this->data[$excession])) return;
304
305
            unset($allWithExcession[$excession]);
306
        }
307
308
        return $allWithExcession;
309
    }
310
311
    /**
312
     * Method responsible for returning only
313
     * the data passed in parameter.
314
     * 
315
     * @param string $only
316
     * 
317
     * @return array|string
318
     */
319
    public function only(String $only)
0 ignored issues
show
Unused Code introduced by
The parameter $only is not used and could be removed. ( Ignorable by Annotation )

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

319
    public function only(/** @scrutinizer ignore-unused */ String $only)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
320
    {
321
        $result = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
322
    }
323
324
    /**
325
     * Method responsible for returning all header data.
326
     * 
327
     * @return array
328
     */
329
    public function getHeaders()
330
    {
331
        return $this->headers;
332
    }
333
334
    /**
335
     * Method responsible for returning one header data or default value.
336
     * 
337
     * @param string $header
338
     * @param null|string $asDefault
339
     * 
340
     * @return null|string
341
     */
342
    public function header(String $header, ?String $asDefault = null)
343
    {
344
        if (isset($this->headers[$header])) {
345
            return $this->headers[$header];
346
        }
347
348
        return $asDefault;
349
    }
350
351
    /**
352
     * Method responsible for checking if there is one
353
     * header in the request.
354
     * 
355
     * @param string $header
356
     * 
357
     * @return bool
358
     */
359
    public function hasHeader(String $header)
360
    {
361
        return (bool) $this->header($header);
362
    }
363
364
    /**
365
     * Method responsible for returning ip of client request.
366
     * 
367
     * @return string
368
     */
369
    public function ip()
370
    {
371
        if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
372
            if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') > 0) {
373
                $addr = explode(",", $_SERVER['HTTP_X_FORWARDED_FOR']);
374
                return trim($addr[0]);
375
            } else {
376
                return $_SERVER['HTTP_X_FORWARDED_FOR'];
377
            }
378
        } else {
379
            return $_SERVER['REMOTE_ADDR'];
380
        }
381
    }
382
383
    public function bearerToken()
384
    {
385
        $authorizationHeader = $this->header('Authorization');
386
387
        if(!$authorizationHeader) return null;
388
389
        if(preg_match("/^Bearer\s(.*)+$/", $authorizationHeader, $found)) {
390
            return $authorizationHeader;
391
        }
392
393
        return null;
394
    }
395
}
396