Completed
Push — master ( effdc3...14321e )
by Sinnarasa
05:10
created

Router::callResponse()   C

Complexity

Conditions 11
Paths 14

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 3
Metric Value
c 5
b 1
f 3
dl 0
loc 28
rs 5.2653
cc 11
eloc 20
nc 14
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace JetFire\Routing;
4
5
use JetFire\Routing\Matcher\ArrayMatcher;
6
use ReflectionClass;
7
use ReflectionMethod;
8
9
/**
10
 * Class Router
11
 * @package JetFire\Routing
12
 */
13
class Router
14
{
15
16
    /**
17
     * @var Route
18
     */
19
    public $route;
20
    /**
21
     * @var RouteCollection
22
     */
23
    public $collection;
24
    /**
25
     * @var ResponseInterface
26
     */
27
    public $response;
28
    /**
29
     * @var
30
     */
31
    public $middleware;
32
    /**
33
     * @var array
34
     */
35
    public $matcher = [];
36
    /**
37
     * @var
38
     */
39
    public $dispatcher;
40
    /**
41
     * @var array
42
     */
43
    private $config = [
44
        'templateExtension' => ['.html', '.php', '.json', '.xml'],
45
        'templateCallback' => [],
46
        'di' => '',
47
        'generateRoutesPath' => false,
48
    ];
49
50
    /**
51
     * @param RouteCollection $collection
52
     * @param ResponseInterface $response
53
     * @param Route $route
54
     */
55
    public function __construct(RouteCollection $collection, ResponseInterface $response = null, Route $route = null)
56
    {
57
        $this->collection = $collection;
58
        $this->response = is_null($response) ? new Response() : $response;
59
        $this->response->setStatusCode(404);
60
        $this->route = is_null($route) ? new Route() : $route;
61
        $this->middleware = new Middleware($this);
62
        $this->config['di'] = function ($class) {
63
            return new $class;
64
        };
65
    }
66
67
    /**
68
     * @param array $config
69
     */
70
    public function setConfig($config)
71
    {
72
        $this->config = array_merge($this->config, $config);
73
    }
74
75
    /**
76
     * @return array
77
     */
78
    public function getConfig()
79
    {
80
        return $this->config;
81
    }
82
83
    /**
84
     * @param object|array $matcher
85
     */
86
    public function setMatcher($matcher)
87
    {
88
        if (is_object($matcher))
89
            $matcher = [$matcher];
90
        $this->matcher = $matcher;
91
    }
92
93
    /**
94
     * @param string $matcher
95
     */
96
    public function addMatcher($matcher)
97
    {
98
        $this->matcher[] = $matcher;
99
    }
100
101
    /**
102
     * @description main function
103
     */
104
    public function run()
105
    {
106
        $this->setUrl();
107
        if ($this->config['generateRoutesPath']) $this->collection->generateRoutesPath();
108
        if ($this->match()) $this->callTarget();
109
        $this->callResponse();
110
    }
111
112
    /**
113
     * @param null $url
114
     */
115
    public function setUrl($url = null)
0 ignored issues
show
Coding Style introduced by
setUrl uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
setUrl uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
116
    {
117
        if (is_null($url))
118
            $url = (isset($_GET['url'])) ? $_GET['url'] : substr(str_replace(str_replace('/index.php', '', $_SERVER['SCRIPT_NAME']), '', $_SERVER['REQUEST_URI']), 1);
119
        $this->route->setUrl('/' . trim(explode('?', $url)[0], '/'));
120
    }
121
122
    /**
123
     * @return bool
124
     */
125
    public function match()
126
    {
127
        foreach ($this->matcher as $key => $matcher) {
128
            if (call_user_func([$this->matcher[$key], 'match'])) return true;
129
        }
130
        return false;
131
    }
132
133
    /**
134
     *
135
     */
136
    public function callTarget()
137
    {
138
        $target = is_array($this->route->getTarget('dispatcher')) ? $this->route->getTarget('dispatcher') : [$this->route->getTarget('dispatcher')];
139
        if (!empty($target)) {
140
            foreach ($target as $call) {
141
                $this->dispatcher = new $call($this->route, $this->response);
142
                call_user_func([$this->dispatcher, 'call']);
143
            }
144
        }
145
    }
146
147
    /**
148
     * @param array $responses
149
     */
150
    public function setResponses($responses = [])
151
    {
152
        $this->route->addDetail('response_templates', $responses);
153
    }
154
155
    /**
156
     * @description set response code
157
     */
158
    public function callResponse()
159
    {
160
        if (isset($this->route->getDetail()['response_templates']) && isset($this->route->getDetail()['response_templates'][$code = $this->response->getStatusCode()])) {
161
            $this->route->setCallback($this->route->getDetail()['response_templates'][$code]);
162
            $matcher = null;
163
            foreach ($this->matcher as $instance) if ($instance instanceof ArrayMatcher) $matcher = $instance;
164
            if (is_null($matcher)) $matcher = new ArrayMatcher($this);
165
            foreach (call_user_func([$matcher, 'getResolver']) as $match)
166
                if (is_array($target = call_user_func_array([$matcher, $match], [$this->route->getCallback()]))) {
167
                    call_user_func_array([$matcher, 'setTarget'], [$target]);
168
                    $this->callTarget();
169
                    break;
170
                }
171
            $this->response->setStatusCode($code);
172
        }
173
        if (isset($this->collection->middleware['before_render'])){
174
            foreach ($this->collection->middleware['before_render'] as $callback){
175
                $call = explode('@', $callback);
176
                if(isset($call[1])){
177
                    $classes = ['JetFire\Routing\Router' => $this];
178
                    $args = ['router' => $this, 'route' => $this->route, 'response' => $this->response];
179
                    $this->callMethod($call[0], $call[1], $args, $args, $classes);
180
                }
181
            }
182
        }
183
184
        $this->response->send();
185
    }
186
187
    /**
188
     * @param $controller
189
     * @param $method
190
     * @param array $methodArgs
191
     * @param array $ctrlArgs
192
     * @param array $classInstance
193
     * @return mixed|null
194
     */
195
    public function callMethod($controller, $method, $methodArgs = [], $ctrlArgs = [], $classInstance = [])
196
    {
197
        if (class_exists($controller) && method_exists($controller, $method)) {
198
            $reflectionMethod = new ReflectionMethod($controller, $method);
199
            $dependencies = [];
200
            foreach ($reflectionMethod->getParameters() as $arg) {
201 View Code Duplication
                if (isset($methodArgs[$arg->name]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
202
                    array_push($dependencies, $methodArgs[$arg->name]);
203
                else if (!is_null($arg->getClass())) {
204
                    array_push($dependencies, call_user_func_array($this->route->getTarget('di'), [$arg->getClass()->name]));
205
                }
206
            }
207
            $dependencies = array_merge($dependencies, $methodArgs);
208
            return $reflectionMethod->invokeArgs($this->callClass($controller, $ctrlArgs, $classInstance), $dependencies);
209
        }
210
        return null;
211
    }
212
213
    /**
214
     * @param $controller
215
     * @param array $ctrlArgs
216
     * @param array $classInstance
217
     * @return object
218
     * @throws \Exception
219
     */
220
    public function callClass($controller, $ctrlArgs = [], $classInstance = [])
221
    {
222
        $reflector = new ReflectionClass($controller);
223
        if (!$reflector->isInstantiable())
224
            throw new \Exception('Controller [' . $controller . '] is not instantiable.');
225
        $constructor = $reflector->getConstructor();
226
        if (is_null($constructor))
227
            return call_user_func_array($this->route->getTarget('di'), [$controller]);
228
        $dependencies = [];
229
        foreach ($constructor->getParameters() as $arg) {
230
            if (isset($ctrlArgs[$arg->name]))
231
                array_push($dependencies, $ctrlArgs[$arg->name]);
232 View Code Duplication
            else if (isset($classInstance[$arg->getClass()->name]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
233
                array_push($dependencies, $classInstance[$arg->getClass()->name]);
234
            else if (!is_null($arg->getClass())) {
235
                array_push($dependencies, call_user_func_array($this->route->getTarget('di'), [$arg->getClass()->name]));
236
            }
237
        }
238
        $dependencies = array_merge($dependencies, $ctrlArgs);
239
        return $reflector->newInstanceArgs($dependencies);
240
    }
241
242
}
243