Passed
Push — master ( 5a27b6...d7396e )
by Jean-Christophe
02:39
created

ControllerParser   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 173
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 119
dl 0
loc 173
rs 8.72
c 0
b 0
f 0
wmc 46

8 Methods

Rating   Name   Duplication   Size   Complexity  
C parse() 0 42 16
A createRouteMethod() 0 3 2
A isRest() 0 2 1
B asArray() 0 28 8
A generateRouteAnnotationFromMethod() 0 4 1
B addParamsPath() 0 25 7
A parseRouteArray() 0 21 6
A scanParam() 0 17 5

How to fix   Complexity   

Complex Class

Complex classes like ControllerParser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ControllerParser, and based on these observations, apply Extract Interface, too.

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 jc
14
 * @version 1.0.1
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=[];$annotsClass=[];
0 ignored issues
show
Unused Code introduced by
The assignment to $annotsClass is dead and can be removed.
Loading history...
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
			foreach ( $methods as $method ) {
47
				if ($method->getDeclaringClass()->getName() === $controllerClass || $inherited) {
48
					try{
49
						$annots=Reflexion::getAnnotationsMethod($controllerClass, $method->name, ["@route","@get","@post"]);
50
						if (sizeof($annots)>0) {
51
							foreach ( $annots as $annot ) {
52
								if (UString::isNull($annot->path)) {
53
									$newAnnot=$this->generateRouteAnnotationFromMethod($method);
54
									$annot->path=$newAnnot[0]->path;
55
								}else{
56
									$annot->path=$this->parseMethodPath($method, $annot->path);
57
								}
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
74
	private function generateRouteAnnotationFromMethod(\ReflectionMethod $method) {
75
		$annot=new RouteAnnotation();
76
		$annot->path=self::getPathFromMethod($method);
77
		return [ $annot ];
78
	}
79
80
	public function asArray() {
81
		$result=[ ];
82
		$prefix="";
83
		$httpMethods=false;
84
		if ($this->mainRouteClass) {
85
			if (isset($this->mainRouteClass->path))
86
				$prefix=$this->mainRouteClass->path;
87
			if (isset($this->mainRouteClass->methods)) {
88
				$httpMethods=$this->mainRouteClass->methods;
89
				if ($httpMethods !== null) {
90
					if (\is_string($httpMethods))
91
						$httpMethods=[ $httpMethods ];
92
				}
93
			}
94
		}
95
		foreach ( $this->routesMethods as $method => $arrayAnnotsMethod ) {
96
			$routeAnnotations=$arrayAnnotsMethod["annotations"];
97
98
			foreach ( $routeAnnotations as $routeAnnotation ) {
99
				$params=[ "path" => $routeAnnotation->path,"methods" => $routeAnnotation->methods,"name" => $routeAnnotation->name,"cache" => $routeAnnotation->cache,"duration" => $routeAnnotation->duration,"requirements" => $routeAnnotation->requirements,"priority"=>$routeAnnotation->priority ];
100
				self::parseRouteArray($result, $this->controllerClass, $params, $arrayAnnotsMethod["method"], $method, $prefix, $httpMethods);
101
			}
102
		}
103
		uasort($result, function ($item1, $item2) {
104
			return UArray::getRecursive($item2,"priority",0) <=> UArray::getRecursive($item1,"priority",0);
105
		});
106
		UArray::removeRecursive($result,"priority");
107
		return $result;
108
	}
109
110
	public static function parseRouteArray(&$result, $controllerClass, $routeArray, \ReflectionMethod $method, $methodName, $prefix="", $httpMethods=NULL) {
111
		if (!isset($routeArray["path"])) {
112
			$routeArray["path"]=self::getPathFromMethod($method);
113
		}
114
		$pathParameters=self::addParamsPath($routeArray["path"], $method, $routeArray["requirements"]);
115
		$name=$routeArray["name"];
116
		if (!isset($name)) {
117
			$name=UString::cleanAttribute(ClassUtils::getClassSimpleName($controllerClass) . "_" . $methodName);
118
		}
119
		$cache=$routeArray["cache"];
120
		$duration=$routeArray["duration"];
121
		$path=$pathParameters["path"];
122
		$parameters=$pathParameters["parameters"];
123
		$priority=$routeArray["priority"];
124
		$path=self::cleanpath($prefix, $path);
125
		if (isset($routeArray["methods"]) && \is_array($routeArray["methods"])) {
126
			self::createRouteMethod($result, $controllerClass, $path, $routeArray["methods"], $methodName, $parameters, $name, $cache, $duration,$priority);
127
		} elseif (\is_array($httpMethods)) {
128
			self::createRouteMethod($result, $controllerClass, $path, $httpMethods, $methodName, $parameters, $name, $cache, $duration,$priority);
129
		} else {
130
			$result[$path]=[ "controller" => $controllerClass,"action" => $methodName,"parameters" => $parameters,"name" => $name,"cache" => $cache,"duration" => $duration,"priority"=>$priority];
131
		}
132
	}
133
134
	public static function addParamsPath($path, \ReflectionMethod $method, $requirements) {
135
		$parameters=[ ];
136
		$hasOptional=false;
137
		preg_match_all('@\{(\.\.\.|\~)?(.+?)\}@s', $path, $matches);
138
		if (isset($matches[2]) && \sizeof($matches[2]) > 0) {
139
			$path=\preg_quote($path);
140
			$params=Reflexion::getMethodParameters($method);
141
			$index=0;
142
			foreach ( $matches[2] as $paramMatch ) {
143
				$find=\array_search($paramMatch, $params);
144
				if ($find !== false) {
145
					$requirement='.+?';
146
					if (isset($requirements[$paramMatch])) {
147
						$requirement=$requirements[$paramMatch];
148
					}
149
					self::scanParam($parameters, $hasOptional, $matches, $index, $paramMatch, $find, $path, $requirement);
150
				} else {
151
					throw new \Exception("{$paramMatch} is not a parameter of the method " . $method->name);
152
				}
153
				$index++;
154
			}
155
		}
156
		if ($hasOptional)
157
			$path.="/(.*?)";
158
		return [ "path" => $path,"parameters" => $parameters ];
159
	}
160
161
	private static function scanParam(&$parameters, &$hasOptional, $matches, $index, $paramMatch, $find, &$path, $requirement) {
162
		$toReplace=true;
163
		if (isset($matches[1][$index])) {
164
			if ($matches[1][$index] === "...") {
165
				$parameters[]="*";
166
				$path=\str_replace("\{\.\.\." . $paramMatch . "\}", "(.*?)", $path);
167
				$toReplace=false;
168
			} elseif ($matches[1][$index] === "~") {
169
				$parameters[]="~" . $find;
170
				$path=\str_replace("\{~" . $paramMatch . "\}", "", $path);
171
				$hasOptional=true;
172
				$toReplace=false;
173
			}
174
		} 
175
		if($toReplace) {
176
			$parameters[]=$find;
177
			$path=\str_replace("\{" . $paramMatch . "\}", "({$requirement})", $path);
178
		}
179
	}
180
181
	private static function createRouteMethod(&$result, $controllerClass, $path, $httpMethods, $method, $parameters, $name, $cache, $duration,$priority) {
182
		foreach ( $httpMethods as $httpMethod ) {
183
			$result[$path][$httpMethod]=[ "controller" => $controllerClass,"action" => $method,"parameters" => $parameters,"name" => $name,"cache" => $cache,"duration" => $duration,"priority"=>$priority ];
184
		}
185
	}
186
187
	public function isRest() {
188
		return $this->rest;
189
	}
190
}
191