Cancelled
Push — master ( be3788...06fa12 )
by Nazar
03:16
created

Controller::controller_router_handler_internal()   C

Complexity

Conditions 8
Paths 11

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 17
nc 11
nop 4
dl 0
loc 22
ccs 16
cts 16
cp 1
crap 8
rs 6.6037
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 2
			$controller_class::{"{$method_name}_$unknown_method_handler"}($Request, $Response);
84 2
			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