Passed
Push — feature/events ( 9d660f...96fd77 )
by Mathieu
02:50
created

Router::doRouting()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 10
Bugs 0 Features 1
Metric Value
cc 5
eloc 18
c 10
b 0
f 1
nc 8
nop 0
dl 0
loc 31
ccs 0
cts 18
cp 0
crap 30
rs 9.3554
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Suricate;
6
7
/**
8
// TODO : handle closure
9
 **/
10
class Router extends Service
11
{
12
    private $requestUri;
13
    private $baseUri;
14
    private $routes;
15
    private $response;
16
    private $appMiddlewares = ['\Suricate\Middleware\CheckMaintenance'];
17
18 9
    public function __construct()
19
    {
20 9
        parent::__construct();
21
22 9
        $this->routes = [];
23 9
        $this->response = Suricate::Response();
24 9
        $this->parseRequest();
25
26
        // Get app base URI, to transform real path before passing to route
27 9
        $this->baseUri = Suricate::App()->getParameter('base_uri');
28 9
    }
29
30 8
    public function configure($parameters = [])
31
    {
32 8
        foreach ($parameters as $routeName => $routeData) {
33
            if (isset($routeData['isRest']) && $routeData['isRest']) {
34
                $this->buildRestRoutes($routeName, $routeData);
35
            } else {
36
                $this->buildRoute($routeName, $routeData);
37
            }
38
        }
39 8
    }
40
41
    private function buildRoute($routeName, $routeData)
42
    {
43
        $routeTarget = null;
44
        if (isset($routeData['target'])) {
45
            $routeTarget = explode('::', $routeData['target']);
46
        }
47
48
        $routeMethod = isset($routeData['method'])
49
            ? $routeData['method']
50
            : 'any';
51
        $parameters = isset($routeData['parameters'])
52
            ? $routeData['parameters']
53
            : [];
54
55
        $middleware = [];
56
        if (isset($routeData['middleware'])) {
57
            $middleware = (array) $routeData['middleware'];
58
        }
59
60
        $this->addRoute(
61
            $routeName,
62
            $routeMethod,
63
            $routeData['path'],
64
            $routeTarget,
65
            $parameters,
66
            $middleware
67
        );
68
    }
69
70
    private function buildRestRoutes($routeBaseName, $routeBaseData)
71
    {
72
        // If route has a parameters array defined, take the first defined
73
        // argument as ":id" parameter, and use key as parameter name
74
        // otherwise, default to id => [0-9]*
75
        if (
76
            isset($routeBaseData['parameters']) &&
77
            is_array($routeBaseData['parameters'])
78
        ) {
79
            reset($routeBaseData['parameters']);
80
            $primaryParameterName = key($routeBaseData['parameters']);
81
82
            $routeParameters = dataGet($routeBaseData, 'parameters', []);
83
        } else {
84
            $primaryParameterName = 'id';
85
            $primaryParameterPattern = '[0-9]*';
86
87
            $routeParameters = array_merge(
88
                [$primaryParameterName => $primaryParameterPattern],
89
                dataGet($routeBaseData, 'parameters', [])
90
            );
91
        }
92
93
        $resources = [
94
            'index' => ['method' => ['GET'], 'append' => ''],
95
            'create' => ['method' => ['GET'], 'append' => '/create'],
96
            'store' => ['method' => ['POST', 'OPTIONS'], 'append' => ''],
97
            'show' => [
98
                'method' => ['GET'],
99
                'append' => '/:' . $primaryParameterName
100
            ],
101
            'edit' => [
102
                'method' => ['GET'],
103
                'append' => '/:' . $primaryParameterName . '/edit'
104
            ],
105
            'update' => [
106
                'method' => ['PUT', 'OPTIONS'],
107
                'append' => '/:' . $primaryParameterName
108
            ],
109
            'destroy' => [
110
                'method' => ['DELETE', 'OPTIONS'],
111
                'append' => '/:' . $primaryParameterName
112
            ]
113
        ];
114
115
        foreach ($resources as $name => $definition) {
116
            $routeName = $routeBaseName . '.' . $name;
117
            $routeData = $routeBaseData;
118
            $routeData['method'] = $definition['method'];
119
            $routeData['path'] .= $definition['append'];
120
            $routeData['target'] .= '::' . $name;
121
            $routeData['parameters'] = $routeParameters;
122
123
            $this->buildRoute($routeName, $routeData);
124
        }
125
    }
126
127 9
    private function parseRequest()
128
    {
129 9
        $this->requestUri = Suricate::Request()->getRequestUri();
130 9
        $this->response->setRequestUri($this->requestUri);
131 9
    }
132
133
    public function addRoute(
134
        $routeName,
135
        $routeMethod,
136
        $routePath,
137
        $routeTarget,
138
        $parametersDefinitions,
139
        $middleware = null
140
    ) {
141
        $computedRoutePath =
142
            $this->baseUri != '/' ? $this->baseUri . $routePath : $routePath;
143
        $this->routes[$routeName] = new Route(
144
            $routeName,
145
            $routeMethod,
146
            $computedRoutePath,
147
            Suricate::Request(),
148
            $routeTarget,
149
            $parametersDefinitions,
150
            $middleware
151
        );
152
    }
153
154
    public function addMiddleware(Middleware $middleware)
155
    {
156
        array_unshift($this->appMiddlewares, $middleware);
157
158
        return $this;
159
    }
160
161
    public function getMiddlewares()
162
    {
163
        return $this->appMiddlewares;
164
    }
165
166
    /**
167
     * Get router defined routes
168
     *
169
     * @return array
170
     */
171 1
    public function getRoutes(): array
172
    {
173 1
        return $this->routes;
174
    }
175
176 1
    public function getResponse()
177
    {
178 1
        return $this->response;
179
    }
180
    /**
181
     * Loop through each defined routes, to find good one
182
     * @return null
183
     */
184
    public function doRouting()
185
    {
186
        $hasRoute = false;
187
188
        foreach ($this->routes as $route) {
189
            if ($route->isMatched) {
190
                $hasRoute = true;
191
192
                Suricate::Logger()->debug(
193
                    '[router] Route "' .
194
                        $route->getPath() .
195
                        '" matched, target: ' .
196
                        json_encode($route->target)
197
                );
198
                $result = $route->dispatch(
199
                    $this->response,
200
                    $this->appMiddlewares
201
                );
202
                if ($result === false) {
203
                    break;
204
                }
205
            }
206
        }
207
208
        // No route matched
209
        if (!$hasRoute) {
210
            Suricate::Logger()->debug('[router] No route found');
211
            app()->abort('404');
212
        }
213
214
        $this->response->write();
215
    }
216
}
217