Completed
Push — master ( b11528...c19e75 )
by Nazar
04:50
created

Route::analyze_route_path()   D

Complexity

Conditions 11
Paths 192

Size

Total Lines 80
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 53
CRAP Score 11.0058

Importance

Changes 0
Metric Value
cc 11
eloc 56
nc 192
nop 1
dl 0
loc 80
ccs 53
cts 55
cp 0.9636
crap 11.0058
rs 4.9629
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
		$url_language = Language::instance()->url_language($route);
180
		/**
181
		 * Obtaining page path in form of array
182
		 */
183 28
		$route = explode('/', $route);
184 28
		if ($url_language) {
185 4
			array_shift($route);
186
		}
187 28
		if (@$route[0] === '') {
188 22
			array_shift($route);
189
		}
190 28
		$cli_path     = $this->cli && @strtolower($route[0]) == 'cli';
191 28
		$admin_path   = @strtolower($route[0]) == 'admin';
192 28
		$api_path     = @strtolower($route[0]) == 'api';
193 28
		$regular_path = !($cli_path || $admin_path || $api_path);
194 28
		$path_prefix  = '';
195 28
		$home_page    = false;
196
		/**
197
		 * If url is cli, admin or API page - set corresponding variables to corresponding path prefix
198
		 */
199 28
		if (!$regular_path) {
200 8
			$path_prefix = array_shift($route).'/';
201
		}
202
		/**
203
		 * Module detection
204
		 */
205 28
		$current_module = $this->determine_page_module($route, $home_page, $admin_path, $regular_path);
206 28
		list($route_path, $route_ids) = $this->split_route($route);
207 28
		$rc             = implode('/', $route);
208 28
		$old_rc         = $rc;
209 28
		$old_route      = $route;
210 28
		$old_route_path = $route_path;
211 28
		$old_route_ids  = $route_ids;
212 28
		Event::instance()->fire(
213 28
			'System/Request/routing_replace/after',
214
			[
215 28
				'rc'             => &$rc, // TODO: Deprecated key, remove in 6.x
216 28
				'route'          => &$route,
217 28
				'route_path'     => &$route_path,
218 28
				'route_ids'      => &$route_ids,
219 28
				'cli_path'       => &$cli_path,
220 28
				'admin_path'     => &$admin_path,
221 28
				'api_path'       => &$api_path,
222 28
				'regular_path'   => &$regular_path,
223 28
				'current_module' => &$current_module,
224 28
				'home_page'      => &$home_page
225
			]
226
		);
227
		// TODO: Deprecated, remove in 6.x
228 28
		if ($rc != $old_rc) {
229
			$route = explode('/', $rc);
230
			list($route_path, $route_ids) = $this->split_route($route);
231
		}
232 28
		if ($route != $old_route && $route_path == $old_route_path && $route_ids == $old_route_ids) {
233 2
			list($route_path, $route_ids) = $this->split_route($route);
234
		}
235
		return [
236 28
			'route'           => $route,
237 28
			'route_path'      => $route_path,
238 28
			'route_ids'       => $route_ids,
239 28
			'path_normalized' => trim(
240 28
				"$path_prefix$current_module/$rc",
241 28
				'/'
242
			),
243 28
			'cli_path'        => $cli_path,
244 28
			'admin_path'      => $admin_path,
245 28
			'api_path'        => $api_path,
246 28
			'regular_path'    => $regular_path,
247 28
			'current_module'  => $current_module,
248 28
			'home_page'       => $home_page
249
		];
250
	}
251
	/**
252
	 * @param array $route
253
	 *
254
	 * @return array[] Key `0` contains array of paths, key `1` contains array of identifiers
255
	 */
256 28
	protected function split_route ($route) {
257 28
		$route_path = [];
258 28
		$route_ids  = [];
259
		/**
260
		 * Separate numeric and other parts of route
261
		 */
262 28
		foreach ($route as $item) {
263 6
			if (is_numeric($item)) {
264 2
				$route_ids[] = $item;
265
			} else {
266 6
				$route_path[] = $item;
267
			}
268
		}
269 28
		return [$route_path, $route_ids];
270
	}
271
	/**
272
	 * @param Config $Config
273
	 * @param string $path_normalized
274
	 *
275
	 * @throws ExitException
276
	 */
