/library/Zend/Di/Definition/RuntimeDefinition.php

https://github.com/leerbag/zf2 · PHP · 359 lines · 206 code · 62 blank · 91 comment · 29 complexity · f4774fd253683311ea2d190380f9d155 MD5 · raw file

  1. <?php
  2. namespace Zend\Di\Definition;
  3. use Zend\Di\Definition\Annotation,
  4. Zend\Code\Annotation\AnnotationManager,
  5. Zend\Code\Annotation\AnnotationCollection,
  6. Zend\Code\Reflection;
  7. class RuntimeDefinition implements Definition
  8. {
  9. /**
  10. * @var array
  11. */
  12. protected $classes = array();
  13. /**
  14. * @var bool
  15. */
  16. protected $explicitLookups = false;
  17. /**
  18. * @var IntrospectionStrategy
  19. */
  20. protected $introspectionStrategy = null;
  21. /**
  22. * @var array
  23. */
  24. protected $injectionMethods = array();
  25. /**
  26. *
  27. */
  28. public function __construct(IntrospectionStrategy $introspectionStrategy = null, array $explicitClasses = null)
  29. {
  30. $this->introspectionStrategy = ($introspectionStrategy) ?: new IntrospectionStrategy();
  31. if ($explicitClasses) {
  32. $this->setExplicitClasses($explicitClasses);
  33. }
  34. }
  35. /**
  36. * @param IntrospectionStrategy $introspectionStrategy
  37. * @return void
  38. */
  39. public function setIntrospectionStrategy(IntrospectionStrategy $introspectionStrategy)
  40. {
  41. $this->introspectionStrategy = $introspectionStrategy;
  42. }
  43. /**
  44. * @return IntrospectionStrategy
  45. */
  46. public function getIntrospectionStrategy()
  47. {
  48. return $this->introspectionStrategy;
  49. }
  50. public function setExplicitClasses(array $explicitClasses)
  51. {
  52. $this->explicitLookups = true;
  53. foreach ($explicitClasses as $eClass) {
  54. $this->classes[$eClass] = true;
  55. }
  56. $this->classes = $explicitClasses;
  57. }
  58. public function forceLoadClass($class)
  59. {
  60. $this->processClass($class);
  61. }
  62. /**
  63. * Return nothing
  64. *
  65. * @return array
  66. */
  67. public function getClasses()
  68. {
  69. return array_keys($this->classes);
  70. }
  71. /**
  72. * Return whether the class exists
  73. *
  74. * @param string $class
  75. * @return bool
  76. */
  77. public function hasClass($class)
  78. {
  79. if ($this->explicitLookups === true) {
  80. return (array_key_exists($class, $this->classes));
  81. }
  82. return class_exists($class, true);
  83. }
  84. /**
  85. * Return the supertypes for this class
  86. *
  87. * @param string $class
  88. * @return array of types
  89. */
  90. public function getClassSupertypes($class)
  91. {
  92. if (!array_key_exists($class, $this->classes[$class])) {
  93. $this->processClass($class);
  94. }
  95. return $this->classes[$class]['supertypes'];
  96. }
  97. /**
  98. * Get the instantiator
  99. *
  100. * @param string $class
  101. * @return string|callable
  102. */
  103. public function getInstantiator($class)
  104. {
  105. if (!array_key_exists($class, $this->classes)) {
  106. $this->processClass($class);
  107. }
  108. return $this->classes[$class]['instantiator'];
  109. }
  110. /**
  111. * Return if there are injection methods
  112. *
  113. * @param string $class
  114. * @return bool
  115. */
  116. public function hasMethods($class)
  117. {
  118. if (!array_key_exists($class, $this->classes)) {
  119. $this->processClass($class);
  120. }
  121. return (count($this->classes[$class]['methods']) > 0);
  122. }
  123. /**
  124. * Return injection methods
  125. *
  126. * @param string $class
  127. * @param string $method
  128. * @return bool
  129. */
  130. public function hasMethod($class, $method)
  131. {
  132. if (!array_key_exists($class, $this->classes)) {
  133. $this->processClass($class);
  134. }
  135. return isset($this->classes[$class]['methods'][$method]);
  136. }
  137. /**
  138. * Return an array of the injection methods
  139. *
  140. * @param string $class
  141. * @return array
  142. */
  143. public function getMethods($class)
  144. {
  145. if (!array_key_exists($class, $this->classes)) {
  146. $this->processClass($class);
  147. }
  148. return $this->classes[$class]['methods'];
  149. }
  150. public function hasMethodParameters($class, $method)
  151. {
  152. if (!isset($this->classes[$class])) {
  153. return false;
  154. }
  155. return (array_key_exists($method, $this->classes[$class]['parameters']));
  156. }
  157. /**
  158. * Return the parameters for a method
  159. *
  160. * 3 item array:
  161. * #1 - Class name, string if it exists, else null
  162. * #2 - Optional?, boolean
  163. * #3 - Instantiable, boolean if class exists, otherwise null
  164. *
  165. * @param string $class
  166. * @param string $method
  167. * @return array
  168. */
  169. public function getMethodParameters($class, $method)
  170. {
  171. if (!is_array($this->classes[$class])) {
  172. $this->processClass($class);
  173. }
  174. return $this->classes[$class]['parameters'][$method];
  175. }
  176. protected function processClass($class)
  177. {
  178. $strategy = $this->introspectionStrategy; // localize for readability
  179. /** @var $rClass \Zend\Code\Reflection\ClassReflection */
  180. $rClass = new Reflection\ClassReflection($class);
  181. $className = $rClass->getName();
  182. $matches = null; // used for regex below
  183. // setup the key in classes
  184. $this->classes[$className] = array(
  185. 'supertypes' => array(),
  186. 'instantiator' => null,
  187. 'methods' => array(),
  188. 'parameters' => array()
  189. );
  190. $def = &$this->classes[$className]; // localize for brevity
  191. // class annotations?
  192. if ($strategy->getUseAnnotations() == true) {
  193. $annotations = $rClass->getAnnotations($strategy->getAnnotationManager());
  194. if (($annotations instanceof AnnotationCollection)
  195. && $annotations->hasAnnotation('Zend\Di\Definition\Annotation\Instantiator')) {
  196. // @todo Instnatiator support in annotations
  197. }
  198. }
  199. if ($def['instantiator'] == null) {
  200. if ($rClass->isInstantiable()) {
  201. $def['instantiator'] = '__construct';
  202. }
  203. }
  204. if ($rClass->hasMethod('__construct')) {
  205. $def['methods']['__construct'] = true; // required
  206. $this->processParams($def, $rClass, $rClass->getMethod('__construct'));
  207. }
  208. foreach ($rClass->getMethods(Reflection\MethodReflection::IS_PUBLIC) as $rMethod) {
  209. $methodName = $rMethod->getName();
  210. if ($rMethod->getName() === '__construct') {
  211. continue;
  212. }
  213. if ($strategy->getUseAnnotations() == true) {
  214. $annotations = $rMethod->getAnnotations($strategy->getAnnotationManager());
  215. if (($annotations instanceof AnnotationCollection)
  216. && $annotations->hasAnnotation('Zend\Di\Definition\Annotation\Inject')) {
  217. $def['methods'][$methodName] = true;
  218. $this->processParams($def, $rClass, $rMethod);
  219. continue;
  220. }
  221. }
  222. $methodPatterns = $this->introspectionStrategy->getMethodNameInclusionPatterns();
  223. // matches a method injection pattern?
  224. foreach ($methodPatterns as $methodInjectorPattern) {
  225. preg_match($methodInjectorPattern, $methodName, $matches);
  226. if ($matches) {
  227. $def['methods'][$methodName] = false; // check ot see if this is required?
  228. $this->processParams($def, $rClass, $rMethod);
  229. continue 2;
  230. }
  231. }
  232. // method
  233. // by annotation
  234. // by setter pattern,
  235. // by interface
  236. }
  237. $interfaceInjectorPatterns = $this->introspectionStrategy->getInterfaceInjectionInclusionPatterns();
  238. // matches the interface injection pattern
  239. /** @var $rIface \ReflectionClass */
  240. foreach ($rClass->getInterfaces() as $rIface) {
  241. foreach ($interfaceInjectorPatterns as $interfaceInjectorPattern) {
  242. preg_match($interfaceInjectorPattern, $rIface->getName(), $matches);
  243. if ($matches) {
  244. foreach ($rIface->getMethods() as $rMethod) {
  245. if ($rMethod->getName() === '__construct') { // ctor not allowed in ifaces
  246. continue;
  247. }
  248. $def['methods'][$rMethod->getName()] = true;
  249. $this->processParams($def, $rClass, $rMethod);
  250. }
  251. continue 2;
  252. }
  253. }
  254. }
  255. //var_dump($this->classes);
  256. }
  257. protected function processParams(&$def, Reflection\ClassReflection $rClass, Reflection\MethodReflection $rMethod)
  258. {
  259. if (count($rMethod->getParameters()) === 0) {
  260. return;
  261. }
  262. $methodName = $rMethod->getName();
  263. // @todo annotations here for alternate names?
  264. $def['parameters'][$methodName] = array();
  265. foreach ($rMethod->getParameters() as $p) {
  266. /** @var $p \ReflectionParameter */
  267. $actualParamName = $p->getName();
  268. $paramName = $this->createDistinctParameterName($actualParamName, $rClass->getName());
  269. $fqName = $rClass->getName() . '::' . $rMethod->getName() . ':' . $p->getPosition();
  270. $def['parameters'][$methodName][$fqName] = array();
  271. // set the class name, if it exists
  272. $def['parameters'][$methodName][$fqName][] = $actualParamName;
  273. $def['parameters'][$methodName][$fqName][] = ($p->getClass() !== null) ? $p->getClass()->getName() : null;
  274. $def['parameters'][$methodName][$fqName][] = !$p->isOptional();
  275. }
  276. }
  277. protected function createDistinctParameterName($paramName, $class)
  278. {
  279. $currentParams = array();
  280. if ($this->classes[$class]['parameters'] === array()) {
  281. return $paramName;
  282. }
  283. foreach ($this->classes as $cdata) {
  284. foreach ($cdata['parameters'] as $mdata) {
  285. $currentParams = array_merge($currentParams, array_keys($mdata));
  286. }
  287. }
  288. if (!in_array($paramName, $currentParams)) {
  289. return $paramName;
  290. }
  291. $alt = 2;
  292. while (in_array($paramName . (string) $alt, $currentParams)) {
  293. $alt++;
  294. }
  295. return $paramName . (string) $alt;
  296. }
  297. }