Completed
Push — master ( 312807...b11528 )
by Nazar
04:22
created

Route::analyze_route_path()   F

Complexity

Conditions 14
Paths 288

Size

Total Lines 83
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 57
CRAP Score 14.0076

Importance

Changes 0
Metric Value
cc 14
eloc 60
nc 288
nop 1
dl 0
loc 83
ccs 57
cts 59
cp 0.9661
crap 14.0076
rs 3.7522
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2015-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\Request;
9
use
10
	cs\Config,
11
	cs\Event,
12
	cs\ExitException,
13
	cs\Language,
14
	cs\Request\Route\Static_files,
15
	cs\Response;
16
17
/**
18
 * @property bool   $cli
19
 * @property string $scheme
20
 * @property string $host
21
 * @property string $path
22
 *
23
 * @method string header(string $name)
24
 */
25
trait Route {
26
	use
27
		Static_files;
28
	/**
29
	 * Current mirror according to configuration
30
	 *
31
	 * @var int
32
	 */
33
	public $mirror_index;
34
	/**
35
	 * Normalized processed representation of relative address, may differ from raw, should be used in most cases
36
	 *
37
	 * @var string
38
	 */
39
	public $path_normalized;
40
	/**
41
	 * Contains parsed route of current page url in form of array without module name and prefixes `admin|api`
42
	 *
43
	 * @var array
44
	 */
45
	public $route;
46
	/**
47
	 * Like `$route` property, but excludes numerical items
48
	 *
49
	 * @var string[]
50
	 */
51
	public $route_path;
52
	/**
53
	 * Like `$route` property, but only includes numerical items (opposite to route_path property)
54
	 *
55
	 * @var int[]
56
	 */
57
	public $route_ids;
58
	/**
59
	 * Request to administration section
60
	 *
61
	 * @var bool
62
	 */
63
	public $admin_path;
64
	/**
65
	 * Request to CLI interface
66
	 *
67
	 * @var bool
68
	 */
69
	public $cli_path;
70
	/**
71
	 * Request to api section
72
	 *
73
	 * @var bool
74
	 */
75
	public $api_path;
76
	/**
77
	 * Request to regular page (not administration section, not API and not CLI)
78
	 *
79
	 * @var bool
80
	 */
81
	public $regular_path;
82
	/**
83
	 * Current module
84
	 *
85
	 * @var string
86
	 */
87
	public $current_module;
88
	/**
89
	 * Home page
90
	 *
91
	 * @var bool
92
	 */
93
	public $home_page;
94
	/**
95
	 * Initialize route based on system configuration, requires `::init_server()` being called first since uses its data
96
	 *
97
	 * @throws ExitException
98
	 */
99 30
	public function init_route () {
100
		/**
101
		 * Serve static files here for early exit
102
		 */
103 30
		$this->serve_static_files();
104 30
		$this->mirror_index    = -1;
105 30
		$this->path_normalized = '';
106 30
		$this->route           = [];
107 30
		$this->route_path      = [];
108 30
		$this->route_ids       = [];
109 30
		$this->cli_path        = false;
110 30
		$this->admin_path      = false;
111 30
		$this->api_path        = false;
112 30
		$this->regular_path    = true;
113 30
		$this->current_module  = '';
114 30
		$this->home_page       = false;
115 30
		if ($this->cli) {
116 4
			$results = $this->analyze_route_path($this->path);
117
		} else {
118 28
			$Config             = Config::instance();
119 28
			$this->mirror_index = $this->determine_current_mirror_index($Config);
120
			/**
121
			 * If match was not found - mirror is not allowed!
122
			 */
123 28
			if ($this->mirror_index === -1) {
124 2
				throw new ExitException("Mirror $this->host not allowed", 400);
125
			}
126 28
			$results = $this->analyze_route_path($this->path);
127 28
			$this->handle_redirect($Config, $results['path_normalized']);
128
		}
129 28
		$this->route           = $results['route'];
130 28
		$this->route_path      = $results['route_path'];
131 28
		$this->route_ids       = $results['route_ids'];
132 28
		$this->path_normalized = $results['path_normalized'];
133 28
		$this->cli_path        = $results['cli_path'];
134 28
		$this->admin_path      = $results['admin_path'];
135 28
		$this->api_path        = $results['api_path'];
136 28
		$this->regular_path    = $results['regular_path'];
137 28
		$this->current_module  = $results['current_module'];
138 28
		$this->home_page       = $results['home_page'];
139 28
	}
140
	/**
141
	 * @param Config $Config
142
	 *
143
	 * @return int
144
	 */
145 28
	protected function determine_current_mirror_index ($Config) {
146
		/**
147
		 * Search for url matching in all mirrors
148
		 */
149 28
		foreach ($Config->core['url'] as $i => $address) {
150 28
			list($scheme, $urls) = explode('://', $address, 2);
151 28
			if ($scheme == $this->scheme) {
152 28
				foreach (explode(';', $urls) as $url) {
153 28
					if (mb_strpos("$this->host/$this->path", "$url/") === 0) {
154 28
						return $i;
155
					}
156
				}
157
			}
158
		}
159 2
		return -1;
160
	}
161
	/**
162
	 * Process raw relative route.
163
	 *
164
	 * As result returns current route in system in form of array, normalized path, detects module path points to, whether this is API call, administration
165
	 * page, or home page whether this is API call, admin page, or home page
166
	 *
167
	 * @param string $path
168
	 *
169
	 * @return array Array contains next elements: `route`, `path_normalized`, `cli_path`, `admin_path`, `api_path`, `current_module`, `home_page`
170
	 */
171 30
	public function analyze_route_path ($path) {
172 30
		$route = trim($path, '/');
173 30
		Event::instance()->fire(
174 30
			'System/Request/routing_replace/before',
175
			[
176 30
				'rc' => &$route
177
			]
178
		);
179 28
		if (Language::instance()->url_language($route)) {
180 4
			$route = explode('/', $route, 2);
181 4
			$route = isset($route[1]) ? $route[1] : '';
182
		}
183
		/**
184
		 * Obtaining page path in form of array
185
		 */
186 28
		$route      = $route ? explode('/', $route) : [];
187 28
		$cli_path   = '';
188 28
		$admin_path = '';
189 28
		$api_path   = '';
190 28
		$home_page  = false;
191
		/**
192
		 * If url is cli, admin or API page - set corresponding variables to corresponding path prefix
193
		 */
194 28
		if ($this->cli && @mb_strtolower($route[0]) == 'cli') {
195 2
			$cli_path = 'cli/';
196 2
			array_shift($route);
197 28
		} elseif (@mb_strtolower($route[0]) == 'admin') {
198 4
			$admin_path = 'admin/';
199 4
			array_shift($route);
200 26
		} elseif (@mb_strtolower($route[0]) == 'api') {
201 6
			$api_path = 'api/';
202 6
			array_shift($route);
203
		}
204 28
		$regular_path = !($cli_path || $admin_path || $api_path);
205
		/**
206
		 * Module detection
207
		 */
208 28
		$current_module = $this->determine_page_module($route, $home_page, $cli_path, $admin_path, $api_path);
209 28
		list($route_path, $route_ids) = $this->split_route($route);
210 28
		$rc             = implode('/', $route);
211 28
		$old_rc         = $rc;
212 28
		$old_route      = $route;
213 28
		$old_route_path = $route_path;
214 28
		$old_route_ids  = $route_ids;
215 28
		Event::instance()->fire(
216 28
			'System/Request/routing_replace/after',
217
			[
218 28
				'rc'             => &$rc, // TODO: Deprecated key, remove in 6.x
219 28
				'route'          => &$route,
220 28
				'route_path'     => &$route_path,
221 28
				'route_ids'      => &$route_ids,
222 28
				'cli_path'       => &$cli_path,
223 28
				'admin_path'     => &$admin_path,
224 28
				'api_path'       => &$api_path,
225 28
				'regular_path'   => $regular_path,
226 28
				'current_module' => &$current_module,
227 28
				'home_page'      => &$home_page
228
			]
229
		);
230
		// TODO: Deprecated, remove in 6.x
231 28
		if ($rc != $old_rc) {
232
			$route = explode('/', $rc);
233
			list($route_path, $route_ids) = $this->split_route($route);
234
		}
235 28
		if ($route != $old_route && $route_path == $old_route_path && $route_ids == $old_route_ids) {
236 2
			list($route_path, $route_ids) = $this->split_route($route);
237
		}
238
		return [
239 28
			'route'           => $route,
240 28
			'route_path'      => $route_path,
241 28
			'route_ids'       => $route_ids,
242 28
			'path_normalized' => trim(
243 28
				"$cli_path$admin_path$api_path$current_module/$rc",
244 28
				'/'
245
			),
246 28
			'cli_path'        => (bool)$cli_path,
247 28
			'admin_path'      => (bool)$admin_path,
248 28
			'api_path'        => (bool)$api_path,
249 28
			'regular_path'    => (bool)$regular_path,
250 28
			'current_module'  => $current_module,
251 28
			'home_page'       => $home_page
252
		];
253
	}
254
	/**
255
	 * @param array $route
256
	 *
257
	 * @return array[] Key `0` contains array of paths, key `1` contains array of identifiers
258
	 */
259 28
	protected function split_route ($route) {
260 28
		$route_path = [];
261 28
		$route_ids  = [];
262
		/**
263
		 * Separate numeric and other parts of route
264
		 */
265 28
		foreach ($route as $item) {
266 6
			if (is_numeric($item)) {
267 2
				$route_ids[] = $item;
268
			} else {
269 6
				$route_path[] = $item;
270
			}
271
		}
272 28
		return [$route_path, $route_ids];
273
	}
274
	/**
275
	 * @param Config $Config
276
	 * @param string $path_normalized
277
	 *
278
	 * @throws ExitException
279
	 */
280 28
	protected function handle_redirect ($Config, $path_normalized) {
281
		/**
282
		 * Redirection processing
283
		 */
284 28
		if (strpos($path_normalized, 'System/redirect/') === 0) {
285 2
			if ($this->is_referer_local($Config)) {
286 2
				Response::instance()->redirect(
287 2
					substr($path_normalized, 16),
288 2
					301
289
				);
290 2
				throw new ExitException;
291
			} else {
292 2
				throw new ExitException(400);
293
			}
294
		}
295 28
	}
296
	/**
297
	 * Check whether referer is local
298
	 *
299
	 * @param Config $Config
300
	 *
301
	 * @return bool
302
	 */
303 2
	protected function is_referer_local ($Config) {
304 2
		$referer = $this->header('referer');
305 2
		if (!$referer) {
306 2
			return false;
307
		}
308 2
		list($referer_protocol, $referer_host) = explode('://', $referer);
309 2
		$referer_host = explode('/', $referer_host)[0];
310 2
		foreach ($Config->core['url'] as $address) {
311 2
			list($protocol, $urls) = explode('://', $address, 2);
312 2
			if ($protocol === $referer_protocol) {
313 2
				foreach (explode(';', $urls) as $url) {
314 2
					if (mb_strpos("$referer_host/", "$url/") === 0) {
315 2
						return true;
316
					}
317
				}
318
			}
319
		}
320 2
		return false;
321
	}
322
	/**
323
	 * Determine module of current page based on page path and system configuration
324
	 *
325
	 * @param array  $rc
326
	 * @param bool   $home_page
327
	 * @param string $cli_path
328
	 * @param string $admin_path
329
	 * @param string $api_path
330
	 *
331
	 * @return string
332
	 */
333 28
	protected function determine_page_module (&$rc, &$home_page, $cli_path, $admin_path, $api_path) {
334 28
		$Config           = Config::instance();
335 28
		$modules          = $this->get_modules($Config, (bool)$admin_path);
336 28
		$module_specified = @$rc[0];
337 28
		if ($module_specified) {
338 6
			if (in_array($module_specified, $modules)) {
339 6
				return array_shift($rc);
340
			}
341 2
			$L = Language::instance();
342 2
			foreach ($modules as $module) {
343 2
				if ($module_specified == path($L->$module)) {
344 2
					array_shift($rc);
345 2
					return $module;
346
				}
347
			}
348
		}
349 24
		if ($cli_path || $admin_path || $api_path || $module_specified) {
350 4
			$current_module = 'System';
351
		} else {
352 22
			$current_module = $Config->core['default_module'];
353 22
			$home_page      = true;
354
		}
355 24
		return $current_module;
356
	}
357
	/**
358
	 * Get array of modules
359
	 *
360
	 * @param Config $Config
361
	 * @param bool   $admin_path
362
	 *
363
	 * @return string[]
364
	 */
365 28
	protected function get_modules ($Config, $admin_path) {
366 28
		$modules = array_filter(
367 28
			$Config->components['modules'],
368 28
			function ($module_data) use ($admin_path) {
369
				/**
370
				 * Skip uninstalled modules and modules that are disabled (on all pages except admin pages)
371
				 */
372
				return
373
					(
374 28
						$admin_path &&
375 4
						$module_data['active'] == Config\Module_Properties::DISABLED
376
					) ||
377 28
					$module_data['active'] == Config\Module_Properties::ENABLED;
378 28
			}
379
		);
380 28
		return array_keys($modules);
381
	}
382
	/**
383
	 * Get route part by index
384
	 *
385
	 * @param int $index
386
	 *
387
	 * @return int|null|string
388
	 */
389 2
	public function route ($index) {
390 2
		return @$this->route[$index];
391
	}
392
	/**
393
	 * Get route path part by index
394
	 *
395
	 * @param int $index
396
	 *
397
	 * @return null|string
398
	 */
399 2
	public function route_path ($index) {
400 2
		return @$this->route_path[$index];
401
	}
402
	/**
403
	 * Get route ids part by index
404
	 *
405
	 * @param int $index
406
	 *
407
	 * @return int|null
408
	 */
409 2
	public function route_ids ($index) {
410 2
		return @$this->route_ids[$index];
411
	}
412
}
413