Test Failed
Branch develop (ab83c3)
by Bohuslav
02:47
created

ClassAutoBind::resolveNamespace()   C

Complexity

Conditions 7
Paths 3

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
c 0
b 0
f 0
rs 6.7272
cc 7
eloc 16
nc 3
nop 3
1
<?php
2
namespace Kambo\Router\Dispatcher;
3
4
// \Spl
5
use ReflectionMethod;
6
7
// \Kambo\Router
8
use Kambo\Router\Dispatcher;
9
use Kambo\Router\Route\Route\Parsed;
10
11
/**
12
 * Class dispatcher with module/controller/action support
13
 *
14
 * @package Kambo\Router\Dispatcher
15
 * @author  Bohuslav Simek <[email protected]>
16
 * @license MIT
17
 */
18
class ClassAutoBind implements Dispatcher
19
{
20
    /**
21
     * Not found handler which will be called if nothing has been found.
22
     *
23
     * @var mixed
24
     */
25
    private $notFoundHandler;
26
27
    /**
28
     * Base namespace for all dispatched classes.
29
     *
30
     * @var string
31
     */
32
    private $baseNamespace = null;
33
34
    /**
35
     * Name of class that will be used for constructing a namespace for proper
36
     * class resolve.
37
     *
38
     * @var string
39
     */
40
    private $controllerName = 'Controllers';
41
42
    /**
43
     * Name of module that will be used for constructing a namespace for proper
44
     * class resolve.
45
     *
46
     * @var string
47
     */
48
    private $moduleName = 'Modules';
49
50
    /**
51
     * Prefix for action method.
52
     * Target method is allways called with this prefix.
53
     *
54
     * @var string
55
     */
56
    private $actionName = 'action';
57
58
    /**
59
     * Dispatch found route with given parameters
60
     *
61
     * @param \Kambo\Router\Route\Route\Parsed $route      Instance of found and parsed route.
62
     * @param array                            $parameters Additional parameters which will be passed into
63
     *                                                     the dispatcher.
64
     *
65
     * @return mixed
66
     */
67
    public function dispatchRoute(Parsed $route, array $parameters)
68
    {
69
        $handler = $route->getHandler();
70
        if (isset($handler['controler']) && isset($handler['action'])) {
71
            $routePlaceholders             = $route->getPlaceholders();
72
            list($controllerName, $action) = $this->resolveControlerAction(
73
                $route->getParameters(),
74
                $routePlaceholders,
75
                $handler
76
            );
77
78
            // Create instance of target class
79
            $controller = new $controllerName();
80
            // Create class method name with prefix
81
            $methodName = $this->actionName.$action;
82
83
            $parameterMap = $this->getMethodParameters(
84
                $controllerName,
0 ignored issues
show
Documentation introduced by
$controllerName is of type string, but the function expects a object<Kambo\Router\Dispatcher\class>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
85
                $methodName
86
            );
87
88
            $methodParameters = $this->getFunctionArgumentsControlers(
89
                $parameterMap,
90
                $route->getParameters(),
91
                $routePlaceholders,
92
                $handler
93
            );
94
95
            return call_user_func_array(
96
                [
97
                    $controller,
98
                    $methodName
99
                ],
100
                $methodParameters
101
            );
102
        }
103
104
        return $this->dispatchNotFound();
105
    }
106
107
    /**
108
     * Called if any of route did not match the request.
109
     *
110
     * @return mixed
111
     */
112
    public function dispatchNotFound()
113
    {
114
        if (isset($this->notFoundHandler)) {
115
            $notFoundHandler = $this->notFoundHandler;
116
            $controllerName  = implode(
117
                '\\',
118
                [
119
                    $this->baseNamespace,
120
                    $this->controllerName,
121
                    $notFoundHandler['controler']
122
                ]
123
            );
124
125
            $controllerInstance = new $controllerName();
126
127
            return call_user_func(
128
                [
129
                    $controllerInstance,
130
                    $this->actionName.$notFoundHandler['action']
131
                ]
132
            );
133
        }
134
135
        return null;
136
    }
137
138
    /**
139
     * Set base namespace to allow proper resolve of class name
140
     *
141
     * @param string $baseNamespace base namespace
142
     *
143
     * @return self for fluent interface
144
     */
145
    public function setBaseNamespace($baseNamespace)
146
    {
147
        $this->baseNamespace = $baseNamespace;
148
149
        return $this;
150
    }
151
152
    /**
153
     * Sets not found handler
154
     *
155
     * @param string $handler handler that will be excuted if nothing has been
156
     *                        found
157
     *
158
     * @return self for fluent interface
159
     */
160
    public function setNotFoundHandler($handler)
161
    {
162
        $this->notFoundHandler = $handler;
163
164
        return $this;
165
    }
166
167
    // ------------ PRIVATE METHODS
168
169
    /**
170
     * Resolve target name of class (controller) and method (action)
171
     *
172
     * @param mixed $matches    found matched variables
173
     * @param mixed $parameters route parameters
174
     * @param mixed $handler    handler that should be executed
175
     *
176
     * @return mixed
177
     */
178
    private function resolveControlerAction($matches, $parameters, $handler)
179
    {
180
        $controler = $handler['controler'];
181
        $action    = $handler['action'];
182
        $namespace = $this->resolveNamespace($parameters, $handler, $matches);
183
184
        if ($this->isPlaceholder($action)) {
185
            $transformed = $this->transformHandler($matches, $parameters, $handler);
186
            if ($this->isPlaceholder($controler)) {
187
                $controler = $namespace.'\\'.$transformed['controler'];
188
            } else {
189
                $controler = $namespace.'\\'.$controler;
190
            }
191
192
            return [
193
                $controler,
194
                $transformed['action']
195
            ];
196
        }
197
198
        return [
199
            $namespace.'\\'.$controler,
200
            $action
201
        ];
202
    }
203
204
    /**
205
     * Transform provided handler with variables and parameters
206
     *
207
     * @param mixed $matches    found matched variables
208
     * @param mixed $parameters route parameters
209
     * @param mixed $handler    handler that should be executed
210
     *
211
     * @return mixed
212
     */
213
    private function transformHandler($matches, $parameters, $handler)
214
    {
215
        $transformed = [];
216
        foreach ($handler as $target => $placeholder) {
217
            foreach ($parameters as $key => $parameterName) {
218
                if ($parameterName[0][0] == $placeholder) {
219
                    if ($target == 'controler') {
220
                        $transformed[$target] = $matches[$key].'Controler';
221
                    } else {
222
                        $transformed[$target] = $matches[$key];
223
                    }
224
                }
225
            }
226
        }
227
228
        return $transformed;
229
    }
230
231
    /**
232
     * Resolve proper namespace according parameters, handler and matches
233
     *
234
     * @param mixed $parameters route parameters
235
     * @param mixed $handler    handler that should be executed
236
     * @param mixed $matches    found matched variables
237
238
     * @return mixed
239
     */
240
    private function resolveNamespace($parameters, $handler, $matches)
241
    {
242
        if (isset($handler['module'])) {
243
            $moduleName = $handler['module'];
244
            if ($this->isPlaceholder($moduleName)) {
245
                foreach ($handler as $target => $placeholder) {
246
                    foreach ($parameters as $key => $parameterName) {
247
                        if ($parameterName[0][0] == $placeholder) {
248
                            if ($target == 'module') {
249
                                $moduleName = $matches[$key];
250
                            }
251
                        }
252
                    }
253
                }
254
            }
255
256
            return implode(
257
                '\\',
258
                [
259
                    $this->baseNamespace,
260
                    $this->moduleName,
261
                    $moduleName,
262
                    $this->controllerName
263
                ]
264
            );
265
        }
266
267
        return $this->baseNamespace.'\\'.$this->controllerName;
268
    }
269
270
    /**
271
     * Check if the variable is placeholder
272
     *
273
     * @param string $value  found route
274
     *
275
     * @return boolean true if value should be transfered
276
     */
277
    private function isPlaceholder($value)
278
    {
279
        if (strrchr($value, '}') && (0 === strpos($value, '{'))) {
280
            return true;
281
        }
282
283
        return false;
284
    }
285
286
    /**
287
     * Get function arguments for controler
288
     *
289
     * @param mixed $paramMap   parameter map
290
     * @param mixed $matches    found matched variables
291
     * @param mixed $parameters route parameters
292
     * @param mixed $handlers   handler that should be executed
293
294
     * @return mixed
295
     */
296
    private function getFunctionArgumentsControlers(
297
        $paramMap,
298
        $matches,
299
        $parameters,
300
        $handlers
301
    ) {
302
        $output  = [];
303
        $matches = array_values($matches);
304
305
        if (isset($parameters)) {
306
            foreach ($handlers as $placeholder) {
307
                if ($this->isPlaceholder($placeholder)) {
308
                    foreach ($parameters as $key => $parameterName) {
309
                        if ($parameterName[0][0] == $placeholder) {
310
                            unset($parameters[$key]);
311
                            unset($matches[$key]);
312
                        }
313
                    }
314
                }
315
            }
316
317
            $parameters = array_values($parameters);
318
            $matches    = array_values($matches);
319
320 View Code Duplication
            foreach ($parameters as $key => $valueName) {
321
                foreach ($paramMap as $possition => $value) {
322
                    if ($value == $valueName[1][0]) {
323
                        $output[] = $matches[$possition];
324
                    }
325
                }
326
            }
327
        }
328
329
        return $output;
330
    }
331
332
    /**
333
     * Get names of parameters for provided class and method
334
     *
335
     * @param class  $class      name of class
336
     * @param string $methodName name of method
337
     *
338
     * @return array
339
     */
340
    private function getMethodParameters($class, $methodName)
341
    {
342
        $methodReflection = new ReflectionMethod($class, $methodName);
343
        $parametersName   = [];
344
        foreach ($methodReflection->getParameters() as $parameter) {
345
            $parametersName[] = $parameter->name;
346
        }
347
348
        return $parametersName;
349
    }
350
}
351