Passed
Push — master ( 570be6...1a3368 )
by Jean-Christophe
01:28
created

ControllerParser::scanParam()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 8
nop 8
dl 0
loc 17
rs 9.4888
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Ubiquity\cache\parser;
4
5
use Ubiquity\orm\parser\Reflexion;
6
use Ubiquity\utils\base\UString;
7
use Ubiquity\annotations\router\RouteAnnotation;
8
use Ubiquity\cache\ClassUtils;
9
use Ubiquity\utils\base\UArray;
10
11
/**
12
 * Scans a controller to detect routes defined by annotations 
13
 * @author jcheron <[email protected]>
14
 * @version 1.0.2
15
 */
16
class ControllerParser {
17
	use ControllerParserPathTrait;
18
	
19
	private $controllerClass;
20
	private $mainRouteClass;
21
	private $routesMethods=[ ];
22
	private $rest=false;
23
	private static $excludeds=[ "__construct","isValid","initialize","finalize","onInvalidControl","loadView","forward","redirectToRoute" ];
24
25
	public function parse($controllerClass) {
26
		$automated=false;
27
		$inherited=false;
28
		$this->controllerClass=$controllerClass;
29
		$restAnnotsClass=[];
30
		$reflect=new \ReflectionClass($controllerClass);
31
		if (!$reflect->isAbstract() && $reflect->isSubclassOf("Ubiquity\controllers\Controller")) {
32
			$instance=new $controllerClass();
33
			try{
34
				$annotsClass=Reflexion::getAnnotationClass($controllerClass, "@route");
35
				$restAnnotsClass=Reflexion::getAnnotationClass($controllerClass, "@rest");
36
			}catch (\Exception $e){
37
				//When controllerClass generates an exception
38
			}
39
			$this->rest=\sizeof($restAnnotsClass) > 0;
40
			if (\sizeof($annotsClass) > 0) {
41
				$this->mainRouteClass=$annotsClass[0];
42
				$inherited=$this->mainRouteClass->inherited;
43
				$automated=$this->mainRouteClass->automated;
44
			}
45
			$methods=Reflexion::getMethods($instance, \ReflectionMethod::IS_PUBLIC);
46
			$this->parseMethods($methods, $controllerClass, $inherited, $automated);
47
		}
48
	}
49
	
50
	private function parseMethods($methods,$controllerClass,$inherited,$automated){
51
		foreach ( $methods as $method ) {
52
			if ($method->getDeclaringClass()->getName() === $controllerClass || $inherited) {
53
				try{
54
					$annots=Reflexion::getAnnotationsMethod($controllerClass, $method->name, ["@route","@get","@post"]);
55
					if (sizeof($annots)>0) {
56
						foreach ( $annots as $annot ) {
57
							$this->parseAnnot($annot, $method);
58
						}
59
						$this->routesMethods[$method->name]=[ "annotations" => $annots,"method" => $method ];
60
					} else {
61
						if ($automated) {
62
							if ($method->class !== 'Ubiquity\\controllers\\Controller' && \array_search($method->name, self::$excludeds) === false && !UString::startswith($method->name, "_"))
63
								$this->routesMethods[$method->name]=[ "annotations" => $this->generateRouteAnnotationFromMethod($method),"method" => $method ];
64
						}
65
					}
66
				}catch(\Exception $e){
67
					//When controllerClass generates an exception
68
				}
69
			}
70
		}
71
	}
72
	
73
	private function parseAnnot(&$annot,$method){
74
		if (UString::isNull($annot->path)) {
75
			$newAnnot=$this->generateRouteAnnotationFromMethod($method);
76
			$annot->path=$newAnnot[0]->path;
77
		}else{
78
			$annot->path=$this->parseMethodPath($method, $annot->path);
79
		}
80
	}
81
82
	private function generateRouteAnnotationFromMethod(\ReflectionMethod $method) {
83
		$annot=new RouteAnnotation();
84
		$annot->path=self::getPathFromMethod($method);
85
		return [ $annot ];
86
	}
87
88
	public function asArray() {
89
		$result=[ ];
90
		$prefix="";
91
		$httpMethods=false;
92
		if ($this->mainRouteClass) {
93
			if (isset($this->mainRouteClass->path))
94
				$prefix=$this->mainRouteClass->path;
95
			if (isset($this->mainRouteClass->methods)) {
96
				$httpMethods=$this->mainRouteClass->methods;
97
				if ($httpMethods !== null) {
98
					if (\is_string($httpMethods))
99
						$httpMethods=[ $httpMethods ];
100
				}
101
			}
102
		}
103
		foreach ( $this->routesMethods as $method => $arrayAnnotsMethod ) {
104
			$routeAnnotations=$arrayAnnotsMethod["annotations"];
105
106
			foreach ( $routeAnnotations as $routeAnnotation ) {
107
				$params=[ "path" => $routeAnnotation->path,"methods" => $routeAnnotation->methods,"name" => $routeAnnotation->name,"cache" => $routeAnnotation->cache,"duration" => $routeAnnotation->duration,"requirements" => $routeAnnotation->requirements,"priority"=>$routeAnnotation->priority ];
108
				self::parseRouteArray($result, $this->controllerClass, $params, $arrayAnnotsMethod["method"], $method, $prefix, $httpMethods);
109
			}
110
		}
111
		uasort($result, function ($item1, $item2) {
112
			return UArray::getRecursive($item2,"priority",0) <=> UArray::getRecursive($item1,"priority",0);
113
		});
114
		UArray::removeRecursive($result,"priority");
115
		self::minifyRoutes($result);
116
		return $result;
117
	}
118
	
119
	public static function minifyRoutes(&$routes){
120
		foreach ($routes as &$route){
121
			self::minifyRoute($route);
122
		}
123
	}
124
	
125
	private static function minifyRoute(&$route){
126
		if(isset($route['name']) && !is_string($route['name'])){
127
			unset($route['name']);
128
		}
129
		if(isset($route['parameters']) && sizeof($route['parameters'])==0){
130
			unset($route['parameters']);
131
		}
132
		if((isset($route['cache']) && $route['cache']==false) || (array_key_exists('cache', $route) && $route['cache']==null)){
133
			unset($route['cache']);
134
			unset($route['duration']);
135
		}
136
		if((isset($route['duration']) && !is_numeric($route['duration'])) || (array_key_exists('duration', $route) && $route['duration']==null)){
137
			unset($route['duration']);
138
		}
139
		if(isset($route['priority'])){
140
			unset($route['priority']);
141
		}
142
	}
143
144
	public static function parseRouteArray(&$result, $controllerClass, $routeArray, \ReflectionMethod $method, $methodName, $prefix="", $httpMethods=NULL) {
145
		if (!isset($routeArray["path"])) {
146
			$routeArray["path"]=self::getPathFromMethod($method);
147
		}
148
		$pathParameters=self::addParamsPath($routeArray["path"], $method, $routeArray["requirements"]);
149
		$name=$routeArray["name"];
150
		if (!isset($name)) {
151
			$name=UString::cleanAttribute(ClassUtils::getClassSimpleName($controllerClass) . "_" . $methodName);
152
		}
153
		$cache=$routeArray["cache"];
154
		$duration=$routeArray["duration"];
155
		$path=$pathParameters["path"];
156
		$parameters=$pathParameters["parameters"];
157
		$priority=$routeArray["priority"];
158
		$path=self::cleanpath($prefix, $path);
159
		if (isset($routeArray["methods"]) && \is_array($routeArray["methods"])) {
160
			self::createRouteMethod($result, $controllerClass, $path, $routeArray["methods"], $methodName, $parameters, $name, $cache, $duration,$priority);
161
		} elseif (\is_array($httpMethods)) {
162
			self::createRouteMethod($result, $controllerClass, $path, $httpMethods, $methodName, $parameters, $name, $cache, $duration,$priority);
163
		} else {
164
			$result[$path]=[ "controller" => $controllerClass,"action" => $methodName,"parameters" => $parameters,"name" => $name,"cache" => $cache,"duration" => $duration,"priority"=>$priority];
165
		}
166
	}
167
168
	public static function addParamsPath($path, \ReflectionMethod $method, $requirements) {
169
		$parameters=[ ];
170
		$hasOptional=false;
171
		preg_match_all('@\{(\.\.\.|\~)?(.+?)\}@s', $path, $matches);
172
		if (isset($matches[2]) && \sizeof($matches[2]) > 0) {
173
			$path=\preg_quote($path);
174
			$params=Reflexion::getMethodParameters($method);
175
			$index=0;
176
			foreach ( $matches[2] as $paramMatch ) {
177
				$find=\array_search($paramMatch, $params);
178
				if ($find !== false) {
179
					$requirement='.+?';
180
					if (isset($requirements[$paramMatch])) {
181
						$requirement=$requirements[$paramMatch];
182
					}
183
					self::scanParam($parameters, $hasOptional, $matches, $index, $paramMatch, $find, $path, $requirement);
184
				} else {
185
					throw new \Exception("{$paramMatch} is not a parameter of the method " . $method->name);
186
				}
187
				$index++;
188
			}
189
		}
190
		if ($hasOptional)
191
			$path.="/(.*?)";
192
		return [ "path" => $path,"parameters" => $parameters ];
193
	}
194
195
	private static function scanParam(&$parameters, &$hasOptional, $matches, $index, $paramMatch, $find, &$path, $requirement) {
196
		$toReplace=true;
197
		if (isset($matches[1][$index])) {
198
			if ($matches[1][$index] === "...") {
199
				$parameters[]="*";
200
				$path=\str_replace("\{\.\.\." . $paramMatch . "\}", "(.*?)", $path);
201
				$toReplace=false;
202
			} elseif ($matches[1][$index] === "~") {
203
				$parameters[]="~" . $find;
204
				$path=\str_replace("\{~" . $paramMatch . "\}", "", $path);
205
				$hasOptional=true;
206
				$toReplace=false;
207
			}
208
		} 
209
		if($toReplace) {
210
			$parameters[]=$find;
211
			$path=\str_replace("\{" . $paramMatch . "\}", "({$requirement})", $path);
212
		}
213
	}
214
215
	private static function createRouteMethod(&$result, $controllerClass, $path, $httpMethods, $method, $parameters, $name, $cache, $duration,$priority) {
216
		foreach ( $httpMethods as $httpMethod ) {
217
			$result[$path][$httpMethod]=[ "controller" => $controllerClass,"action" => $method,"parameters" => $parameters,"name" => $name,"cache" => $cache,"duration" => $duration,"priority"=>$priority ];
218
		}
219
	}
220
221
	public function isRest() {
222
		return $this->rest;
223
	}
224
}
225