Failed Conditions
Branch refactor/kernels (3a00e4)
by Atanas
01:41
created

Router::routeCondition()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 10
ccs 0
cts 0
cp 0
crap 12
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package   WPEmerge
4
 * @author    Atanas Angelov <[email protected]>
5
 * @copyright 2018 Atanas Angelov
6
 * @license   https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0
7
 * @link      https://wpemerge.com/
8
 */
9
10
namespace WPEmerge\Routing;
11
12
use WPEmerge\Exceptions\ConfigurationException;
13
use WPEmerge\Helpers\Handler;
14
use WPEmerge\Requests\RequestInterface;
15
use WPEmerge\Routing\Conditions\ConditionFactory;
16
use WPEmerge\Routing\Conditions\ConditionInterface;
17
use WPEmerge\Support\Arr;
18
19
/**
20
 * Provide routing for site requests (i.e. all non-api requests).
21
 */
22
class Router implements HasRoutesInterface {
23
	use HasRoutesTrait;
24
25
	/**
26
	 * Condition factory.
27
	 *
28
	 * @var ConditionFactory
29
	 */
30
	protected $condition_factory = null;
31
32
	/**
33
	 * Group stack.
34
	 *
35
	 * @var array<array<string, mixed>>
36
	 */
37
	protected $group_stack = [];
38
39
	/**
40
	 * Current active route.
41
	 *
42
	 * @var RouteInterface
43
	 */
44
	protected $current_route = null;
45
46
	/**
47
	 * Constructor.
48
	 *
49
	 * @codeCoverageIgnore
50
	 * @param ConditionFactory $condition_factory
51
	 */
52
	public function __construct( ConditionFactory $condition_factory ) {
53
		$this->condition_factory = $condition_factory;
54
	}
55
56
	/**
57
	 * Get the current route.
58
	 *
59
	 * @return RouteInterface
60
	 */
61
	public function getCurrentRoute() {
62
		return $this->current_route;
63 1
	}
64 1
65
	/**
66
	 * Set the current route.
67
	 *
68
	 * @param  RouteInterface
69
	 * @return void
70
	 */
71
	public function setCurrentRoute( RouteInterface $current_route ) {
72
		$this->current_route = $current_route;
73 1
	}
74 1
75 1
	/**
76
	 * Merge the methods attribute combining values.
77
	 *
78
	 * @param  array<string> $old
79
	 * @param  array<string> $new
80
	 * @return array<string>
81
	 */
82
	public function mergeMethodsAttribute( $old, $new ) {
83
		return array_merge( $old, $new );
84
	}
85
86
	/**
87
	 * Merge the condition attribute.
88
	 *
89
	 * @param  string|ConditionInterface $old
90
	 * @param  string|ConditionInterface $new
91
	 * @return ConditionInterface|string
92
	 */
93
	public function mergeConditionAttribute( $old, $new ) {
94
		try {
95
			$condition = $this->condition_factory->merge( $old, $new );
96
		} catch ( ConfigurationException $e ) {
97
			throw new ConfigurationException( 'Route condition is not a valid route string or condition.' );
98
		}
99
100
		return $condition !== null ? $condition : '';
101
	}
102
103
	/**
104
	 * Merge the middleware attribute combining values.
105
	 *
106
	 * @param  array<string> $old
107
	 * @param  array<string> $new
108
	 * @return array<string>
109
	 */
110
	public function mergeMiddlewareAttribute( $old, $new ) {
111
		return array_merge( $old, $new );
112
	}
113
114
	/**
115
	 * Merge the namespace attribute taking the latest value.
116
	 *
117
	 * @param  string $old
118
	 * @param  string $new
119
	 * @return string
120
	 */
121
	public function mergeNamespaceAttribute( $old, $new ) {
122 1
		return ! empty( $new ) ? $new : $old;
123 1
	}
124
125 1
	/**
126
	 * Merge the handler attribute taking the latest value.
127
	 *
128 1
	 * @param  string|\Closure $old
129
	 * @param  string|\Closure $new
130
	 * @return string|\Closure
131 1
	 */
132 1
	public function mergeHandlerAttribute( $old, $new ) {
133
		return ! empty( $new ) ? $new : $old;
134
	}
135
136
	/**
137 3
	 * Merge attributes into route.
138 3
	 *
139
	 * @param  array<string, mixed> $old
140 2
	 * @param  array<string, mixed> $new
141 2
	 * @return array<string, mixed>
142 1
	 */
143
	public function mergeAttributes( $old, $new ) {
144 1
		$attributes = [
145
			'methods' => $this->mergeMethodsAttribute(
146 2
				(array) Arr::get( $old, 'methods', [] ),
147
				(array) Arr::get( $new, 'methods', [] )
148
			),
149
150
			'condition' => $this->mergeConditionAttribute(
151
				Arr::get( $old, 'condition', '' ),
0 ignored issues
show
Bug introduced by
It seems like WPEmerge\Support\Arr::get($old, 'condition', '') can also be of type array<string,mixed>; however, parameter $old of WPEmerge\Routing\Router::mergeConditionAttribute() does only seem to accept WPEmerge\Routing\Conditi...nditionInterface|string, 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

151
				/** @scrutinizer ignore-type */ Arr::get( $old, 'condition', '' ),
Loading history...
152 2
				Arr::get( $new, 'condition', '' )
0 ignored issues
show
Bug introduced by
It seems like WPEmerge\Support\Arr::get($new, 'condition', '') can also be of type array<string,mixed>; however, parameter $new of WPEmerge\Routing\Router::mergeConditionAttribute() does only seem to accept WPEmerge\Routing\Conditi...nditionInterface|string, 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

152
				/** @scrutinizer ignore-type */ Arr::get( $new, 'condition', '' )
Loading history...
153 2
			),
154 2
155
			'middleware' => $this->mergeMiddlewareAttribute(
156 2
				(array) Arr::get( $old, 'middleware', [] ),
157 1
				(array) Arr::get( $new, 'middleware', [] )
158 1
			),
159 1
160 1
			'namespace' => $this->mergeNamespaceAttribute(
161 1
				Arr::get( $old, 'namespace', '' ),
1 ignored issue
show
Bug introduced by
It seems like WPEmerge\Support\Arr::get($old, 'namespace', '') can also be of type array<string,mixed>; however, parameter $old of WPEmerge\Routing\Router::mergeNamespaceAttribute() does only seem to accept string, 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

161
				/** @scrutinizer ignore-type */ Arr::get( $old, 'namespace', '' ),
Loading history...
162
				Arr::get( $new, 'namespace', '' )
1 ignored issue
show
Bug introduced by
It seems like WPEmerge\Support\Arr::get($new, 'namespace', '') can also be of type array<string,mixed>; however, parameter $new of WPEmerge\Routing\Router::mergeNamespaceAttribute() does only seem to accept string, 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

162
				/** @scrutinizer ignore-type */ Arr::get( $new, 'namespace', '' )
Loading history...
163 2
			),
164 2
165
			'handler' => $this->mergeNamespaceAttribute(
166 2
				Arr::get( $old, 'handler', '' ),
167
				Arr::get( $new, 'handler', '' )
168 2
			),
169
		];
170 2
171 2
		return $attributes;
172 2
	}
173 2
174
	/**
175 2
	 * Get the top group from the stack.
176
	 *
177
	 * @codeCoverageIgnore
178
	 * @return array<string, mixed>
179
	 */
180
	protected function getGroup() {
181
		return Arr::last( $this->group_stack, null, [] );
182
	}
183
184 1
	/**
185
	 * Add a group to the group stack, merging all previous attributes.
186
	 *
187
	 * @codeCoverageIgnore
188 1
	 * @param array<string, mixed> $group
189
	 * @return void
190
	 */
191
	protected function pushGroup( $group ) {
192
		$this->group_stack[] = $this->mergeAttributes( $this->getGroup(), $group );
193
	}
194
195
	/**
196
	 * Remove last group from the group stack.
197 2
	 *
198 2
	 * @codeCoverageIgnore
199
	 * @return void
200 2
	 */
201 2
	protected function popGroup() {
202 1
		array_pop( $this->group_stack );
203 1
	}
204
205 1
	/**
206
	 * Create a route group.
207 1
	 *
208
	 * @codeCoverageIgnore
209
	 * @param array<string, mixed> $attributes
210
	 * @param \Closure|string      $routes Closure or path to file.
211
	 * @return void
212
	 */
213
	public function group( $attributes, $routes ) {
214
		$this->pushGroup( $attributes );
215
216
		if ( is_string( $routes ) ) {
217
			// @codeCoverageIgnore
218
			require_once $routes;
219
		} else {
220
			$routes();
221
		}
222
223
		$this->popGroup();
224
	}
225
226
	/**
227
	 * Make a route condition.
228
	 *
229
	 * @param  mixed              $condition
230
	 * @return ConditionInterface
231
	 */
232
	protected function routeCondition( $condition ) {
233
		if ( $condition === '' ) {
234
			throw new ConfigurationException( 'No route condition specified. Did you miss to call url() or where()?' );
235
		}
236
237
		if ( ! $condition instanceof ConditionInterface ) {
238
			$condition = $this->condition_factory->make( $condition );
239
		}
240
241
		return $condition;
242
	}
243
244
	/**
245
	 * Make a route handler.
246
	 *
247
	 * @codeCoverageIgnore
248
	 * @param  string|\Closure|null $handler
249
	 * @param  string               $namespace
250
	 * @return Handler
251
	 */
252
	protected function routeHandler( $handler, $namespace ) {
253
		return new Handler( $handler, '', $namespace );
254
	}
255
256
	/**
257
	 * Make a route.
258
	 *
259
	 * @param  array<string, mixed> $attributes
260
	 * @return RouteInterface
261
	 */
262
	public function route( $attributes ) {
263
		$attributes = $this->mergeAttributes( $this->getGroup(), $attributes );
264
265
		$methods = Arr::get( $attributes, 'methods', [] );
266
		$condition = Arr::get( $attributes, 'condition', null );
267
		$handler = Arr::get( $attributes, 'handler', '' );
268
		$namespace = Arr::get( $attributes, 'namespace', '' );
269
		$middleware = Arr::get( $attributes, 'middleware', [] );
270
271
		$condition = $this->routeCondition( $condition );
272
		$handler = $this->routeHandler( $handler, $namespace );
2 ignored issues
show
Bug introduced by
It seems like $namespace can also be of type array<string,mixed>; however, parameter $namespace of WPEmerge\Routing\Router::routeHandler() does only seem to accept string, 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

272
		$handler = $this->routeHandler( $handler, /** @scrutinizer ignore-type */ $namespace );
Loading history...
Bug introduced by
It seems like $handler can also be of type array<string,mixed>; however, parameter $handler of WPEmerge\Routing\Router::routeHandler() does only seem to accept Closure|null|string, 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

272
		$handler = $this->routeHandler( /** @scrutinizer ignore-type */ $handler, $namespace );
Loading history...
273
274
		$route = new Route( $methods, $condition, $handler );
275
276
		$route->middleware( $middleware );
277
278
		return $route;
279
	}
280
281
	/**
282
	 * Assign and return the first satisfied route (if any) as the current one for the given request.
283
	 *
284
	 * @param  RequestInterface $request
285
	 * @return RouteInterface
286
	 */
287
	public function execute( $request ) {
288
		$routes = $this->getRoutes();
289
290
		foreach ( $routes as $route ) {
291
			if ( $route->isSatisfied( $request ) ) {
292
				$this->setCurrentRoute( $route );
293
				return $route;
294
			}
295
		}
296
297
		return null;
298
	}
299
}
300