Completed
Push — master ( 9f103c...960d95 )
by Nazar
04:37
created

Route::analyze_route_path()   C

Complexity

Conditions 14
Paths 96

Size

Total Lines 81
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 54
CRAP Score 14.0285

Importance

Changes 3
Bugs 1 Features 2
Metric Value
cc 14
eloc 58
c 3
b 1
f 2
nc 96
nop 1
dl 0
loc 81
ccs 54
cts 57
cp 0.9474
crap 14.0285
rs 5.0042

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