Passed
Push — release/0.4.16 ( 821afa )
by Mathieu
09:30
created

Router::buildRoute()   B

Complexity

Conditions 7
Paths 48

Size

Total Lines 39
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 7
eloc 25
c 3
b 0
f 0
nc 48
nop 2
dl 0
loc 39
rs 8.5866
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
    public function __construct()
19
    {
20
        parent::__construct();
21
22
        $this->routes = [];
23
        $this->response = Suricate::Response();
24
        $this->parseRequest();
25
26
        // Get app base URI, to transform real path before passing to route
27
        $this->baseUri = Suricate::App()->getParameter('base_uri');
28
    }
29
30
    public function configure($parameters = [])
31
    {
32
        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
    }
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
        $routePath = $routeData['path'];
61
        $routePrefix = $this->baseUri;
62
        if (isset($routeData['baseuri'])) {
63
            $routePrefix = $routeData['baseuri'];
64
            if (is_callable($routeData['baseuri'])) {
65
                $routePrefix = $routeData['baseuri']();
66
            }
67
        }
68
69
        // baseUri is / by default, if route in ini file are beginning with a '/',
70
        // strip the double slash here
71
        $computedRoutePath = str_replace('//', '/', $routePrefix . $routePath);
72
73
        $this->addRoute(
74
            $routeName,
75
            $routeMethod,
76
            $computedRoutePath,
77
            $routeTarget,
78
            $parameters,
79
            $middleware
80
        );
81
    }
82
83
    private function buildRestRoutes($routeBaseName, $routeBaseData)
84
    {
85
        // If route has a parameters array defined, take the first defined
86
        // argument as ":id" parameter, and use key as parameter name
87
        // otherwise, default to id => [0-9]*
88
        if (
89
            isset($routeBaseData['parameters']) &&
90
            is_array($routeBaseData['parameters'])
91
        ) {
92
            reset($routeBaseData['parameters']);
93
            $primaryParameterName = key($routeBaseData['parameters']);
94
95
            $routeParameters = dataGet($routeBaseData, 'parameters', []);
96
        } else {
97
            $primaryParameterName = 'id';
98
            $primaryParameterPattern = '[0-9]*';
99
100
            $routeParameters = array_merge(
101
                [$primaryParameterName => $primaryParameterPattern],
102
                dataGet($routeBaseData, 'parameters', [])
103
            );
104
        }
105
106
        $resources = [
107
            'index' => ['method' => ['GET'], 'append' => ''],
108
            'create' => ['method' => ['GET'], 'append' => '/create'],
109
            'store' => ['method' => ['POST', 'OPTIONS'], 'append' => ''],
110
            'show' => [
111
                'method' => ['GET'],
112
                'append' => '/:' . $primaryParameterName
113
            ],
114
            'edit' => [
115
                'method' => ['GET'],
116
                'append' => '/:' . $primaryParameterName . '/edit'
117
            ],
118
            'update' => [
119
                'method' => ['PUT', 'OPTIONS'],
120
                'append' => '/:' . $primaryParameterName
121
            ],
122
            'destroy' => [
123
                'method' => ['DELETE', 'OPTIONS'],
124
                'append' => '/:' . $primaryParameterName
125
            ]
126
        ];
127
128
        foreach ($resources as $name => $definition) {
129
            $routeName = $routeBaseName . '.' . $name;
130
            $routeData = $routeBaseData;
131
            $routeData['method'] = $definition['method'];
132
            $routeData['path'] .= $definition['append'];
133
            $routeData['target'] .= '::' . $name;
134
            $routeData['parameters'] = $routeParameters;
135
136
            $this->buildRoute($routeName, $routeData);
137
        }
138
    }
139
140
    private function parseRequest()
141
    {
142
        $this->requestUri = Suricate::Request()->getRequestUri();
143
        $this->response->setRequestUri($this->requestUri);
144
    }
145
146
    public function addRoute(
147
        $routeName,
148
        $routeMethod,
149
        $routePath,
150
        $routeTarget,
151
        $parametersDefinitions,
152
        $middleware = null
153
    ) {
154
        $this->routes[$routeName] = new Route(
155
            $routeName,
156
            $routeMethod,
157
            $routePath,
158
            Suricate::Request(),
159
            $routeTarget,
160
            $parametersDefinitions,
161
            $middleware
162
        );
163
    }
164
165
    public function addMiddleware(Middleware $middleware)
166
    {
167
        array_unshift($this->appMiddlewares, $middleware);
168
169
        return $this;
170
    }
171
172
    public function getMiddlewares()
173
    {
174
        return $this->appMiddlewares;
175
    }
176
177
    /**
178
     * Get router defined routes
179
     *
180
     * @return array
181
     */
182
    public function getRoutes(): array
183
    {
184
        return $this->routes;
185
    }
186
187
    public function getResponse()
188
    {
189
        return $this->response;
190
    }
191
    /**
192
     * Loop through each defined routes, to find good one
193
     * @return void
194
     */
195
    public function doRouting()
196
    {
197
        $hasRoute = false;
198
199
        foreach ($this->routes as $route) {
200
            if ($route->isMatched) {
201
                $hasRoute = true;
202
203
                Suricate::Logger()->debug(
204
                    '[router] Route "' .
205
                        $route->getPath() .
206
                        '" matched, target: ' .
207
                        json_encode($route->target)
208
                );
209
                $result = $route->dispatch(
210
                    $this->response,
211
                    $this->appMiddlewares
212
                );
213
                if ($result === false) {
214
                    break;
215
                }
216
            }
217
        }
218
219
        // No route matched
220
        if (!$hasRoute) {
221
            Suricate::Logger()->debug('[router] No route found');
222
            app()->abort('404');
223
        }
224
225
        $this->response->write();
226
    }
227
}
228