GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( f28312...62896a )
by
unknown
02:33
created

Router   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 314
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 147
dl 0
loc 314
rs 8.5599
c 0
b 0
f 0
wmc 48

6 Methods

Rating   Name   Duplication   Size   Complexity  
D setController() 0 74 15
C parseAction() 0 50 14
A setAddresses() 0 5 1
A getAddresses() 0 3 1
F parseRequest() 0 88 16
A getUri() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Router often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Router, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This file is part of the O2System Framework package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author         Steeve Andrian Salim
9
 * @copyright      Copyright (c) Steeve Andrian Salim
10
 */
11
12
// ------------------------------------------------------------------------
13
14
namespace O2System\Kernel\Http;
15
16
// ------------------------------------------------------------------------
17
18
/**
19
 * Class Router
20
 * @package O2System\Kernel\Http
21
 */
22
class Router
23
{
24
    /**
25
     * Router::$uri
26
     *
27
     * @var Message\Uri
28
     */
29
    protected $uri;
30
31
    /**
32
     * Router::$addresses
33
     *
34
     * @var Router\Addresses
35
     */
36
    protected $addresses;
37
38
    // ------------------------------------------------------------------------
39
40
    /**
41
     * Router::getUri
42
     *
43
     * Gets routed Uri.
44
     *
45
     * @return Message\Uri
46
     */
47
    public function getUri()
48
    {
49
        return $this->uri;
50
    }
51
52
    // ------------------------------------------------------------------------
53
54
    /**
55
     * Router::getAddresses
56
     *
57
     * @return \O2System\Kernel\Http\Router\Addresses
58
     */
59
    public function getAddresses()
60
    {
61
        return $this->addresses;
62
    }
63
64
    // ------------------------------------------------------------------------
65
66
    /**
67
     * Router::setAddresses
68
     *
69
     * Sets router addresses.
70
     *
71
     * @param \O2System\Kernel\Http\Router\Addresses $addresses
72
     *
73
     * @return $this
74
     */
75
    public function setAddresses(Router\Addresses $addresses)
76
    {
77
        $this->addresses = $addresses;
78
79
        return $this;
80
    }
81
82
    // ------------------------------------------------------------------------
83
84
    /**
85
     * Router::parseRequest
86
     *
87
     * @param \O2System\Kernel\Http\Message\Uri|null $uri
88
     *
89
     * @return bool
90
     * @throws \ReflectionException
91
     */
92
    public function parseRequest(Message\Uri $uri = null)
93
    {
94
        $this->uri = is_null($uri) ? server_request()->getUri() : $uri;
95
        $uriSegments = $this->uri->getSegments()->getParts();
96
        $uriString = $this->uri->getSegments()->getString();
97
98
        if (count($uriSegments)) {
99
            if (strpos(end($uriSegments), '.json') !== false) {
100
                output()->setContentType('application/json');
0 ignored issues
show
Bug introduced by
The method setContentType() does not exist on O2System\Kernel\Cli\Output. ( Ignorable by Annotation )

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

100
                output()->/** @scrutinizer ignore-call */ setContentType('application/json');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
101
                $endSegment = str_replace('.json', '', end($uriSegments));
102
                array_pop($uriSegments);
103
                array_push($uriSegments, $endSegment);
104
                $this->uri = $this->uri->withSegments(new Message\Uri\Segments($uriSegments));
105
                $uriString = $this->uri->getSegments()->getString();
106
            } elseif (strpos(end($uriSegments), '.xml') !== false) {
107
                output()->setContentType('application/xml');
108
                $endSegment = str_replace('.xml', '', end($uriSegments));
109
                array_pop($uriSegments);
110
                array_push($uriSegments, $endSegment);
111
                $this->uri = $this->uri->withSegments(new Message\Uri\Segments($uriSegments));
112
                $uriString = $this->uri->getSegments()->getString();
113
            }
114
        } else {
115
            $uriPath = urldecode(
116
                parse_url($_SERVER[ 'REQUEST_URI' ], PHP_URL_PATH)
117
            );
118
119
            $uriPathParts = explode('public/', $uriPath);
120
            $uriPath = end($uriPathParts);
121
122
            if ($uriPath !== '/') {
123
                $uriString = $uriPath;
124
                $uriSegments = array_filter(explode('/', $uriString));
125
126
                $this->uri = $this->uri->withSegments(new Message\Uri\Segments($uriSegments));
0 ignored issues
show
Bug introduced by
$uriSegments of type array is incompatible with the type null|string expected by parameter $string of O2System\Kernel\Http\Mes...Segments::__construct(). ( Ignorable by Annotation )

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

126
                $this->uri = $this->uri->withSegments(new Message\Uri\Segments(/** @scrutinizer ignore-type */ $uriSegments));
Loading history...
127
                $uriString = $this->uri->getSegments()->getString();
128
            }
129
        }
130
131
        // Try to translate from uri string
132
        if (false !== ($action = $this->addresses->getTranslation($uriString))) {
133
            if ( ! $action->isValidHttpMethod(server_request()->getMethod()) && ! $action->isAnyHttpMethod()) {
134
                output()->sendError(405);
135
            } else {
136
                if (false !== ($parseSegments = $action->getParseUriString($uriString))) {
137
                    $uriSegments = $parseSegments;
138
                } else {
139
                    $uriSegments = [];
140
                }
141
142
                $this->uri = $this->uri->withSegments(new Message\Uri\Segments($uriSegments));
0 ignored issues
show
Bug introduced by
$uriSegments of type array|true is incompatible with the type null|string expected by parameter $string of O2System\Kernel\Http\Mes...Segments::__construct(). ( Ignorable by Annotation )

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

142
                $this->uri = $this->uri->withSegments(new Message\Uri\Segments(/** @scrutinizer ignore-type */ $uriSegments));
Loading history...
143
144
                $this->parseAction($action, $uriSegments);
0 ignored issues
show
Bug introduced by
It seems like $uriSegments can also be of type true; however, parameter $uriSegments of O2System\Kernel\Http\Router::parseAction() does only seem to accept array, 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

144
                $this->parseAction($action, /** @scrutinizer ignore-type */ $uriSegments);
Loading history...
Bug introduced by
It seems like $action can also be of type true; however, parameter $action of O2System\Kernel\Http\Router::parseAction() does only seem to accept O2System\Kernel\Http\Router\DataStructures\Action, 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

144
                $this->parseAction(/** @scrutinizer ignore-type */ $action, $uriSegments);
Loading history...
145
146
                if (services()->has('controller')) {
147
                    return true;
148
                }
149
            }
150
        }
151
152
        // Try to get route from controller & page
153
        if ($uriTotalSegments = count($uriSegments)) {
154
            $namespaces = [
155
                'App\Controllers\\',
156
                'App\Http\Controllers\\',
157
                'O2System\Reactor\Http\Controllers\\',
158
            ];
159
160
            for ($i = 0; $i <= $uriTotalSegments; $i++) {
161
                $uriRoutedSegments = array_slice($uriSegments, 0, ($uriTotalSegments - $i));
162
163
                foreach ($namespaces as $namespace) {
164
165
                    $controllerClassName = $namespace . implode('\\',
166
                            array_map('studlycase', $uriRoutedSegments));
167
168
                    if (class_exists($controllerClassName)) {
169
                        $uriSegments = array_diff($uriSegments, $uriRoutedSegments);
170
                        $this->setController(new Router\DataStructures\Controller($controllerClassName),
171
                            $uriSegments);
172
                        break;
173
                    }
174
                }
175
176
                // break the loop if the controller has been set
177
                if (services()->has('controller')) {
178
                    return true;
179
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
180
                }
181
            }
182
        }
183
184
        // Let's the app do the rest when there is no controller found
185
        // the app should redirect to PAGE 404
186
    }
187
188
    // ------------------------------------------------------------------------
189
190
    /**
191
     * Router::parseAction
192
     *
193
     * @param \O2System\Kernel\Http\Router\DataStructures\Action $action
194
     * @param array                                              $uriSegments
195
     *
196
     * @throws \ReflectionException
197
     */
198
    protected function parseAction(Router\DataStructures\Action $action, array $uriSegments = [])
199
    {
200
        $closure = $action->getClosure();
201
        if (empty($closure)) {
202
            output()->sendError(204);
203
        }
204
205
        if ($closure instanceof Controller) {
206
            $uriSegments = empty($uriSegments)
207
                ? $action->getClosureParameters()
208
                : $uriSegments;
209
            $this->setController(
210
                (new Router\DataStructures\Controller($closure))
0 ignored issues
show
Bug introduced by
$closure of type O2System\Kernel\Http\Controller is incompatible with the type string expected by parameter $filePath of O2System\Kernel\Http\Rou...ntroller::__construct(). ( Ignorable by Annotation )

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

210
                (new Router\DataStructures\Controller(/** @scrutinizer ignore-type */ $closure))
Loading history...
211
                    ->setRequestMethod('index'),
212
                $uriSegments
213
            );
214
        } elseif ($closure instanceof Router\DataStructures\Controller) {
215
            $this->setController($closure, $action->getClosureParameters());
216
        } elseif (is_array($closure)) {
217
            $uri = (new Message\Uri())
0 ignored issues
show
Unused Code introduced by
The assignment to $uri is dead and can be removed.
Loading history...
218
                ->withSegments(new Message\Uri\Segments(''))
219
                ->withQuery('');
220
            $this->parseRequest($this->uri->addSegments($closure));
221
        } else {
222
            if (class_exists($closure)) {
223
                $this->setController(
224
                    (new Router\DataStructures\Controller($closure))
225
                        ->setRequestMethod('index'),
226
                    $uriSegments
227
                );
228
            } elseif (preg_match("/([a-zA-Z0-9\\\]+)(@)([a-zA-Z0-9\\\]+)/", $closure, $matches)) {
229
                $this->setController(
230
                    (new Router\DataStructures\Controller($matches[ 1 ]))
231
                        ->setRequestMethod($matches[ 3 ]),
232
                    $uriSegments
233
                );
234
            } elseif (is_string($closure) && $closure !== '') {
235
                if (is_json($closure)) {
236
                    output()->setContentType('application/json');
237
                    output()->send($closure);
0 ignored issues
show
Bug introduced by
The method send() does not exist on O2System\Kernel\Cli\Output. ( Ignorable by Annotation )

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

237
                    output()->/** @scrutinizer ignore-call */ send($closure);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
238
                } else {
239
                    output()->send($closure);
240
                }
241
            } elseif (is_array($closure) || is_object($closure)) {
242
                output()->send($closure);
243
            } elseif (is_numeric($closure)) {
244
                output()->sendError($closure);
245
            } else {
246
                output()->sendError(204);
247
                exit(EXIT_ERROR);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
248
            }
249
        }
250
    }
251
252
    // ------------------------------------------------------------------------
253
254
    /**
255
     * Router::setController
256
     *
257
     * @param \O2System\Kernel\Http\Router\DataStructures\Controller $controller
258
     * @param array                                                  $uriSegments
259
     *
260
     * @throws \ReflectionException
261
     */
262
    protected function setController(
263
        Router\DataStructures\Controller $controller,
264
        array $uriSegments = []
265
    ) {
266
        if ( ! $controller->isValid()) {
267
            output()->sendError(400);
268
        }
269
270
        // Add Controller PSR4 Namespace
271
        loader()->addNamespace($controller->getNamespaceName(), $controller->getFileInfo()->getPath());
0 ignored issues
show
Bug introduced by
The function loader was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

271
        /** @scrutinizer ignore-call */ 
272
        loader()->addNamespace($controller->getNamespaceName(), $controller->getFileInfo()->getPath());
Loading history...
272
273
        $controllerMethod = $controller->getRequestMethod();
274
        $controllerMethod = empty($controllerMethod) ? reset($uriSegments) : $controllerMethod;
275
        $controllerMethod = camelcase($controllerMethod);
276
277
        // Set default controller method to index
278
        if ( ! $controller->hasMethod($controllerMethod) &&
279
            ! $controller->hasMethod('route')
280
        ) {
281
            $controllerMethod = 'index';
282
        }
283
284
        // has route method, controller method set to index as default
285
        if (empty($controllerMethod)) {
286
            $controllerMethod = 'index';
287
        }
288
289
        if (camelcase(reset($uriSegments)) === $controllerMethod) {
290
            array_shift($uriSegments);
291
        }
292
293
        $controllerMethodParams = $uriSegments;
294
295
        if ($controller->hasMethod('route')) {
296
            $controller->setRequestMethod('route');
297
            $controller->setRequestMethodArgs([
298
                $controllerMethod,
299
                $controllerMethodParams,
300
            ]);
301
        } elseif ($controller->hasMethod($controllerMethod)) {
302
            $method = $controller->getMethod($controllerMethod);
303
304
            // Method doesn't need any parameters
305
            if ($method->getNumberOfParameters() == 0) {
306
                // But there is parameters requested
307
                if (count($controllerMethodParams)) {
308
                    output()->sendError(404);
309
                } else {
310
                    $controller->setRequestMethod($controllerMethod);
311
                }
312
            } else {
313
                $parameters = [];
314
315
                if (count($controllerMethodParams)) {
316
                    if (is_numeric(key($controllerMethodParams))) {
317
                        $parameters = $controllerMethodParams;
318
                    } else {
319
                        foreach ($method->getParameters() as $index => $parameter) {
320
                            if (isset($uriSegments[ $parameter->name ])) {
321
                                $parameters[ $index ] = $controllerMethodParams[ $parameter->name ];
322
                            } else {
323
                                $parameters[ $index ] = null;
324
                            }
325
                        }
326
                    }
327
                }
328
329
                $controller->setRequestMethod($controllerMethod);
330
                $controller->setRequestMethodArgs($parameters);
331
            }
332
        }
333
334
        // Set Controller
335
        services()->add($controller, 'controller');
336
    }
337
}