ControllerParser::isRest()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 2
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Ubiquity\cache\parser;
4
5
use Ubiquity\exceptions\ParserException;
6
use Ubiquity\orm\parser\Reflexion;
7
use Ubiquity\utils\base\UString;
8
use Ubiquity\cache\ClassUtils;
9
use Ubiquity\annotations\AnnotationsEngineInterface;
10
use Ubiquity\exceptions\RouterException;
11
12
/**
13
 * Scans a controller to detect routes defined by annotations or attributes.
14
 * Ubiquity\cache\parser$ControllerParser
15
 * This class is part of Ubiquity
16
 *
17
 * @author jcheron <[email protected]>
18
 * @version 1.1.1
19
 *
20
 */
21
class ControllerParser {
22
	use ControllerParserPathTrait;
23
	
24
	const HTTP_METHODS=['head','get','post','patch','put','delete','options','connect'];
25
	
26
	private string $controllerClass;
27
	private $mainRouteClass;
28
	private array $routesMethods = [ ];
29
	private bool $rest = false;
30
	private bool $silent=false;
31
	private static array $excludeds = [ '__construct','isValid','initialize','finalize','onInvalidControl','loadView','forward','redirectToRoute' ];
32
	
33
	
34
	/**
35
	 *
36
	 * @var AnnotationsEngineInterface
37
	 */
38
	private $annotsEngine;
39
	
40 17
	public function __construct(AnnotationsEngineInterface $annotsEngine) {
41 17
		$this->annotsEngine = $annotsEngine;
42
	}
43
	
44 17
	public function parse($controllerClass) {
45 17
		$automated = false;
46 17
		$inherited = false;
47 17
		$this->controllerClass = $controllerClass;
48 17
		$restAnnotsClass = [ ];
49 17
		$reflect = new \ReflectionClass ( $controllerClass );
50 17
		if (! $reflect->isAbstract () && $reflect->isSubclassOf ( \Ubiquity\controllers\Controller::class )) {
51
			try {
52 17
				$annotsClass = Reflexion::getAnnotationClass ( $controllerClass, 'route' );
53 17
				$restAnnotsClass = Reflexion::getAnnotationClass ( $controllerClass, 'rest' );
54
			} catch ( \Exception $e ) {
55
				// When controllerClass generates an exception
56
			}
57 17
			$this->rest = \count ( $restAnnotsClass ) > 0;
58 17
			if (isset ( $annotsClass ) && \count ( $annotsClass ) > 0) {
59 17
				$this->mainRouteClass = $annotsClass [0];
60 17
				$inherited = $this->mainRouteClass->inherited;
61 17
				$automated = $this->mainRouteClass->automated;
62
			}
63 17
			$methods = Reflexion::getMethods ( $controllerClass, \ReflectionMethod::IS_PUBLIC );
64 17
			$this->parseMethods ( $methods, $controllerClass, $inherited, $automated );
65
		}
66
	}
67
	
68 17
	private function parseMethods($methods, $controllerClass, $inherited, $automated) {
69 17
		foreach ( $methods as $method ) {
70 17
			if ($method->getDeclaringClass ()->getName () === $controllerClass || $inherited) {
71
				try {
72 17
					$annots = Reflexion::getAnnotationsMethod ( $controllerClass, $method->name, [ 'route','get','post','patch','put','delete','options' ] );
73 17
					if (\count ( $annots ) > 0) {
74 17
						foreach ( $annots as $annot ) {
75 17
							$this->parseAnnot ( $annot, $method );
76
						}
77 17
						$this->routesMethods [$method->name] = [ 'annotations' => $annots,'method' => $method ];
78
					} else {
79 17
						if ($automated && $this->isRoutable($method)){
80 17
							$this->routesMethods [$method->name] = [ 'annotations' => $this->generateRouteAnnotationFromMethod ( $method ),'method' => $method ];
81
						}
82
					}
83 17
				} catch ( \Exception $e ) {
84 17
					if (!$this->silent && $e instanceof ParserException){
85
						throw $e;
86
					}
87
					// When controllerClass generates an exception
88
				}
89
			}
90
		}
91
	}
92
93 17
	private function isRoutable(\ReflectionMethod $method):bool{
94 17
		return $method->class !== 'Ubiquity\\controllers\\Controller'
95 17
			&& \array_search ( $method->name, self::$excludeds ) === false
96 17
			&& ! UString::startswith ( $method->name, '_' )
97 17
			&& Reflexion::getAnnotationsMethod($method->class,$method->name,'noRoute')===false;
98
	}
99
	
100 17
	private function parseAnnot(&$annot, $method) {
101 17
		if (UString::isNull ( $annot->path )) {
102 17
			$newAnnot = $this->generateRouteAnnotationFromMethod ( $method );
103 17
			$annot->path = $newAnnot [0]->path;
104
		} else {
105 17
			$annot->path = $this->parseMethodPath ( $method, $annot->path );
106
		}
107
	}
108
	
109 17
	private function generateRouteAnnotationFromMethod(\ReflectionMethod $method): array {
110 17
		return [ $this->annotsEngine->getAnnotation ( null, 'route', [ 'path' => self::getPathFromMethod ( $method ) ] ) ];
111
	}
112
	
113 17
	private static function generateRouteName(string $controllerName,string $action): string {
114 17
		$ctrl=\str_ireplace('controller','',ClassUtils::getClassSimpleName ( $controllerName ));
115 17
		return \lcfirst($ctrl) . '.' . $action;
116
	}
117
	
118 17
	public function asArray(): array {
119 17
		$result = [ ];
120 17
		$prefix = '';
121 17
		$httpMethods = false;
122 17
		if ($this->mainRouteClass) {
123 17
			if (isset ( $this->mainRouteClass->path )) {
124 17
				$this->mainRouteClass->path=self::parseMainPath($this->mainRouteClass->path,$this->controllerClass);
125 17
				$prefix = $this->mainRouteClass->path;
126
			}
127 17
			if (isset ( $this->mainRouteClass->methods )) {
128
				$httpMethods = $this->mainRouteClass->methods;
129
				if ($httpMethods !== null) {
130
					if (\is_string ( $httpMethods ))
131
						$httpMethods = [ $httpMethods ];
132
				}
133
			}
134
		}
135 17
		foreach ( $this->routesMethods as $method => $arrayAnnotsMethod ) {
136 17
			$routeAnnotations = $arrayAnnotsMethod ['annotations'];
137
			
138 17
			foreach ( $routeAnnotations as $routeAnnotation ) {
139 17
				$params = [ 'path' => $routeAnnotation->path,'methods' => $routeAnnotation->methods,'name' => $routeAnnotation->name,'cache' => $routeAnnotation->cache,'duration' => $routeAnnotation->duration,'requirements' => $routeAnnotation->requirements,'priority' => $routeAnnotation->priority ];
140 17
				self::parseRouteArray ( $result, $this->controllerClass, $params, $arrayAnnotsMethod ['method'], $method, $prefix, $httpMethods );
141
			}
142
		}
143 17
		self::$mainParams=null;
144 17
		return $result;
145
	}
146
	
147 47
	public static function parseRouteArray(&$result, $controllerClass, $routeArray, \ReflectionMethod $method, $methodName, $prefix = '', $httpMethods = NULL) {
148 47
		if (! isset ( $routeArray ['path'] )) {
149
			$routeArray ['path'] = self::getPathFromMethod ( $method );
150
		}
151 47
		$pathParameters = self::addParamsPath ( $routeArray ['path'], $method, $routeArray ['requirements'] );
152 47
		$name = $routeArray ['name'];
153 47
		if (! isset ( $name )) {
154 17
			$name = self::generateRouteName($controllerClass,$methodName);
155
		}
156 47
		$cache = $routeArray ['cache'];
157 47
		$duration = $routeArray ['duration'];
158 47
		$path = $pathParameters ['path'];
159 47
		$parameters = $pathParameters ['parameters'];
160 47
		$priority = $routeArray ['priority'];
161 47
		$callback = $routeArray ['callback'] ?? null;
162 47
		$isRoot=false;
163 47
		$path = self::cleanpath ( $prefix, $path ,$isRoot);
164 47
		if (isset ( $routeArray ['methods'] ) && \is_array ( $routeArray ['methods'] )) {
165 17
			self::createRouteMethod ( $result, $controllerClass, $path, $routeArray ['methods'], $methodName, $parameters, $name, $cache, $duration, $priority, $callback , $isRoot);
166 47
		} elseif (\is_array ( $httpMethods )) {
167
			self::createRouteMethod ( $result, $controllerClass, $path, $httpMethods, $methodName, $parameters, $name, $cache, $duration, $priority, $callback , $isRoot);
168
		} else {
169 47
			$v = [ 'controller' => $controllerClass,'action' => $methodName,'parameters' => $parameters,'name' => $name,'cache' => $cache,'duration' => $duration,'priority' => $priority];
170 47
			if (isset ( $callback )) {
171 1
				$v ['callback'] = $callback;
172
			}
173 47
			if(!$isRoot && isset(self::$mainParams) && \count(self::$mainParams)>0){
174 17
				$v['main.params']=self::$mainParams;
175
			}
176 47
			$result [$path] = $v;
177
		}
178
	}
179
	
180 17
	private static function createRouteMethod(&$result, $controllerClass, $path, $httpMethods, $method, $parameters, $name, $cache, $duration, $priority, $callback = null,$isRoot=false) {
181 17
		foreach ( $httpMethods as $httpMethod ) {
182 17
			$httpMethod=\strtolower($httpMethod);
183 17
			if(\array_search($httpMethod, self::HTTP_METHODS)===false){
184
				throw new RouterException("$httpMethod is not a valid HTTP method!");
185
			}
186 17
			$v = [ 'controller' => $controllerClass,'action' => $method,'parameters' => $parameters,'name' => $name,'cache' => $cache,'duration' => $duration,'priority' => $priority ];
187 17
			if (isset ( $callback )) {
188
				$v ['callback'] = $callback;
189
			}
190 17
			if(!$isRoot && isset(self::$mainParams) && \count(self::$mainParams)>0){
191 17
				$v['main.params']=self::$mainParams;
192
			}
193 17
			$result [$path] [$httpMethod] = $v;
194
		}
195
	}
196
	
197 17
	public function isRest(): bool {
198 17
		return $this->rest;
199
	}
200
	
201
	/**
202
	 * @param bool $silent
203
	 */
204 17
	public function setSilent(bool $silent): void {
205 17
		$this->silent = $silent;
206
	}
207
}
208