277 28
	protected function handle_redirect ($Config, $path_normalized) {
278
		/**
279
		 * Redirection processing
280
		 */
281 28
		if (strpos($path_normalized, 'System/redirect/') === 0) {
282 2
			if ($this->is_referer_local($Config)) {
283 2
				Response::instance()->redirect(
284 2
					substr($path_normalized, 16),
285 2
					301
286
				);
287 2
				throw new ExitException;
288
			} else {
289 2
				throw new ExitException(400);
290
			}
291
		}
292 28
	}
293
	/**
294
	 * Check whether referer is local
295
	 *
296
	 * @param Config $Config
297
	 *
298
	 * @return bool
299
	 */
300 2
	protected function is_referer_local ($Config) {
301 2
		$referer = $this->header('referer');
302 2
		if (!$referer) {
303 2
			return false;
304
		}
305 2
		list($referer_protocol, $referer_host) = explode('://', $referer);
306 2
		$referer_host = explode('/', $referer_host)[0];
307 2
		foreach ($Config->core['url'] as $address) {
308 2
			list($protocol, $urls) = explode('://', $address, 2);
309 2
			if ($protocol === $referer_protocol) {
310 2
				foreach (explode(';', $urls) as $url) {
311 2
					if (mb_strpos("$referer_host/", "$url/") === 0) {
312 2
						return true;
313
					}
314
				}
315
			}
316
		}
317 2
		return false;
318
	}
319
	/**
320
	 * Determine module of current page based on page path and system configuration
321
	 *
322
	 * @param array $rc
323
	 * @param bool  $home_page
324
	 * @param bool  $admin_path
325
	 * @param bool  $regular_path
326
	 *
327
	 * @return string
328
	 */
329 28
	protected function determine_page_module (&$rc, &$home_page, $admin_path, $regular_path) {
330 28
		$Config           = Config::instance();
331 28
		$modules          = $this->get_modules($Config, (bool)$admin_path);
332 28
		$module_specified = @$rc[0];
333 28
		if ($module_specified) {
334 6
			if (in_array($module_specified, $modules)) {
335 6
				return array_shift($rc);
336
			}
337 2
			$L = Language::instance();
338 2
			foreach ($modules as $module) {
339 2
				if ($module_specified == path($L->$module)) {
340 2
					array_shift($rc);
341 2
					return $module;
342
				}
343
			}
344
		}
345 24
		if (!$regular_path || $module_specified) {
346 4
			$current_module = 'System';
347
		} else {
348 22
			$current_module = $Config->core['default_module'];
349 22
			$home_page      = true;
350
		}
351 24
		return $current_module;
352
	}
353
	/**
354
	 * Get array of modules
355
	 *
356
	 * @param Config $Config
357
	 * @param bool   $admin_path
358
	 *
359
	 * @return string[]
360
	 */
361 28
	protected function get_modules ($Config, $admin_path) {
362 28
		$modules = array_filter(
363 28
			$Config->components['modules'],
364 28
			function ($module_data) use ($admin_path) {
365
				/**
366
				 * Skip uninstalled modules and modules that are disabled (on all pages except admin pages)
367
				 */
368
				return
369
					(
370 28
						$admin_path &&
371 4
						$module_data['active'] == Config\Module_Properties::DISABLED
372
					) ||
373 28
					$module_data['active'] == Config\Module_Properties::ENABLED;
374 28
			}
375
		);
376 28
		return array_keys($modules);
377
	}
378
	/**
379
	 * Get route part by index
380
	 *
381
	 * @param int $index
382
	 *
383
	 * @return int|null|string
384
	 */
385 2
	public function route ($index) {
386 2
		return @$this->route[$index];
387
	}
388
	/**
389
	 * Get route path part by index
390
	 *
391
	 * @param int $index
392
	 *
393
	 * @return null|string
394
	 */
395 2
	public function route_path ($index) {
396 2
		return @$this->route_path[$index];
397
	}
398
	/**
399
	 * Get route ids part by index
400
	 *
401
	 * @param int $index
402
	 *
403
	 * @return int|null
404
	 */
405 2
	public function route_ids ($index) {
406 2
		return @$this->route_ids[$index];
407
	}
408
}
409