Completed
Push — master ( 960d95...99bb18 )
by Nazar
04:42
created

Controller::controller_router()   C

Complexity

Conditions 7
Paths 24

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 24
nop 1
dl 0
loc 24
ccs 17
cts 17
cp 1
crap 7
rs 6.7272
c 0
b 0
f 0
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\App\Router;
9
use
10
	cs\Page,
11
	cs\Response;
12
13
/**
14
 * @property string[] $controller_path Path that will be used by controller to render page
15
 */
16
trait Controller {
17
	/**
18
	 * Call methods necessary for module page rendering
19
	 *
20
	 * @param \cs\Request $Request
21
	 *
22
	 * @throws \cs\ExitException
23
	 */
24 8
	protected function controller_router ($Request) {
25 8
		$suffix = '';
26 8
		if ($Request->cli_path) {
27 2
			$suffix = '\\cli';
28 8
		} elseif ($Request->admin_path) {
29 2
			$suffix = '\\admin';
30 8
		} elseif ($Request->api_path) {
31 6
			$suffix = '\\api';
32
		}
33 8
		$controller_class = class_exists("cs\\custom\\modules\\$Request->current_module$suffix\\Controller")
34 2
			? "cs\\custom\\modules\\$Request->current_module$suffix\\Controller"
35 8
			: "cs\\modules\\$Request->current_module$suffix\\Controller";
36 8
		foreach ($this->controller_path as $index => $path) {
37
			/**
38
			 * Starting from index 2 (first is always `index`) we need to maintain underscore-separated string that includes all paths from index 1 and till
39
			 * current
40
			 */
41 8
			if ($index > 1) {
42 2
				$path = implode('_', array_slice($this->controller_path, 1, $index));
43
			}
44 8
			$next_exists = isset($this->controller_path[$index + 1]);
45 8
			$this->controller_router_handler($Request, $controller_class, $path, !$next_exists);
46
		}
47 8
	}
48
	/**
49
	 * Call methods that corresponds for specific paths in URL
50
	 *
51
	 * @param \cs\Request $Request
52
	 * @param string      $controller_class
53
	 * @param string      $method_name
54
	 * @param bool        $required
55
	 *
56
	 * @throws \cs\ExitException
57
	 */
58 8
	protected function controller_router_handler ($Request, $controller_class, $method_name, $required = true) {
59 8
		$method_name = str_replace('.', '_', $method_name);
60 8
		$this->controller_router_handler_internal($Request, $controller_class, $method_name, $required);
61 8
	}
62
	/**
63
	 * @param \cs\Request $Request
64
	 * @param string      $controller_class
65
	 * @param string      $method_name
66
	 * @param bool        $required
67
	 *
68
	 * @throws \cs\ExitException
69
	 */
70 8
	protected function controller_router_handler_internal ($Request, $controller_class, $method_name, $required) {
71 8
		$Response = Response::instance();
72 8
		$found    = $this->controller_router_handler_internal_execute($controller_class, $method_name, $Request, $Response);
73 8
		if (!$Request->cli_path && !$Request->api_path) {
74 4
			return;
75
		}
76 6
		$request_method = strtolower($Request->method);
77 6
		$found          = $this->controller_router_handler_internal_execute($controller_class, $method_name.'_'.$request_method, $Request, $Response) || $found;
78 6
		if ($found || !$required) {
79 6
			return;
80
		}
81 2
		$unknown_method_handler = $Request->api_path ? 'options' : 'cli';
82 2
		if (method_exists($controller_class, "{$method_name}_$unknown_method_handler")) {
83
			$controller_class->{"{$method_name}_$unknown_method_handler"}($Request, $Response);
0 ignored issues
show
Bug introduced by
The method "{$method_name}_{$unknown_method_handler}" cannot be called on $controller_class (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
84
			return;
85
		}
86 2
		$this->handler_not_found(
87 2
			$this->controller_router_available_methods($this->working_directory, $controller_class, $method_name),
88
			$request_method,
89
			$Request
90
		);
91 2
	}
92
	/**
93
	 * @param string $working_directory
94
	 * @param string $controller_class
95
	 * @param string $method_name
96
	 *
97
	 * @return string[]
98
	 */
99 4
	protected function controller_router_available_methods ($working_directory, $controller_class, $method_name) {
100 4
		$structure = file_exists("$working_directory/index.json") ? file_get_json("$working_directory/index.json") : ['index'];
101 4
		$structure = $this->controller_router_available_methods_to_flat_structure($structure);
102 4
		$methods   = array_filter(
103 4
			get_class_methods($controller_class) ?: [],
104 4
			function ($found_method) use ($method_name, $structure) {
105 4
				if (!preg_match("/^{$method_name}_[a-z_]+$/", $found_method)) {
106 4
					return false;
107
				}
108 4
				foreach ($structure as $structure_method) {
109 4
					if (strpos($found_method, $structure_method) === 0 && strpos($method_name, $structure_method) !== 0) {
110 4
						return false;
111
					}
112
				}
113 4
				return true;
114 4
			}
115
		);
116 4
		$methods   = _strtoupper(_substr($methods, strlen($method_name) + 1));
117 4
		natcasesort($methods);
118 4
		return array_values($methods);
119
	}
120
	/**
121
	 * @param array  $structure
122
	 * @param string $prefix
123
	 *
124
	 * @return string[]
125
	 */
126 4
	protected function controller_router_available_methods_to_flat_structure ($structure, $prefix = '') {
127
		// First key in order to avoid warning when `$flat_structure` is empty at `return`
128 4
		$flat_structure = [[]];
129 4
		foreach ($structure as $path => $nested_structure) {
130 4
			if (!is_array($nested_structure)) {
131 4
				$path             = $nested_structure;
132 4
				$nested_structure = [];
133
			}
134 4
			$flat_structure[] = [$prefix.$path];
135 4
			$flat_structure[] = $this->controller_router_available_methods_to_flat_structure($nested_structure, $prefix.$path.'_');
136
		}
137 4
		return array_merge(...$flat_structure);
138
	}
139
	/**
140
	 * @param string      $controller_class
141
	 * @param string      $method_name
142
	 * @param \cs\Request $Request
143
	 * @param Response    $Response
144
	 *
145
	 * @return bool
146
	 */
147 8
	protected function controller_router_handler_internal_execute ($controller_class, $method_name, $Request, $Response) {
148 8
		if (!method_exists($controller_class, $method_name)) {
149 8
			return false;
150
		}
151 6
		$result = $controller_class::$method_name($Request, $Response);
152 6
		if ($result !== null) {
153 2
			Page::instance()->{$Request->api_path ? 'json' : 'content'}($result);
154
		}
155 6
		return true;
156
	}
157
}
158