PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Di/Definition/CompilerDefinition.php

http://github.com/zendframework/zf2
PHP | 397 lines | 230 code | 60 blank | 107 comment | 31 complexity | ce65013a037a11756fa9af6718790136 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Di\Definition;
  10. use Zend\Code\Annotation\AnnotationCollection;
  11. use Zend\Code\Reflection;
  12. use Zend\Code\Scanner\AggregateDirectoryScanner;
  13. use Zend\Code\Scanner\DerivedClassScanner;
  14. use Zend\Code\Scanner\DirectoryScanner;
  15. use Zend\Code\Scanner\FileScanner;
  16. /**
  17. * Class definitions based on a set of directories to be scanned
  18. */
  19. class CompilerDefinition implements DefinitionInterface
  20. {
  21. protected $isCompiled = false;
  22. protected $introspectionStrategy = null;
  23. protected $allowReflectionExceptions = false;
  24. /**
  25. * @var AggregateDirectoryScanner
  26. */
  27. protected $directoryScanner = null;
  28. protected $classes = array();
  29. /**
  30. * Constructor
  31. *
  32. * @param null|IntrospectionStrategy $introspectionStrategy
  33. */
  34. public function __construct(IntrospectionStrategy $introspectionStrategy = null)
  35. {
  36. $this->introspectionStrategy = ($introspectionStrategy) ?: new IntrospectionStrategy();
  37. $this->directoryScanner = new AggregateDirectoryScanner();
  38. }
  39. /**
  40. * Set introspection strategy
  41. *
  42. * @param IntrospectionStrategy $introspectionStrategy
  43. */
  44. public function setIntrospectionStrategy(IntrospectionStrategy $introspectionStrategy)
  45. {
  46. $this->introspectionStrategy = $introspectionStrategy;
  47. }
  48. /**
  49. * @param bool $allowReflectionExceptions
  50. */
  51. public function setAllowReflectionExceptions($allowReflectionExceptions = true)
  52. {
  53. $this->allowReflectionExceptions = (bool) $allowReflectionExceptions;
  54. }
  55. /**
  56. * Get introspection strategy
  57. *
  58. * @return IntrospectionStrategy
  59. */
  60. public function getIntrospectionStrategy()
  61. {
  62. return $this->introspectionStrategy;
  63. }
  64. /**
  65. * Add directory
  66. *
  67. * @param string $directory
  68. */
  69. public function addDirectory($directory)
  70. {
  71. $this->addDirectoryScanner(new DirectoryScanner($directory));
  72. }
  73. /**
  74. * Add directory scanner
  75. *
  76. * @param DirectoryScanner $directoryScanner
  77. */
  78. public function addDirectoryScanner(DirectoryScanner $directoryScanner)
  79. {
  80. $this->directoryScanner->addDirectoryScanner($directoryScanner);
  81. }
  82. /**
  83. * Add code scanner file
  84. *
  85. * @param FileScanner $fileScanner
  86. */
  87. public function addCodeScannerFile(FileScanner $fileScanner)
  88. {
  89. if ($this->directoryScanner === null) {
  90. $this->directoryScanner = new DirectoryScanner();
  91. }
  92. $this->directoryScanner->addFileScanner($fileScanner);
  93. }
  94. /**
  95. * Compile
  96. *
  97. * @return void
  98. */
  99. public function compile()
  100. {
  101. /* @var $classScanner DerivedClassScanner */
  102. foreach ($this->directoryScanner->getClassNames() as $class) {
  103. $this->processClass($class);
  104. }
  105. }
  106. /**
  107. * @return ArrayDefinition
  108. */
  109. public function toArrayDefinition()
  110. {
  111. return new ArrayDefinition(
  112. $this->classes
  113. );
  114. }
  115. /**
  116. * @param string $class
  117. * @throws \ReflectionException
  118. */
  119. protected function processClass($class)
  120. {
  121. $strategy = $this->introspectionStrategy; // localize for readability
  122. try {
  123. $rClass = new Reflection\ClassReflection($class);
  124. } catch (\ReflectionException $e) {
  125. if (!$this->allowReflectionExceptions) {
  126. throw $e;
  127. }
  128. return;
  129. }
  130. $className = $rClass->getName();
  131. $matches = null; // used for regex below
  132. // setup the key in classes
  133. $this->classes[$className] = array(
  134. 'supertypes' => array(),
  135. 'instantiator' => null,
  136. 'methods' => array(),
  137. 'parameters' => array()
  138. );
  139. $def = &$this->classes[$className]; // localize for brevity
  140. // class annotations?
  141. if ($strategy->getUseAnnotations() == true) {
  142. $annotations = $rClass->getAnnotations($strategy->getAnnotationManager());
  143. if (($annotations instanceof AnnotationCollection)
  144. && $annotations->hasAnnotation('Zend\Di\Definition\Annotation\Instantiator')
  145. ) {
  146. // @todo Instantiator support in annotations
  147. }
  148. }
  149. /* @var $rTarget \Zend\Code\Reflection\ClassReflection */
  150. $rTarget = $rClass;
  151. $supertypes = array();
  152. do {
  153. $supertypes = array_merge($supertypes, $rTarget->getInterfaceNames());
  154. if (!($rTargetParent = $rTarget->getParentClass())) {
  155. break;
  156. }
  157. $supertypes[] = $rTargetParent->getName();
  158. $rTarget = $rTargetParent;
  159. } while (true);
  160. $def['supertypes'] = $supertypes;
  161. if ($def['instantiator'] === null) {
  162. if ($rClass->isInstantiable()) {
  163. $def['instantiator'] = '__construct';
  164. }
  165. }
  166. if ($rClass->hasMethod('__construct')) {
  167. $def['methods']['__construct'] = true; // required
  168. try {
  169. $this->processParams($def, $rClass, $rClass->getMethod('__construct'));
  170. } catch (\ReflectionException $e) {
  171. if (!$this->allowReflectionExceptions) {
  172. throw $e;
  173. }
  174. return;
  175. }
  176. }
  177. foreach ($rClass->getMethods(Reflection\MethodReflection::IS_PUBLIC) as $rMethod) {
  178. $methodName = $rMethod->getName();
  179. if ($rMethod->getName() === '__construct' || $rMethod->isStatic()) {
  180. continue;
  181. }
  182. if ($strategy->getUseAnnotations() == true) {
  183. $annotations = $rMethod->getAnnotations($strategy->getAnnotationManager());
  184. if (($annotations instanceof AnnotationCollection)
  185. && $annotations->hasAnnotation('Zend\Di\Definition\Annotation\Inject')
  186. ) {
  187. $def['methods'][$methodName] = true;
  188. $this->processParams($def, $rClass, $rMethod);
  189. continue;
  190. }
  191. }
  192. $methodPatterns = $this->introspectionStrategy->getMethodNameInclusionPatterns();
  193. // matches a method injection pattern?
  194. foreach ($methodPatterns as $methodInjectorPattern) {
  195. preg_match($methodInjectorPattern, $methodName, $matches);
  196. if ($matches) {
  197. $def['methods'][$methodName] = false; // check ot see if this is required?
  198. $this->processParams($def, $rClass, $rMethod);
  199. continue 2;
  200. }
  201. }
  202. // method
  203. // by annotation
  204. // by setter pattern,
  205. // by interface
  206. }
  207. $interfaceInjectorPatterns = $this->introspectionStrategy->getInterfaceInjectionInclusionPatterns();
  208. // matches the interface injection pattern
  209. /** @var $rIface \ReflectionClass */
  210. foreach ($rClass->getInterfaces() as $rIface) {
  211. foreach ($interfaceInjectorPatterns as $interfaceInjectorPattern) {
  212. preg_match($interfaceInjectorPattern, $rIface->getName(), $matches);
  213. if ($matches) {
  214. foreach ($rIface->getMethods() as $rMethod) {
  215. if (($rMethod->getName() === '__construct') || !count($rMethod->getParameters())) {
  216. // constructor not allowed in interfaces
  217. // ignore methods without parameters
  218. continue;
  219. }
  220. $def['methods'][$rMethod->getName()] = true;
  221. $this->processParams($def, $rClass, $rMethod);
  222. }
  223. continue 2;
  224. }
  225. }
  226. }
  227. }
  228. /**
  229. * @param array $def
  230. * @param \Zend\Code\Reflection\ClassReflection $rClass
  231. * @param \Zend\Code\Reflection\MethodReflection $rMethod
  232. */
  233. protected function processParams(&$def, Reflection\ClassReflection $rClass, Reflection\MethodReflection $rMethod)
  234. {
  235. if (count($rMethod->getParameters()) === 0) {
  236. return;
  237. }
  238. $methodName = $rMethod->getName();
  239. // @todo annotations here for alternate names?
  240. $def['parameters'][$methodName] = array();
  241. foreach ($rMethod->getParameters() as $p) {
  242. /** @var $p \ReflectionParameter */
  243. $actualParamName = $p->getName();
  244. $fqName = $rClass->getName() . '::' . $rMethod->getName() . ':' . $p->getPosition();
  245. $def['parameters'][$methodName][$fqName] = array();
  246. // set the class name, if it exists
  247. $def['parameters'][$methodName][$fqName][] = $actualParamName;
  248. $def['parameters'][$methodName][$fqName][] = ($p->getClass() !== null) ? $p->getClass()->getName() : null;
  249. $def['parameters'][$methodName][$fqName][] = !($optional =$p->isOptional());
  250. $def['parameters'][$methodName][$fqName][] = $optional && $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null;
  251. }
  252. }
  253. /**
  254. * {@inheritDoc}
  255. */
  256. public function getClasses()
  257. {
  258. return array_keys($this->classes);
  259. }
  260. /**
  261. * {@inheritDoc}
  262. */
  263. public function hasClass($class)
  264. {
  265. return (array_key_exists($class, $this->classes));
  266. }
  267. /**
  268. * {@inheritDoc}
  269. */
  270. public function getClassSupertypes($class)
  271. {
  272. if (!array_key_exists($class, $this->classes)) {
  273. $this->processClass($class);
  274. }
  275. return $this->classes[$class]['supertypes'];
  276. }
  277. /**
  278. * {@inheritDoc}
  279. */
  280. public function getInstantiator($class)
  281. {
  282. if (!array_key_exists($class, $this->classes)) {
  283. $this->processClass($class);
  284. }
  285. return $this->classes[$class]['instantiator'];
  286. }
  287. /**
  288. * {@inheritDoc}
  289. */
  290. public function hasMethods($class)
  291. {
  292. if (!array_key_exists($class, $this->classes)) {
  293. $this->processClass($class);
  294. }
  295. return (count($this->classes[$class]['methods']) > 0);
  296. }
  297. /**
  298. * {@inheritDoc}
  299. */
  300. public function hasMethod($class, $method)
  301. {
  302. if (!array_key_exists($class, $this->classes)) {
  303. $this->processClass($class);
  304. }
  305. return isset($this->classes[$class]['methods'][$method]);
  306. }
  307. /**
  308. * {@inheritDoc}
  309. */
  310. public function getMethods($class)
  311. {
  312. if (!array_key_exists($class, $this->classes)) {
  313. $this->processClass($class);
  314. }
  315. return $this->classes[$class]['methods'];
  316. }
  317. /**
  318. * {@inheritDoc}
  319. */
  320. public function hasMethodParameters($class, $method)
  321. {
  322. if (!isset($this->classes[$class])) {
  323. return false;
  324. }
  325. return (array_key_exists($method, $this->classes[$class]['parameters']));
  326. }
  327. /**
  328. * {@inheritDoc}
  329. */
  330. public function getMethodParameters($class, $method)
  331. {
  332. if (!is_array($this->classes[$class])) {
  333. $this->processClass($class);
  334. }
  335. return $this->classes[$class]['parameters'][$method];
  336. }
  337. }