Completed
Push — master ( 62549b...430ab9 )
by Nazar
04:10
created

Router::execute_router()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 2
Metric Value
c 4
b 1
f 2
dl 0
loc 9
rs 9.6666
cc 2
eloc 7
nc 2
nop 0
1
<?php
2
/**
3
 * @package   CleverStyle CMS
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\App;
9
use
10
	cs\ExitException,
11
	cs\Request,
12
	cs\Response,
13
	cs\App\Router\CLI,
14
	cs\App\Router\Controller,
15
	cs\App\Router\Files;
16
17
/**
18
 * @property string[] $controller_path Path that will be used by controller to render page
19
 */
20
trait Router {
21
	use
22
		CLI,
23
		Controller,
24
		Files;
25
	/**
26
	 * Path that will be used by controller to render page
27
	 *
28
	 * @var string[]
29
	 */
30
	protected $controller_path;
31
	/**
32
	 * Execute router
33
	 *
34
	 * Depending on module, files-based or controller-based router might be used
35
	 *
36
	 * @throws ExitException
37
	 */
38
	protected function execute_router () {
39
		$Request = Request::instance();
40
		$this->check_and_normalize_route($Request);
41
		if (file_exists("$this->working_directory/Controller.php")) {
42
			$this->controller_router($Request);
43
		} else {
44
			$this->files_router($Request);
45
		}
46
	}
47
	/**
48
	 * Normalize `cs\Request::$route_path` and fill `cs\App::$controller_path`
49
	 *
50
	 * @param Request $Request
51
	 *
52
	 * @throws ExitException
53
	 */
54
	protected function check_and_normalize_route ($Request) {
55
		if (!file_exists("$this->working_directory/index.json")) {
56
			return;
57
		}
58
		$structure = file_get_json("$this->working_directory/index.json");
59
		if (!$structure) {
60
			return;
61
		}
62
		for ($nesting_level = 0; $structure; ++$nesting_level) {
63
			/**
64
			 * Next level of routing path
65
			 */
66
			$path = @$Request->route_path[$nesting_level];
67
			/**
68
			 * If path not specified - take first from structure
69
			 */
70
			$this->check_and_normalize_route_internal($path, $structure, $Request->cli_path || $Request->api_path);
71
			$Request->route_path[$nesting_level] = $path;
72
			/**
73
			 * Fill paths array intended for controller's usage
74
			 */
75
			$this->controller_path[] = $path;
76
			/**
77
			 * If nested structure is not available - we'll not go into next iteration of this cycle
78
			 */
79
			$structure = @$structure[$path];
80
		}
81
	}
82
	/**
83
	 * @param string $path
84
	 * @param array  $structure
85
	 * @param bool   $cli_or_api_path
86
	 *
87
	 * @throws ExitException
88
	 */
89
	protected function check_and_normalize_route_internal (&$path, $structure, $cli_or_api_path) {
90
		/**
91
		 * If path not specified - take first from structure
92
		 */
93
		if (!$path) {
94
			$path = isset($structure[0]) ? $structure[0] : array_keys($structure)[0];
95
			/**
96
			 * We need exact paths for CLI and API request (or `_` ending if available) and less strict mode for other cases that allows go deeper automatically
97
			 */
98
			if ($path !== '_' && $cli_or_api_path) {
99
				throw new ExitException(404);
100
			}
101
		} elseif (!isset($structure[$path]) && !in_array($path, $structure)) {
102
			throw new ExitException(404);
103
		}
104
		/** @noinspection PhpUndefinedMethodInspection */
105
		if (!$this->check_permission($path)) {
106
			throw new ExitException(403);
107
		}
108
	}
109
	/**
110
	 * If HTTP method handler not found we generate either `501 Not Implemented` if other methods are supported or `404 Not Found` if handlers for others
111
	 * methods also doesn't exist
112
	 *
113
	 * @param string[] $available_methods
114
	 * @param string   $request_method
115
	 * @param Request  $Request
116
	 *
117
	 * @throws ExitException
118
	 */
119
	protected function handler_not_found ($available_methods, $request_method, $Request) {
120
		if ($available_methods) {
121
			if ($Request->cli_path) {
122
				$this->print_cli_structure($Request->path);
123
				if ($request_method !== 'cli') {
124
					throw new ExitException(501);
125
				}
126
			} else {
127
				Response::instance()->header('Allow', implode(', ', $available_methods));
128
				if ($request_method !== 'options') {
129
					throw new ExitException(501);
130
				}
131
			}
132
		} else {
133
			throw new ExitException(404);
134
		}
135
	}
136
}
137