Passed
Push — master ( 16d34a...de61bc )
by Jean-Christophe
10:20
created

DiControllerParser::getInjectableAutowired()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 23
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 14.2702

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 20
c 1
b 0
f 1
dl 0
loc 23
ccs 8
cts 17
cp 0.4706
rs 8.6666
cc 7
nc 9
nop 2
crap 14.2702
1
<?php
2
3
namespace Ubiquity\controllers\di;
4
5
use Ubiquity\orm\parser\Reflexion;
6
use Ubiquity\utils\base\UArray;
7
use Ubiquity\utils\base\UString;
8
use Ubiquity\cache\ClassUtils;
9
use Ubiquity\exceptions\DiException;
10
11
/**
12
 * Parse the controllers for dependency injections.
13
 *
14
 * Ubiquity\controllers\di$DiControllerParser
15
 * This class is part of Ubiquity
16
 *
17
 * @author jcheron <[email protected]>
18
 * @version 1.0.0
19
 * @since Ubiquity 2.1.0
20
 *
21
 */
22
class DiControllerParser {
23
	protected $injections = [ ];
24
25 16
	public function parse($controllerClass, $config) {
26 16
		$instance = new $controllerClass ();
27 16
		$properties = Reflexion::getProperties ( $instance );
28 16
		foreach ( $properties as $property ) {
29 16
			$propName = $property->getName ();
30 16
			$annot = Reflexion::getAnnotationMember ( $controllerClass, $propName, "@injected" );
31 16
			if ($annot !== false) {
32 15
				$name = $annot->name;
33 15
				if ($this->isInjectable ( $controllerClass, $name ?? $propName, false )) {
34 15
					$this->injections [$propName] = $this->getInjection ( $name ?? $propName, $config, $controllerClass, $annot->code ?? null);
35
				}
36
			} else {
37 16
				$annot = Reflexion::getAnnotationMember ( $controllerClass, $propName, "@autowired" );
38 16
				if ($annot !== false) {
39 15
					$type = Reflexion::getPropertyType ( $controllerClass, $propName );
40 15
					if ($type !== false) {
41 15
						if ($this->isInjectable ( $controllerClass, $propName, false )) {
42 15
							$this->getInjectableAutowired ( $type, $propName );
43
						}
44
					} else {
45 9
						throw new DiException ( sprintf ( '%s property has no type and cannot be autowired!', $propName ) );
46
					}
47
				}
48
			}
49
		}
50 16
		$this->scanGlobalDi ( $config ['di'] ?? [ ], $controllerClass );
51 16
	}
52
53 15
	protected function getInjectableAutowired($type, $propName) {
54 15
		$typeR = new \ReflectionClass ( $type );
55 15
		if ($typeR->isInstantiable ()) {
56 15
			$constructor= $typeR->getConstructor ();
57 15
			$nbParams = $constructor==null?0:$typeR->getConstructor ()->getNumberOfRequiredParameters ();
58 15
			if ($nbParams == 0) {
59 15
				$this->injections [$propName] = "function(){return new " . $type . "();}";
60
			} elseif ($nbParams == 1) {
61
				$this->injections [$propName] = "function(\$controller){return new " . $type . "(\$controller);}";
62
			} else {
63 9
				throw new DiException ( sprintf ( 'Service %s constructor has too many mandatory arguments for %s injection!', $type, $propName ) );
64
			}
65
		} else {
66
			$namespace = $typeR->getNamespaceName ();
67
			$oClass = $namespace . "\\" . ucfirst ( $propName );
68
			if (class_exists ( $oClass )) {
69
				if (is_subclass_of ( $oClass, $type )) {
70
					$this->getInjectableAutowired ( $oClass, $propName );
71
				} else {
72
					throw new DiException ( sprintf ( 'Class %s is not a subclass of %s!', $oClass, $type ) );
73
				}
74
			} else {
75
				throw new DiException ( sprintf ( 'Class %s does not exists!', $oClass ) );
76
			}
77
		}
78 15
	}
79
80 16
	protected function scanGlobalDi($diConfig, $controller) {
81 16
		$classname = ClassUtils::getClassSimpleName ( $controller );
82 16
		foreach ( $diConfig as $k => $v ) {
83 16
			if (UString::startswith ( $k, "*." ) || UString::startswith ( $k, $classname . "." )) {
84 16
				$dis = explode ( '.', $k );
85 16
				$nkey = end ( $dis );
86 16
				if (property_exists ( $controller, $nkey ) === false) {
87 16
					$this->injections [$nkey] = $v;
88
				}
89
			}
90
		}
91 16
	}
92
93 15
	protected function isInjectable($classname, $member, $silent = true) {
94 15
		$prop = new \ReflectionProperty ( $classname, $member );
95 15
		if ($prop->isPublic ()) {
96 15
			return true;
97
		}
98 15
		$setter = 'set' . ucfirst ( $member );
99 15
		if (method_exists ( $classname, $setter )) {
100 15
			return true;
101
		}
102
		if (! $silent) {
103
			throw new DiException ( sprintf ( '%s member must be public or have a setter to be injected in the class %s!', $member, $classname ) );
104
		}
105
		return false;
106
	}
107
108 15
	protected function getInjection($name, $config, $controller, $code = null) {
109 15
		if ($code != null) {
110
			return "function(\$controller){return " . $code . ";}";
111
		}
112 15
		if (isset ( $config ["di"] )) {
113 15
			$di = $config ['di'];
114 15
			if ($name != null) {
115 15
				$classname = ClassUtils::getClassSimpleName ( $controller );
116 15
				if (isset ( $di [$name] )) {
117
					return $di [$name];
118 15
				} elseif (isset ( $di [$classname . '.' . $name] )) {
119
					return $di [$classname . '.' . $name];
120 15
				} elseif (isset ( $di ['*.' . $name] )) {
121 15
					return $di ['*.' . $name];
122
				} else {
123
					throw new \Exception ( "key " . $name . " is not present in config di array" );
124
				}
125
			}
126
		} else {
127
			throw new \Exception ( "key di is not present in config array" );
128
		}
129
	}
130
131 16
	public function __toString() {
132 16
		return "return " . UArray::asPhpArray ( $this->injections, "array" ) . ";";
133
	}
134
135
	/**
136
	 *
137
	 * @return array
138
	 */
139 16
	public function getInjections() {
140 16
		return $this->injections;
141
	}
142
}
143
144