Completed
Push — master ( 3e5c1c...c3a169 )
by Nazar
03:59
created

Router::init_router()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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