Completed
Push — master ( 1798cf...75b2e6 )
by Bohuslav
01:48
created

DispatcherClass   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 328
Duplicated Lines 2.13 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 1
dl 7
loc 328
rs 8.8
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
B dispatchRoute() 0 39 3
B dispatchNotFound() 0 25 2
A setBaseNamespace() 0 6 1
A setNotFoundHandler() 0 6 1
B resolveControlerAction() 0 25 3
B transformHandler() 0 17 5
C resolveNamespace() 0 29 7
A isPlaceholder() 0 8 3
D getFunctionArgumentsControlers() 7 31 9
A getMethodParameters() 0 10 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace Kambo\Router\Dispatchers;
3
4
use Kambo\Router\Dispatchers\Interfaces\DispatcherInterface;
5
use Kambo\Router\Route\Route;
6
7
/**
8
 * Class dispatcher with module/controller/action support
9
 *
10
 * @author  Bohuslav Simek <[email protected]>
11
 * @license Apache-2.0
12
 * @package Kambo\Router\Dispatchers
13
 */
14
class DispatcherClass implements DispatcherInterface
15
{
16
    /**
17
     * Not found handler which will be called if nothing has been found.
18
     *
19
     * @var mixed
20
     */
21
    private $notFoundHandler;
22
23
    /**
24
     * Base namespace for all dispatched classes.
25
     *
26
     * @var string
27
     */
28
    private $baseNamespace = null;
29
30
    /**
31
     * Name of class that will be used for constructing a namespace for proper
32
     * class resolve.
33
     *
34
     * @var string
35
     */
36
    private $controllerName = 'Controllers';
37
38
    /**
39
     * Name of module that will be used for constructing a namespace for proper
40
     * class resolve.
41
     *
42
     * @var string
43
     */
44
    private $moduleName = 'Modules';
45
46
    /**
47
     * Prefix for action method.
48
     * Target method is allways called with this prefix.
49
     *
50
     * @var string
51
     */
52
    private $actionName = 'action';
53
54
    /**
55
     * Dispatch found route with given parameters
56
     *
57
     * @param Route $route      found route
58
     * @param mixed $parameters parameters for route
59
     *
60
     * @return mixed
61
     */
62
    public function dispatchRoute(Route $route, array $parameters)
63
    {
64
        $handler = $route->getHandler();
65
        if (isset($handler['controler']) && isset($handler['action'])) {
66
            $routeParameters               = $route->getParameters();
67
            list($controllerName, $action) = $this->resolveControlerAction(
68
                $parameters,
69
                $routeParameters,
70
                $handler
71
            );
72
73
            // Create instance of target class
74
            $controller = new $controllerName();
75
            // Create class method name with prefix
76
            $methodName = $this->actionName.$action;
77
78
            $parameterMap = $this->getMethodParameters(
79
                $controllerName,
0 ignored issues
show
Documentation introduced by
$controllerName is of type string, but the function expects a object<Kambo\Router\Dispatchers\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...
80
                $methodName
81
            );
82
83
            $methodParameters = $this->getFunctionArgumentsControlers(
84
                $parameterMap,
85
                $parameters,
86
                $routeParameters,
87
                $handler
88
            );
89
90
            return call_user_func_array(
91
                [
92
                    $controller,
93
                    $methodName
94
                ],
95
                $methodParameters
96
            );
97
        }
98
99
        return $this->dispatchNotFound();
100
    }
101
102
    /**
103
     * Called if nothing has been not found
104
     *
105
     * @return mixed
106
     */
107
    public function dispatchNotFound()
108
    {
109
        if (isset($this->notFoundHandler)) {
110
            $notFoundHandler = $this->notFoundHandler;
111
            $controllerName  = implode(
112
                '\\',
113
                [
114
                    $this->baseNamespace,
115
                    $this->controllerName,
116
                    $notFoundHandler['controler']
117
                ]
118
            );
119
120
            $controllerInstance = new $controllerName();
121
122
            return call_user_func(
123
                [
124
                    $controllerInstance,
125
                    $this->actionName.$notFoundHandler['action']
126
                ]
127
            );
128
        }
129
130
        return null;
131
    }
132
133
    /**
134
     * Set base namespace to allow proper resolve of class name
135
     *
136
     * @param string $baseNamespace base namespace
137
     *
138
     * @return self for fluent interface
139
     */
140
    public function setBaseNamespace($baseNamespace)
141
    {
142
        $this->baseNamespace = $baseNamespace;
143
144
        return $this;
145
    }
146
147
    /**
148
     * Set not found handler
149
     *
150
     * @param string $handler handler that will be excuted if nothing has been
151
     *                        found
152
     *
153
     * @return self for fluent interface
154
     */
155
    public function setNotFoundHandler($handler)
156
    {
157
        $this->notFoundHandler = $handler;
158
159
        return $this;
160
    }
161
162
    // ------------ PRIVATE METHODS
163
164
    /**
165
     * Resolve target name of class (controller) and method (action)
166
     *
167
     * @param mixed $matches    found matched variables
168
     * @param mixed $parameters route parameters
169
     * @param mixed $handler    handler that should be executed
170
     *
171
     * @return mixed
172
     */
173
    private function resolveControlerAction($matches, $parameters, $handler)
174
    {
175
        $controler = $handler['controler'];
176
        $action    = $handler['action'];
177
        $namespace = $this->resolveNamespace($parameters, $handler, $matches);
178
179
        if ($this->isPlaceholder($action)) {
180
            $transformed = $this->transformHandler($matches, $parameters, $handler);
181
            if ($this->isPlaceholder($controler)) {
182
                $controler = $namespace.'\\'.$transformed['controler'];
183
            } else {
184
                $controler = $namespace.'\\'.$controler;
185
            }
186
187
            return [
188
                $controler,
189
                $transformed['action']
190
            ];
191
        }
192
193
        return [
194
            $namespace.'\\'.$controler,
195
            $action
196
        ];
197
    }
198
199
    /**
200
     * Transform provided handler with variables and parameters
201
     *
202
     * @param mixed $matches    found matched variables
203
     * @param mixed $parameters route parameters
204
     * @param mixed $handler    handler that should be executed
205
     *
206
     * @return mixed
207
     */
208
    private function transformHandler($matches, $parameters, $handler)
209
    {
210
        $transformed = [];
211
        foreach ($handler as $target => $placeholder) {
212
            foreach ($parameters as $key => $parameterName) {
213
                if ($parameterName[0][0] == $placeholder) {
214
                    if ($target == 'controler') {
215
                        $transformed[$target] = $matches[$key].'Controler';
216
                    } else {
217
                        $transformed[$target] = $matches[$key];
218
                    }
219
                }
220
            }
221
        }
222
223
        return $transformed;
224
    }
225
226
    /**
227
     * Resolve proper namespace according parameters, handler and matches
228
     *
229
     * @param mixed $parameters route parameters
230
     * @param mixed $handler    handler that should be executed
231
     * @param mixed $matches    found matched variables
232
233
     * @return mixed
234
     */
235
    private function resolveNamespace($parameters, $handler, $matches)
236
    {
237
        if (isset($handler['module'])) {
238
            $moduleName = $handler['module'];
239
            if ($this->isPlaceholder($moduleName)) {
240
                foreach ($handler as $target => $placeholder) {
241
                    foreach ($parameters as $key => $parameterName) {
242
                        if ($parameterName[0][0] == $placeholder) {
243
                            if ($target == 'module') {
244
                                $moduleName = $matches[$key];
245
                            }
246
                        }
247
                    }
248
                }
249
            }
250
251
            return implode(
252
                '\\',
253
                [
254
                    $this->baseNamespace,
255
                    $this->moduleName,
256
                    $moduleName,
257
                    $this->controllerName
258
                ]
259
            );
260
        }
261
262
        return $this->baseNamespace.'\\'.$this->controllerName;
263
    }
264
265
    /**
266
     * Check if the variable is placeholder
267
     *
268
     * @param string $value  found route
269
     *
270
     * @return boolean true if value should be transfered
271
     */
272
    private function isPlaceholder($value)
273
    {
274
        if (strrchr($value, '}') && (0 === strpos($value, '{'))) {
275
            return true;
276
        }
277
278
        return false;
279
    }
280
281
    /**
282
     * Get function arguments for controler
283
     *
284
     * @param mixed $paramMap   parameter map
285
     * @param mixed $matches    found matched variables
286
     * @param mixed $parameters route parameters
287
     * @param mixed $handlers   handler that should be executed
288
289
     * @return mixed
290
     */
291
    private function getFunctionArgumentsControlers($paramMap, $matches, $parameters, $handlers)
292
    {
293
        $output  = [];
294
        $matches = array_values($matches);
295
296
        if (isset($parameters)) {
297
            foreach ($handlers as $placeholder) {
298
                if ($this->isPlaceholder($placeholder)) {
299
                    foreach ($parameters as $key => $parameterName) {
300
                        if ($parameterName[0][0] == $placeholder) {
301
                            unset($parameters[$key]);
302
                            unset($matches[$key]);
303
                        }
304
                    }
305
                }
306
            }
307
308
            $parameters = array_values($parameters);
309
            $matches    = array_values($matches);
310
311 View Code Duplication
            foreach ($parameters as $key => $valueName) {
312
                foreach ($paramMap as $possition => $value) {
313
                    if ($value == $valueName[1][0]) {
314
                        $output[] = $matches[$possition];
315
                    }
316
                }
317
            }
318
        }
319
320
        return $output;
321
    }
322
323
    /**
324
     * Get names of parameters for provided class and method
325
     *
326
     * @param class  $class      name of class
327
     * @param string $methodName name of method
328
     *
329
     * @return array
330
     */
331
    private function getMethodParameters($class, $methodName)
332
    {
333
        $methodReflection = new \ReflectionMethod($class, $methodName);
334
        $parametersName   = [];
335
        foreach ($methodReflection->getParameters() as $parameter) {
336
            $parametersName[] = $parameter->name;
337
        }
338
339
        return $parametersName;
340
    }
341
}
342