Completed
Push — master ( 3d7e33...391d5f )
by Arman
24s queued 20s
created

Route::add()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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