Route::cacheable()   B
last analyzed

Complexity

Conditions 8
Paths 6

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 14
nc 6
nop 2
dl 0
loc 28
rs 8.4444
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 3.0.0
13
 */
14
15
namespace Quantum\Router;
16
17
use Quantum\Config\Exceptions\ConfigException;
18
use Quantum\Router\Exceptions\RouteException;
19
use Quantum\App\Exceptions\BaseException;
20
use Quantum\Di\Exceptions\DiException;
21
use ReflectionException;
22
use Closure;
23
24
/**
25
 * Route Class
26
 * @package Quantum\Router
27
 */
28
class Route
29
{
30
    /**
31
     * Current module name
32
     * @var string
33
     */
34
    private $moduleName;
35
36
    /**
37
     * Module options
38
     * @var array
39
     */
40
    private $moduleOptions;
41
42
    /**
43
     * Identifies the group middleware
44
     * @var bool
45
     */
46
    private $isGroupMiddlewares;
47
48
    /**
49
     * Identifies the group
50
     * @var boolean
51
     */
52
    private $isGroup = false;
53
54
    /**
55
     * Current group name
56
     * @var string|null
57
     */
58
    private $currentGroupName = null;
59
60
    /**
61
     * Current route
62
     * @var array
63
     */
64
    private $currentRoute = [];
65
66
    /**
67
     * Virtual routes
68
     * @var array
69
     */
70
    private $virtualRoutes = [];
71
72
    /**
73
     * @param array $module
74
     */
75
    public function __construct(array $module)
76
    {
77
        $this->virtualRoutes['*'] = [];
78
        $this->moduleName = key($module);
79
        $this->moduleOptions = $module[$this->moduleName];
80
    }
81
82
    /**
83
     * @param string $route
84
     * @param string $method
85
     * @param ...$params
86
     * @return $this
87
     */
88
    public function add(string $route, string $method, ...$params): Route
89
    {
90
        $this->currentRoute = [
91
            'route' => empty($this->moduleOptions['prefix']) ? $route : $this->moduleOptions['prefix'] . '/' . $route,
92
            'prefix' => $this->moduleOptions['prefix'] ?? '',
93
            'method' => $method,
94
            'module' => $this->moduleName,
95
        ];
96
97
        if (isset($this->moduleOptions['cacheable'])) {
98
            $this->currentRoute['cache_settings']['shouldCache'] = (bool)$this->moduleOptions['cacheable'];
99
        }
100
101
        if (is_callable($params[0])) {
102
            $this->currentRoute['callback'] = $params[0];
103
        } else {
104
            $this->currentRoute['controller'] = $params[0];
105
            $this->currentRoute['action'] = $params[1];
106
        }
107
108
        if ($this->currentGroupName) {
109
            $this->currentRoute['group'] = $this->currentGroupName;
110
            $this->virtualRoutes[$this->currentGroupName][] = $this->currentRoute;
111
        } else {
112
            $this->isGroup = false;
113
            $this->isGroupMiddlewares = false;
114
            $this->virtualRoutes['*'][] = $this->currentRoute;
115
        }
116
117
        return $this;
118
    }
119
120
    /**
121
     * @param string $route
122
     * @param ...$params
123
     * @return $this
124
     */
125
    public function get(string $route, ...$params): Route
126
    {
127
        return $this->add($route, 'GET', ...$params);
128
    }
129
130
    /**
131
     * @param string $route
132
     * @param ...$params
133
     * @return $this
134
     */
135
    public function post(string $route, ...$params): Route
136
    {
137
        return $this->add($route, 'POST', ...$params);
138
    }
139
140
    /**
141
     * @param string $route
142
     * @param ...$params
143
     * @return $this
144
     */
145
    public function put(string $route, ...$params): Route
146
    {
147
        return $this->add($route, 'PUT', ...$params);
148
    }
149
150
    /**
151
     * @param string $route
152
     * @param ...$params
153
     * @return $this
154
     */
155
    public function delete(string $route, ...$params): Route
156
    {
157
        return $this->add($route, 'DELETE', ...$params);
158
    }
159
160
    /**
161
     * Starts a named group of routes
162
     * @param string $groupName
163
     * @param Closure $callback
164
     * @return Route
165
     */
166
    public function group(string $groupName, Closure $callback): Route
167
    {
168
        $this->currentGroupName = $groupName;
169
170
        $this->isGroup = true;
171
        $this->isGroupMiddlewares = false;
172
        $callback($this);
173
        $this->isGroupMiddlewares = true;
174
        $this->currentGroupName = null;
175
176
        return $this;
177
    }
178
179
    /**
180
     * Adds middlewares to routes and route groups
181
     * @param array $middlewares
182
     * @return Route
183
     */
184
    public function middlewares(array $middlewares = []): Route
185
    {
186
        if (!$this->isGroup) {
187
            $lastKey = array_key_last($this->virtualRoutes['*']);
188
            $this->assignMiddlewaresToRoute($this->virtualRoutes['*'][$lastKey], $middlewares);
189
            return $this;
190
        }
191
        $lastKeyOfFirstRound = array_key_last($this->virtualRoutes);
192
193
        if (!$this->isGroupMiddlewares) {
194
            $lastKeyOfSecondRound = array_key_last($this->virtualRoutes[$lastKeyOfFirstRound]);
195
            $this->assignMiddlewaresToRoute($this->virtualRoutes[$lastKeyOfFirstRound][$lastKeyOfSecondRound], $middlewares);
196
            return $this;
197
        }
198
199
        foreach ($this->virtualRoutes[$lastKeyOfFirstRound] as &$route) {
200
            $this->assignMiddlewaresToRoute($route, $middlewares);
201
        }
202
203
        return $this;
204
    }
205
206
    /**
207
     * @param bool $shouldCache
208
     * @param int|null $ttl
209
     * @return $this
210
     * @throws ConfigException
211
     * @throws DiException
212
     * @throws ReflectionException
213
     * @throws BaseException
214
     */
215
    public function cacheable(bool $shouldCache, ?int $ttl = null): Route
216
    {
217
        if (empty(session()->getId())) {
218
            return $this;
219
        }
220
221
        if (!$this->isGroup) {
222
            $lastKey = array_key_last($this->virtualRoutes['*']);
223
224
            $this->virtualRoutes['*'][$lastKey]['cache_settings']['shouldCache'] = $shouldCache;
225
226
            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...
227
                $this->virtualRoutes['*'][$lastKey]['cache_settings']['ttl'] = $ttl;
228
            }
229
230
            return $this;
231
        }
232
        $lastKeyOfFirstRound = array_key_last($this->virtualRoutes);
233
234
        foreach ($this->virtualRoutes[$lastKeyOfFirstRound] as &$route) {
235
            $route['cache_settings']['shouldCache'] = $shouldCache;
236
237
            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...
238
                $route['cache_settings']['ttl'] = $ttl;
239
            }
240
        }
