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.
Completed
Push — master ( 859d66...4f628a )
by やかみ
01:26
created

src/Http/Route.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Kotori.php
4
 *
5
 * A Tiny Model-View-Controller PHP Framework
6
 *
7
 * This content is released under the Apache 2 License
8
 *
9
 * Copyright (c) 2015-2017 Kotori Technology. All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 *     http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23
24
/**
25
 * Route class
26
 *
27
 * Parses URIs and determines routing
28
 *
29
 * @package     Kotori
30
 * @subpackage  Http
31
 * @author      Kokororin
32
 * @link        https://kotori.love
33
 */
34
namespace Kotori\Http;
35
36
use Kotori\Core\Container;
37
use Kotori\Core\Helper;
38
use Kotori\Core\Middleware;
39
use Kotori\Debug\Hook;
40
use Kotori\Exception\ConfigException;
41
use Kotori\Exception\NotFoundException;
42
use Kotori\Exception\RouteNotFoundException;
43
44
class Route
45
{
46
    /**
47
     * Controllers Array
48
     *
49
     * @var array
50
     */
51
    protected $controllers = [];
52
53
    /**
54
     * Current controller
55
     *
56
     * @var string
57
     */
58
    protected $controller;
59
60
    /**
61
     * Current action
62
     *
63
     * @var string
64
     */
65
    protected $action;
66
67
    /**
68
     * Current URI string
69
     *
70
     * @var mixed
71
     */
72
    protected $uri = '';
73
74
    /**
75
     * Parsed URI Array
76
     *
77
     * @var array
78
     */
79
    protected $uris = [];
80
81
    /**
82
     * Parsed params
83
     *
84
     * @var array
85
     */
86
    protected $params = [];
87
88
    /**
89
     * Class constructor
90
     *
91
     * Initialize route class.
92
     *
93
     * @return void
94
     */
95
    public function __construct()
96
    {
97
        if (Container::get('request')->isCli()) {
98
            $this->uri = $this->parseArgv();
99
        } else {
100
            if (isset($_GET['_i'])) {
101
                $_SERVER['PATH_INFO'] = $_GET['_i'];
102
            }
103
104
            $_SERVER['PATH_INFO'] = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO']
105
            : (isset($_SERVER['ORIG_PATH_INFO']) ? $_SERVER['ORIG_PATH_INFO']
106
                : (isset($_SERVER['REDIRECT_PATH_INFO']) ? $_SERVER['REDIRECT_PATH_INFO'] : ''));
107
108
            $this->uri = $_SERVER['PATH_INFO'];
109
        }
110
111
        if (substr($this->uri, 0, 1) == '/') {
112
            $this->uri = ltrim($this->uri, '/');
113
        }
114
115
        if (trim($this->uri, '/') == '') {
116
            $this->uri = '/';
117
        }
118
119
        Hook::listen(__CLASS__);
120
    }
121
122
    /**
123
     * Map URL to controller and action
124
     *
125
     * @return void
126
     *
127
     * @throws \Kotori\Exception\RouteNotFoundException
128
     * @throws \Kotori\Exception\NotFoundException
129
     */
130
    public function dispatch()
131
    {
132
        if (strtolower(Container::get('config')->get('url_mode')) == 'query_string') {
133
            $this->uri = explode('?', $this->uri, 2);
134
            $_SERVER['QUERY_STRING'] = isset($this->uri[1]) ? $this->uri[1] : '';
135
            $this->uri = $this->uri[0];
136
            parse_str($_SERVER['QUERY_STRING'], $_GET);
137
        }
138
139
        if ($this->uri == 'favicon.ico') {
140
            return Container::get('response')->setStatus(404);
141
        }
142
143
        Middleware::register('before_route');
144
        $parsedRoute = $this->parseRoutes($this->uri);
145
        Middleware::register('after_route');
146
147
        if ($parsedRoute) {
148
            $this->uri = $parsedRoute;
149
        } else {
150
            if (Container::get('request')->isOptions()) {
151
                Container::get('response')->setStatus(204);
152
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method dispatch() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
153
            }
154
155
            throw new RouteNotFoundException('Request URI ' . $this->uri . ' is not Matched by any route.');
156
        }
157
158
        $this->uris = ($this->uri != '') ? explode('/', trim($this->uri, '/')) : [];
159
160
        // Clean uris
161
        foreach ($this->uris as $key => $value) {
162
            if ($value == '') {
163
                unset($this->uris[$key]);
164
            }
165
        }
166
167
        $this->uris = array_merge($this->uris);
168
169
        $this->controller = $this->getController();
170
        $this->action = $this->getAction();
171
172
        // If is already initialized
173
        $prefix = Container::get('config')->get('namespace_prefix');
174
175
        $controllerClassName = $prefix . 'controllers\\' . $this->controller;
176
177
        Middleware::register('before_controller');
178
179
        if (isset($this->controllers[$this->controller])) {
180
            $class = $this->controllers[$this->controller];
181
        } else {
182
            $class = new $controllerClassName();
183
            $this->controllers[$this->controller] = $class;
184
        }
185
186
        Middleware::register('after_controller');
187
188
        if (!class_exists($controllerClassName)) {
189
            throw new NotFoundException('Request Controller ' . $this->controller . ' is not Found.');
190
        }
191
192
        if (!method_exists($class, $this->action)) {
193
            throw new NotFoundException('Request Action ' . $this->action . ' is not Found.');
194
        }
195
196
        $callback = [$class, $this->action];
197
        if (!is_callable($callback)) {
198
            throw new NotFoundException($controllerClassName . '::' . $this->action . '() is not callable');
199
        }
200
201
        // Parse params from uri
202
        $this->params = $this->getParams();
203
204
        // Do some final cleaning of the params
205
        $_GET = array_merge($this->params, $_GET);
206
        $_REQUEST = array_merge($_POST, $_GET, $_COOKIE);
207
208
        if (Container::get('config')->get('app_debug')) {
209
            Container::get('response')->setHeader('X-Kotori-Hash', call_user_func(function () {
210
                $lockFile = Helper::getComposerVendorPath() . '/../composer.lock';
211
                if (!Helper::isFile($lockFile)) {
212
                    return 'unknown';
213
                } else {
214
                    $lockData = file_get_contents($lockFile);
215
                    $lockData = json_decode($lockData, true);
216
                    foreach ($lockData['packages'] as $package) {
217
                        if ($package['name'] == 'kokororin/kotori-php') {
218
                            return substr($package['source']['reference'], 0, 6);
219
                        }
220
                    }
221
                }
222
223
                return 'unknown';
224
            }));
225
        }
226
227
        Middleware::register('before_action');
228
        // Call the requested method
229
        call_user_func_array($callback, $this->params);
230
        Middleware::register('after_action');
231
    }
232
233
    /**
234
     * Returns the controller name
235
     *
236
     * @return      string
237
     *
238
     * @throws \Kotori\Exception\NotFoundException
239
     */
240
    public function getController()
241
    {
242
        if (isset($this->uris[0]) && '' !== $this->uris[0]) {
243
            $_controller = $this->uris[0];
244
        } else {
245
            throw new NotFoundException('Cannot dispatch controller name.');
246
        }
247
248
        return strip_tags($_controller);
249
    }
250
251
    /**
252
     * Returns the action name
253
     *
254
     * @return      string
255
     *
256
     * @throws \Kotori\Exception\NotFoundException
257
     */
258
    public function getAction()
259
    {
260
        if (isset($this->uris[1])) {
261
            $_action = $this->uris[1];
262
        } else {
263
            throw new NotFoundException('Cannot dispatch action name.');
264
        }
265
266
        return strip_tags($_action);
267
    }
268
269
    /**
270
     * Returns the request params
271
     *
272
     * @return array
273
     */
274
    public function getParams()
275
    {
276
        $params = $this->uris;
277
        unset($params[0], $params[1]);
278
        return array_merge($params);
279
    }
280
281
    /**
282
     * Returns the URI
283
     *
284
     * @return string
285
     */
286
    public function getUri()
287
    {
288
        return $this->uri;
289
    }
290
291
    /**
292
     * Parse Routes
293
     *
294
     * Matches any routes that may exist in URL_ROUTE array
295
     * against the URI to determine if the class/method need to be remapped.
296
     *
297
     * @param  string $uri
298
     * @return string
299
     */
300
    protected function parseRoutes($uri)
301
    {
302
        $routes = Container::get('config')->get('url_route');
303
304
        $hostName = Container::get('request')->getHostName();
305
306
        if (isset($routes[$hostName])) {
307
            $routes = $routes[$hostName];
308
        }
309
310
        // Get HTTP verb
311
        $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
312
313
        if (null != $routes) {
314
            foreach ($routes as $key => $val) {
315
                // Check if route format is using HTTP verbs
316
                if (is_array($val)) {
317
                    $val = array_change_key_case($val, CASE_LOWER);
318
                    if (isset($val[$http_verb])) {
319
                        $val = $val[$http_verb];
320
                    } else {
321
                        continue;
322
                    }
323
                }
324
325
                // Does the RegEx match?
326
                if (preg_match('#^' . $key . '$#', $uri, $matches)) {
327
                    // Are we using callbacks to process back-references?
328
                    if (!is_string($val) && is_callable($val)) {
329
                        // Remove the original string from the matches array.
330
                        array_shift($matches);
331
332
                        // Execute the callback using the values in matches as its parameters.
333
                        $val = call_user_func_array($val, $matches);
334
                    }
335
                    // Are we using the default routing method for back-references?
336
                    elseif (strpos($val, '$') !== false && strpos($key, '(') !== false) {
337
                        $val = preg_replace('#^' . $key . '$#', $val, $uri);
338
                    }
339
340
                    return $val;
341
                }
342
            }
343
        }
344
345
    }
346
347
    /**
348
     * Parse CLI arguments
349
     *
350
     * Take each command line argument and assume it is a URI segment.
351
     *
352
     * @return  string
353
     */
354
    protected function parseArgv()
355
    {
356
        $args = array_slice($_SERVER['argv'], 1);
357
        return $args ? implode('/', $args) : '';
358
    }
359
360
    /**
361
     * Build Full URL
362
     *
363
     * @param  string $uri
364
     * @param  string $module
365
     * @return string
366
     *
367
     * @throws \Kotori\Exception\ConfigException
368
     */
369
    public function url($uri = '', $module = null)
370
    {
371
        if ($module != null) {
0 ignored issues
show
It seems like you are loosely comparing $module of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
372
            $appNames = Container::get('config')->get('app_name');
373
            if (is_array($appNames)) {
374
                foreach ($appNames as &$appName) {
375
                    $appName = str_replace('./', '', $appName);
376
                }
377
378
                $appNames = array_flip($appNames);
379
                $baseUrl = $appNames[$module];
380
                $baseUrl = '//' . $baseUrl . '/';
381
            }
382
        } else {
383
            $baseUrl = Container::get('request')->getBaseUrl();
384
        }
385
386
        $uri = is_array($uri) ? implode('/', $uri) : trim($uri, '/');
387
        $prefix = $baseUrl . 'index.php?_i=';
0 ignored issues
show
The variable $baseUrl does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
388
389
        switch (strtolower(Container::get('config')->get('url_mode'))) {
390
            case 'path_info':
391
                return $uri == '' ? rtrim($baseUrl, '/') : $baseUrl . $uri;
392
            case 'query_string':
393
                return $uri == '' ? rtrim($baseUrl, '/') : $prefix . $uri;
394
            default:
395
                throw new ConfigException('`url_mode` Config ERROR');
396
        }
397
398
    }
399
400
}
401