Passed
Pull Request — master (#182)
by Arman
06:43 queued 02:40
created

Route::getVirtualRoutes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.5
13
 */
14
15
namespace Quantum\Router;
16
17
use Quantum\Libraries\Database\Exceptions\DatabaseException;
18
use Quantum\Libraries\Session\SessionException;
19
use Quantum\Libraries\Config\ConfigException;
20
use Quantum\Exceptions\RouteException;
21
use Quantum\Exceptions\DiException;
22
use ReflectionException;
23
use Closure;
24
25
/**
26
 * Route Class
27
 * @package Quantum\Router
28
 */
29
class Route
30
{
31
32
    /**
33
     * Current module name
34
     * @var string
35
     */
36
    private $moduleName;
37
38
    /**
39
     * Module options
40
     * @var array
41
     */
42
    private $moduleOptions;
43
44
    /**
45
     * Identifies the group middleware
46
     * @var bool
47
     */
48
    private $isGroupMiddlewares;
49
50
    /**
51
     * Identifies the group
52
     * @var boolean
53
     */
54
    private $isGroup = false;
55
56
    /**
57
     * Current group name
58
     * @var string
59
     */
60
    private $currentGroupName = null;
61
62
    /**
63
     * Current route
64
     * @var array
65
     */
66
    private $currentRoute = [];
67
68
    /**
69
     * Virtual routes
70
     * @var array
71
     */
72
    private $virtualRoutes = [];
73
74
    /**
75
     * @param array $module
76
     */
77
    public function __construct(array $module)
78
    {
79
        $this->virtualRoutes['*'] = [];
80
        $this->moduleName = key($module);
81
        $this->moduleOptions = $module[$this->moduleName];
82
    }
83
84
    /**
85
     * @param string $route
86
     * @param string $method
87
     * @param ...$params
88
     * @return $this
89
     */
90
    public function add(string $route, string $method, ...$params): Route
91
    {
92
        $this->currentRoute = [
93
            'route' => !empty($this->moduleOptions['prefix']) ? $this->moduleOptions['prefix'] . '/' . $route : $route,
94
            'prefix' => $this->moduleOptions['prefix'] ?? '',
95
            'method' => $method,
96
            'module' => $this->moduleName,
97
        ];
98
99
        if (isset($this->moduleOptions['cacheable'])) {
100
            $this->currentRoute['cache_settings']['shouldCache'] = (bool)$this->moduleOptions['cacheable'];
101
        }
102
103
        if (is_callable($params[0])) {
104
            $this->currentRoute['callback'] = $params[0];
105
        } else {
106
            $this->currentRoute['controller'] = $params[0];
107
            $this->currentRoute['action'] = $params[1];
108
        }
109
110
        if ($this->currentGroupName) {
111
            $this->currentRoute['group'] = $this->currentGroupName;
112
            $this->virtualRoutes[$this->currentGroupName][] = $this->currentRoute;
113
        } else {
114
            $this->isGroup = false;
115
            $this->isGroupMiddlewares = false;
116
            $this->virtualRoutes['*'][] = $this->currentRoute;
117
        }
118
119
        return $this;
120
    }
121
122
    /**
123
     * @param string $route
124
     * @param ...$params
125
     * @return $this
126
     */
127
    public function get(string $route, ...$params): Route
128
    {
129
        return $this->add($route, 'GET', ...$params);
130
    }
131
132
    /**
133
     * @param string $route
134
     * @param ...$params
135
     * @return $this
136
     */
137
    public function post(string $route, ...$params): Route
138
    {
139
        return $this->add($route, 'POST', ...$params);
140
    }
141
142
    /**
143
     * Starts a named group of routes
144
     * @param string $groupName
145
     * @param Closure $callback
146
     * @return Route
147
     */
148
    public function group(string $groupName, Closure $callback): Route
149
    {
150
        $this->currentGroupName = $groupName;
151
152
        $this->isGroup = true;
153
        $this->isGroupMiddlewares = false;
154
        $callback($this);
155
        $this->isGroupMiddlewares = true;
156
        $this->currentGroupName = null;
157
158
        return $this;
159
    }
160
161
    /**
162
     * Adds middlewares to routes and route groups
163
     * @param array $middlewares
164
     * @return Route
165
     */
166
    public function middlewares(array $middlewares = []): Route
167
    {
168
        if (!$this->isGroup) {
169
            end($this->virtualRoutes['*']);
170
            $lastKey = key($this->virtualRoutes['*']);
171
            $this->assignMiddlewaresToRoute($this->virtualRoutes['*'][$lastKey], $middlewares);
172
            return $this;
173
        }
174
175
        end($this->virtualRoutes);
176
        $lastKeyOfFirstRound = key($this->virtualRoutes);
177
178
        if (!$this->isGroupMiddlewares) {
179
            end($this->virtualRoutes[$lastKeyOfFirstRound]);
180
            $lastKeyOfSecondRound = key($this->virtualRoutes[$lastKeyOfFirstRound]);
181
            $this->assignMiddlewaresToRoute($this->virtualRoutes[$lastKeyOfFirstRound][$lastKeyOfSecondRound], $middlewares);
182
            return $this;
183
        }
184
185
        foreach ($this->virtualRoutes[$lastKeyOfFirstRound] as &$route) {
186
            $this->assignMiddlewaresToRoute($route, $middlewares);
187
        }
188
189
        return $this;
190
    }
191
192
    /**
193
     * @param bool $shouldCache
194
     * @param int|null $ttl
195
     * @return $this
196
     * @throws ConfigException
197
     * @throws DatabaseException
198
     * @throws DiException
199
     * @throws ReflectionException
200
     * @throws SessionException
201
     */
202
    public function cacheable(bool $shouldCache, ?int $ttl = null): Route
203
    {
204
        if (empty(session()->getId())) {
205
            return $this;
206
        }
207
208
        if (!$this->isGroup) {
209
            end($this->virtualRoutes['*']);
210
            $lastKey = key($this->virtualRoutes['*']);
211
212
            $this->virtualRoutes['*'][$lastKey]['cache_settings']['shouldCache'] = $shouldCache;
213
214
            if ($shouldCache && $ttl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ttl of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
215
                $this->virtualRoutes['*'][$lastKey]['cache_settings']['ttl'] = $ttl;
216
            }
217
218
            return $this;
219
        }
220
221
        end($this->virtualRoutes);
222
        $lastKeyOfFirstRound = key($this->virtualRoutes);
223
224
        foreach ($this->virtualRoutes[$lastKeyOfFirstRound] as &$route) {
225
            $route['cache_settings']['shouldCache'] = $shouldCache;
226
227
            if ($shouldCache && $ttl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ttl of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
228
                $route['cache_settings']['ttl'] = $ttl;
229
            }
230
        }
231
232
        return $this;
233
    }
234
235
    /**
236
     * Sets a unique name for a route
237
     * @param string $name
238
     * @return Route
239
     * @throws RouteException
240
     */
241
    public function name(string $name): Route
242
    {
243
        if (empty($this->currentRoute)) {
244
            throw RouteException::nameBeforeDefinition();
245
        }
246
247
        if ($this->isGroupMiddlewares) {
248
            throw RouteException::nameOnGroup();
249
        }
250
251
        foreach ($this->virtualRoutes as &$virtualRoute) {
252
            foreach ($virtualRoute as &$route) {
253
                if (isset($route['name']) && $route['name'] == $name) {
254
                    throw RouteException::nonUniqueName();
255
                }
256
257
                if ($route['route'] == $this->currentRoute['route']) {
258
                    $route['name'] = $name;
259
                }
260
            }
261
        }
262
263
        return $this;
264
    }
265
266
    /**
267
     * Gets the run-time routes
268
     * @return array
269
     */
270
    public function getRuntimeRoutes(): array
271
    {
272
        $runtimeRoutes = [];
273
        foreach ($this->virtualRoutes as $virtualRoute) {
274
            foreach ($virtualRoute as $route) {
275
                $runtimeRoutes[] = $route;
276
            }
277
        }
278
        return $runtimeRoutes;
279
    }
280
281
    /**
282
     * Gets the virtual routes
283
     * @return array
284
     */
285
    public function getVirtualRoutes(): array
286
    {
287
        return $this->virtualRoutes;
288
    }
289
290
    /**
291
     * Assigns middlewares to the route
292
     * @param array $route
293
     * @param array $middlewares
294
     */
295
    private function assignMiddlewaresToRoute(array &$route, array $middlewares)
296
    {
297
        if (!key_exists('middlewares', $route)) {
298
            $route['middlewares'] = $middlewares;
299
        } else {
300
            $middlewares = array_reverse($middlewares);
301
302
            foreach ($middlewares as $middleware) {
303
                array_unshift($route['middlewares'], $middleware);
304
            }
305
        }
306
    }
307
}
308