Completed
Push — master ( ea8fd7...b1104c )
by Oleg
03:59
created

MvcResolver   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 242
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 7
dl 0
loc 242
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
B getApp() 0 23 5
B initialize() 0 32 6
A prepareExtensions() 0 14 3
B prepareModules() 0 16 5
A prepareController() 0 13 4
A prepareAction() 0 4 2
A getCalculatePath() 0 4 1
A getExtensions() 0 4 1
A getModules() 0 4 1
A getController() 0 4 1
A getAction() 0 4 1
1
<?php /** MicroHMVCResolver */
2
3
namespace Micro\Mvc;
4
5
use Micro\Base\Exception;
6
use Micro\Base\KernelInjector;
7
use Micro\base\ResolverInterface;
8
use Micro\Mvc\Controllers\IController;
9
use Micro\Web\RequestInjector;
10
use Micro\Web\RouterInjector;
11
use Psr\Http\Message\ServerRequestInterface;
12
13
/**
14
 * MVC Resolver class file.
15
 *
16
 * @author Oleg Lunegov <[email protected]>
17
 * @link https://github.com/linpax/microphp-framework
18
 * @copyright Copyright (c) 2013 Oleg Lunegov
19
 * @license https://github.com/linpax/microphp-framework/blob/master/LICENSE
20
 * @package Micro
21
 * @subpackage Resolver
22
 * @version 1.0
23
 * @since 1.0
24
 */
25
class MvcResolver implements ResolverInterface
26
{
27
    /** @var string $uri converted URL */
28
    protected $uri;
29
30
    /** @var string $extensions Extensions in request */
31
    private $extensions;
32
    /** @var string $modules Modules in request */
33
    private $modules;
34
    /** @var string $controller IController to run */
35
    private $controller;
36
    /** @var string $action Action to run */
37
    private $action;
38
39
40
    /**
41
     * Get instance application
42
     *
43
     * @access public
44
     *
45
     * @return IController
46
     * @throws Exception
47
     */
48
    public function getApp()
49
    {
50
        /** @var ServerRequestInterface $request */
51
        $request = (new RequestInjector)->build();
52
        /** @var array $rawQuery */
53
        $rawQuery = $request->getQueryParams();
54
55
        $query = !empty($rawQuery['r']) ? $rawQuery['r'] : '/default';
56
        $query = (substr($query, -1) === '/') ? substr($query, 0, -1) : $query;
57
58
        $this->uri = (new RouterInjector)->build()->parse($query, $request->getMethod());
59
60
        $this->initialize();
61
62
        /** @var string $cls */
63
        $cls = $this->getCalculatePath();
64
65
        if (!class_exists($cls) || !is_subclass_of($cls, '\Micro\Mvc\Controllers\IController')) {
66
            throw new Exception('Controller '.$cls.' not found or not a valid');
67
        }
68
69
        return new $cls($this->getModules());
0 ignored issues
show
Security Code Execution introduced by
$cls can contain request data and is used in code execution context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and $query ?: $_GET is passed to ServerRequest::__construct()
    in vendor/src/ServerRequestFactory.php on line 71
  2. ServerRequest::$queryParams is assigned
    in vendor/src/ServerRequest.php on line 101
  3. Tainted property ServerRequest::$queryParams is read
    in vendor/src/ServerRequest.php on line 156
  4. ServerRequest::getQueryParams() returns tainted data, and $rawQuery is assigned
    in src/mvc/MvcResolver.php on line 53
  5. $query is assigned
    in src/mvc/MvcResolver.php on line 55
  6. $query is assigned
    in src/mvc/MvcResolver.php on line 56
  7. MvcResolver::$uri is assigned
    in src/mvc/MvcResolver.php on line 58
  8. Tainted property MvcResolver::$uri is read, and $this->uri is passed through substr(), and substr($this->uri, 0, $key ?: strlen($this->uri)) is passed through explode(), and $uriBlocks is assigned
    in src/mvc/MvcResolver.php on line 84
  9. $uriBlocks is passed to MvcResolver::prepareExtensions()
    in src/mvc/MvcResolver.php on line 90
  10. $block is assigned
    in src/mvc/MvcResolver.php on line 125
  11. $block is passed through ucfirst(), and MvcResolver::$extensions is assigned
    in src/mvc/MvcResolver.php on line 127
  12. Tainted property MvcResolver::$extensions is read
    in src/mvc/MvcResolver.php on line 227
  13. MvcResolver::getExtensions() returns tainted data
    in src/mvc/MvcResolver.php on line 215
  14. MvcResolver::getCalculatePath() returns tainted data, and $cls is assigned
    in src/mvc/MvcResolver.php on line 63

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
70
    }
71
72
    /**
73
     * Initialize request object
74
     *
75
     * @access public
76
     *
77
     * @return void
78
     * @throws Exception
79
     */
80
    protected function initialize()
81
    {
82
        $key = strpos($this->uri, '?');
83
        $params = $key ? substr($this->uri, $key + 2) : null;
84
        $uriBlocks = explode('/', substr($this->uri, 0, $key ?: strlen($this->uri)));
85
86
        if (0 === strpos($this->uri, '/')) {
87
            array_shift($uriBlocks);
88
        }
89
90
        $this->prepareExtensions($uriBlocks);
91
        $this->prepareModules($uriBlocks);
92
        $this->prepareController($uriBlocks);
93
        $this->prepareAction($uriBlocks);
94
95
        if ($params) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $params of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
96
            $paramBlocks = explode('&', $params);
97
            $query = (new RequestInjector)->build()->getQueryParams();
98
99
            foreach ($paramBlocks AS $param) {
100
                $val = explode('=', $param);
101
102
                $query[$val[0]] = $val[1];
103
            }
104
105
            // replace request
106
            $request = (new RequestInjector)->build();
107
            (new RequestInjector)->addRequirement('request', $request->withQueryParams(
108
                array_replace_recursive($request->getQueryParams(), $query)
109
            ));
110
        }
111
    }
