Passed
Pull Request — master (#178)
by
unknown
04:06
created

Route::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
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 Quantum\Loader\Setup;
24
use ReflectionException;
25
use Closure;
26
27
/**
28
 * Route Class
29
 * @package Quantum\Router
30
 */
31
class Route
32
{
33
34
    /**
35
     * Current module name
36
     * @var string
37
     */
38
    private $moduleName;
39
40
    /**
41
     * Module options
42
     * @var array
43
     */
44
    private $moduleOptions = [];
45
46
    /**
47
     * Identifies the group middleware
48
     * @var bool
49
     */
50
    private $isGroupMiddlewares;
51
52
    /**
53
     * Identifies the group
54
     * @var boolean
55
     */
56
    private $isGroup = false;
57
58
    /**
59
     * Current group name
60
     * @var string
61
     */
62
    private $currentGroupName = null;
63
64
    /**
65
     * Current route
66
     * @var array
67
     */
68
    private $currentRoute = [];
69
70
    /**
71
     * Virtual routes
72
     * @var array
73
     */
74
    private $virtualRoutes = [];
75
76
	/**
77
	 * @param array $module
78
	 * @throws ConfigException
79
	 * @throws DiException
80
	 * @throws ReflectionException
81
	 */
82
    public function __construct(array $module)
83
    {
84
        $this->virtualRoutes['*'] = [];
85
        $this->moduleName = key($module);
86
        $this->moduleOptions = $module[key($module)];
87
88
		if (config()->has('resource_cache') && !config()->has('view_cache')){
89
			config()->import(new Setup('config', 'view_cache'));
90
		}
91
    }
92
93
	/**
94
	 * @param string $route
95
	 * @param string $method
96
	 * @param ...$params
97
	 * @return $this
98
	 * @throws ConfigException
99
	 * @throws DatabaseException
100
	 * @throws DiException
101
	 * @throws LangException
102
	 * @throws ReflectionException
103
	 * @throws SessionException
104
	 */
105
    public function add(string $route, string $method, ...$params): Route
106
    {
107
        $this->currentRoute = [
108
            'route' => !empty($this->moduleOptions['prefix']) ? $this->moduleOptions['prefix'] . '/' . $route : $route,
109
            'prefix' => $this->moduleOptions['prefix'],
110
            'method' => $method,
111
            'module' => $this->moduleName
112
        ];
113
114
	    if ($this->canSetCacheToCurrentRoute()){
115
		    $this->currentRoute['cache_settings'] = [
116
			    'shouldCache' => true,
117
			    'ttl' => config()->get('view_cache.ttl', 300),
118
		    ];
119
	    }
120
121
        if (is_callable($params[0])) {
122
            $this->currentRoute['callback'] = $params[0];
123
        } else {
124
            $this->currentRoute['controller'] = $params[0];
125
            $this->currentRoute['action'] = $params[1];
126
        }
127
128
        if ($this->currentGroupName) {
129
            $this->currentRoute['group'] = $this->currentGroupName;
130
            $this->virtualRoutes[$this->currentGroupName][] = $this->currentRoute;
131
        } else {
132
            $this->isGroup = false;
133
            $this->isGroupMiddlewares = false;
134
            $this->virtualRoutes['*'][] = $this->currentRoute;
135
        }
136
137
        return $this;
138
    }
139
140
	/**
141
	 * @param string $route
142
	 * @param ...$params
143
	 * @return $this
144
	 * @throws ConfigException
145
	 * @throws DatabaseException
146
	 * @throws DiException
147
	 * @throws LangException
148
	 * @throws ReflectionException
149
	 * @throws SessionException
150
	 */
151
    public function get(string $route, ...$params): Route
152
    {
153
        return $this->add($route, 'GET', ...$params);
154
    }
155
156
	/**
157
	 * @param string $route
158
	 * @param ...$params
159
	 * @return $this
160
	 * @throws ConfigException
161
	 * @throws DatabaseException
162
	 * @throws DiException
163
	 * @throws LangException
164
	 * @throws ReflectionException
165
	 * @throws SessionException
166
	 */
167
    public function post(string $route, ...$params): Route
168
    {
169
        return $this->add($route, 'POST', ...$params);
170
    }
171
172
    /**
173
     * Starts a named group of routes
174
     * @param string $groupName
175
     * @param Closure $callback
176
     * @return Route
177
     */
178
    public function group(string $groupName, Closure $callback): Route
179
    {
180
        $this->currentGroupName = $groupName;
181
182
        $this->isGroup = true;
183
        $this->isGroupMiddlewares = false;
184
        $callback($this);
185
        $this->isGroupMiddlewares = true;
186
        $this->currentGroupName = null;
187
188
        return $this;
189
    }
190
191
    /**
192
     * Adds middlewares to routes and route groups
193
     * @param array $middlewares
194
     * @return Route
195
     */
196
    public function middlewares(array $middlewares = []): Route
197
    {
198
        if (!$this->isGroup) {
199
            end($this->virtualRoutes['*']);
200
            $lastKey = key($this->virtualRoutes['*']);
201
            $this->assignMiddlewaresToRoute($this->virtualRoutes['*'][$lastKey], $middlewares);
202
            return $this;
203
        }
204
205
        end($this->virtualRoutes);
206
        $lastKeyOfFirstRound = key($this->virtualRoutes);
207
208
        if (!$this->isGroupMiddlewares) {
209
            end($this->virtualRoutes[$lastKeyOfFirstRound]);
210
            $lastKeyOfSecondRound = key($this->virtualRoutes[$lastKeyOfFirstRound]);
211
            $this->assignMiddlewaresToRoute($this->virtualRoutes[$lastKeyOfFirstRound][$lastKeyOfSecondRound], $middlewares);
212
            return $this;
213
        }
214
215
        foreach ($this->virtualRoutes[$lastKeyOfFirstRound] as &$route) {
216
            $this->assignMiddlewaresToRoute($route, $middlewares);
217
        }
218
219
        return $this;
220
    }
221
222
	/**
223
	 * @param bool $shouldCache
224
	 * @param int|null $ttl
225
	 * @return $this
226
	 * @throws ConfigException
227
	 * @throws DatabaseException
228
	 * @throws DiException
229
	 * @throws LangException
230
	 * @throws ReflectionException
231
	 * @throws SessionException
232
	 */
233
	public function cacheable(bool $shouldCache, int $ttl = null): Route
234
	{
235
		if (empty(session()->getId())){
236
			return $this;
237
		}
238
239
		if (!$ttl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ttl of type integer|null is loosely compared to false; 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...
240
			$ttl = config()->get('view_cache.ttl', 300);
241
		}
242
243
		if (!$this->isGroup){
244
			end($this->virtualRoutes['*']);
245
			$lastKey = key($this->virtualRoutes['*']);
246
247
			if (!$shouldCache && key_exists('cache_settings', $this->virtualRoutes['*'][$lastKey])) {
248
				unset($this->virtualRoutes['*'][$lastKey]['cache_settings']);
249
			}else{
250
				$this->assignCacheToCurrentRoute($this->virtualRoutes['*'][$lastKey], $shouldCache, $ttl);
0 ignored issues
show
Bug introduced by
It seems like $ttl can also be of type null; however, parameter $ttl of Quantum\Router\Route::assignCacheToCurrentRoute() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

250
				$this->assignCacheToCurrentRoute($this->virtualRoutes['*'][$lastKey], $shouldCache, /** @scrutinizer ignore-type */ $ttl);
Loading history...
251
			}
252
253
			return $this;
254
		}
255
256
		end($this->virtualRoutes);
257
		$lastKeyOfFirstRound = key($this->virtualRoutes);
258
259
		foreach ($this->virtualRoutes[$lastKeyOfFirstRound] as &$route) {
260
			if (!$shouldCache && key_exists('cache_settings', $route)) {
261
				unset($route['cache_settings']);
262
			}else{
263
				$this->assignCacheToCurrentRoute($route, $shouldCache, $ttl);
264
			}
265
		}
266
267
		return $this;
268
	}
269
270
    /**
271
     * Sets a unique name for a route
272
     * @param string $name
273
     * @return Route
274
     * @throws RouteException
275
     */
276
    public function name(string $name): Route
277
    {
278
        if (empty($this->currentRoute)) {
279
            throw RouteException::nameBeforeDefinition();
280
        }
281
282
        if ($this->isGroupMiddlewares) {
283
            throw RouteException::nameOnGroup();
284
        }
285
286
        foreach ($this->virtualRoutes as &$virtualRoute) {
287
            foreach ($virtualRoute as &$route) {
288
                if (isset($route['name']) && $route['name'] == $name) {
289
                    throw RouteException::nonUniqueName();
290
                }
291
292
                if ($route['route'] == $this->currentRoute['route']) {
293
                    $route['name'] = $name;
294
                }
295
            }
296
        }
297
298
        return $this;
299
    }
300
301
    /**
302
     * Gets the run-time routes
303
     * @return array
304
     */
305
    public function getRuntimeRoutes(): array
306
    {
307
        $runtimeRoutes = [];
308
        foreach ($this->virtualRoutes as $virtualRoute) {
309
            foreach ($virtualRoute as $route) {
310
                $runtimeRoutes[] = $route;
311
            }
312
        }
313
        return $runtimeRoutes;
314
    }
315
316
    /**
317
     * Gets the virtual routes
318
     * @return array
319
     */
320
    public function getVirtualRoutes(): array
321
    {
322
        return $this->virtualRoutes;
323
    }
324
325
    /**
326
     * Assigns middlewares to the route
327
     * @param array $route
328
     * @param array $middlewares
329
     */
330
    private function assignMiddlewaresToRoute(array &$route, array $middlewares)
331
    {
332
        if (!key_exists('middlewares', $route)) {
333
            $route['middlewares'] = $middlewares;
334
        } else {
335
            $middlewares = array_reverse($middlewares);
336
337
            foreach ($middlewares as $middleware) {
338
                array_unshift($route['middlewares'], $middleware);
339
            }
340
        }
341
    }
342
343
	/**
344
	 * @param array $route
345
	 * @param bool $shouldCache
346
	 * @param int $ttl
347
	 * @return void
348
	 */
349
	private function assignCacheToCurrentRoute(array &$route, bool $shouldCache, int $ttl)
350
	{
351
		$route['cache_settings'] = [
352
			'shouldCache' => $shouldCache,
353
			'ttl' => $ttl,
354
		];
355
	}
356
357
	/**
358
	 * @return bool
359
	 * @throws ConfigException
360
	 * @throws DatabaseException
361
	 * @throws DiException
362
	 * @throws LangException
363
	 * @throws SessionException
364
	 * @throws ReflectionException
365
	 */
366
	private function canSetCacheToCurrentRoute(): bool
367
	{
368
		return config()->get('resource_cache') &&
369
			!empty(session()->getId()) &&
370
			!empty($this->moduleOptions) &&
371
			!empty($this->moduleOptions['cacheable']);
372
	}
373
}
374