241
242
        return $this;
243
    }
244
245
    /**
246
     * Sets a unique name for a route
247
     * @param string $name
248
     * @return Route
249
     * @throws RouteException
250
     */
251
    public function name(string $name): Route
252
    {
253
        if ($this->currentRoute === []) {
254
            throw RouteException::nameBeforeDefinition();
255
        }
256
257
        if ($this->isGroupMiddlewares) {
258
            throw RouteException::nameOnGroup();
259
        }
260
261
        foreach ($this->virtualRoutes as &$virtualRoute) {
262
            foreach ($virtualRoute as &$route) {
263
                if (isset($route['name']) && $route['name'] == $name) {
264
                    throw RouteException::nonUniqueName();
265
                }
266
267
                if ($route['route'] == $this->currentRoute['route']) {
268
                    $route['name'] = $name;
269
                }
270
            }
271
        }
272
273
        return $this;
274
    }
275
276
    /**
277
     * Gets the run-time routes
278
     * @return array
279
     */
280
    public function getRuntimeRoutes(): array
281
    {
282
        $runtimeRoutes = [];
283
        foreach ($this->virtualRoutes as $virtualRoute) {
284
            foreach ($virtualRoute as $route) {
285
                $runtimeRoutes[] = $route;
286
            }
287
        }
288
        return $runtimeRoutes;
289
    }
290
291
    /**
292
     * Gets the virtual routes
293
     * @return array
294
     */
295
    public function getVirtualRoutes(): array
296
    {
297
        return $this->virtualRoutes;
298
    }
299
300
    /**
301
     * Assigns middlewares to the route
302
     * @param array $route
303
     * @param array $middlewares
304
     */
305
    private function assignMiddlewaresToRoute(array &$route, array $middlewares)
306
    {
307
        if (!array_key_exists('middlewares', $route)) {
308
            $route['middlewares'] = $middlewares;
309
        } else {
310
            $middlewares = array_reverse($middlewares);
311
312
            foreach ($middlewares as $middleware) {
313
                array_unshift($route['middlewares'], $middleware);
314
            }
315
        }
316
    }
317
}
318