112
113
    /**
114
     * Prepare extensions
115
     *
116
     * @access private
117
     *
118
     * @param array $uriBlocks uri blocks from URL
119
     *
120
     * @return void
121
     * @throws Exception
122
     */
123
    protected function prepareExtensions(&$uriBlocks)
124
    {
125
        foreach ($uriBlocks as $i => $block) {
126
            if (file_exists((new KernelInjector)->build()->getAppDir().$this->extensions.'/extensions/'.$block)) {
127
                $this->extensions .= '/Extensions/'.ucfirst($block);
128
129
                unset($uriBlocks[$i]);
130
            } else {
131
                break;
132
            }
133
        }
134
135
        $this->extensions = str_replace('/', '\\', $this->extensions);
136
    }
137
138
    /**
139
     * Prepare modules
140
     *
141
     * @access private
142
     *
143
     * @global      Micro
144
     *
145
     * @param array $uriBlocks uri blocks from URL
146
     *
147
     * @return void
148
     * @throws Exception
149
     */
150
    protected function prepareModules(&$uriBlocks)
151
    {
152
        $path = (new KernelInjector)->build()->getAppDir().($this->extensions ?: '');
153
154
        foreach ($uriBlocks as $i => $block) {
155
            if ($block && file_exists($path.strtolower($this->modules).'/modules/'.$block)) {
156
                $this->modules .= '/Modules/'.ucfirst($block);
157
158
                unset($uriBlocks[$i]);
159
            } else {
160
                break;
161
            }
162
        }
163
164
        $this->modules = str_replace('/', '\\', $this->modules);
165
    }
166
167
    /**
168
     * Prepare controller
169
     *
170
     * @access private
171
     *
172
     * @param array $uriBlocks uri blocks from URL
173
     *
174
     * @return void
175
     * @throws Exception
176
     */
177
    protected function prepareController(&$uriBlocks)
178
    {
179
        $path = (new KernelInjector)->build()->getAppDir().($this->extensions ?: '').strtolower($this->modules ?: '');
180
        $str = array_shift($uriBlocks);
181
182
        if (file_exists(str_replace('\\', '/', $path.'/controllers/'.ucfirst($str).'Controller.php'))) {
183
            $this->controller = $str;
184
        } else {
185
            $this->controller = 'default';
186
187
            array_unshift($uriBlocks, $str);
188
        }
189
    }
190
191
    /**
192
     * Prepare action
193
     *
194
     * @access private
195
     *
196
     * @param array $uriBlocks uri blocks from URL
197
     *
198
     * @return void
199
     */
200
    protected function prepareAction(&$uriBlocks)
201
    {
202
        $this->action = array_shift($uriBlocks) ?: 'index';
203
    }
204
205
206
    /**
207
     * Get calculate path to controller
208
     *
209
     * @access public
210
     *
211
     * @return string
212
     */
213
    public function getCalculatePath()
214
    {
215
        return '\\App'.$this->getExtensions().$this->getModules().'\\Controllers\\'.$this->getController();
216
    }
217
218
    /**
219
     * Get extensions from request
220
     *
221
     * @access public
222
     *
223
     * @return string
224
     */
225
    public function getExtensions()
226
    {
227
        return $this->extensions;
228
    }
229
230
    /**
231
     * Get modules from request
232
     *
233
     * @access public
234
     *
235
     * @return string
236
     */
237
    public function getModules()
238
    {
239
        return $this->modules;
240
    }
241
242
    /**
243
     * Get controller from request
244
     *
245
     * @access public
246
     *
247
     * @return string
248
     */
249
    public function getController()
250
    {
251
        return ucfirst($this->controller).'Controller';
252
    }
253
254
255
    /**
256
     * Get action from request
257
     *
258
     * @access public
259
     *
260
     * @return string
261
     */
262
    public function getAction()
263
    {
264
        return $this->action;
265
    }
266
}
267