3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Di\Definition
;
12 use Zend\Code\Annotation\AnnotationCollection
;
13 use Zend\Code\Reflection
;
14 use Zend\Di\Definition\Annotation
;
18 * Class definitions based on runtime reflection
20 class RuntimeDefinition
implements DefinitionInterface
26 protected $classes = array();
31 protected $explicitLookups = false;
34 * @var IntrospectionStrategy
36 protected $introspectionStrategy = null;
41 protected $injectionMethods = array();
46 * @param null|IntrospectionStrategy $introspectionStrategy
47 * @param array|null $explicitClasses
49 public function __construct(IntrospectionStrategy
$introspectionStrategy = null, array $explicitClasses = null)
51 $this->introspectionStrategy
= ($introspectionStrategy) ?
: new IntrospectionStrategy();
52 if ($explicitClasses) {
53 $this->setExplicitClasses($explicitClasses);
58 * @param IntrospectionStrategy $introspectionStrategy
61 public function setIntrospectionStrategy(IntrospectionStrategy
$introspectionStrategy)
63 $this->introspectionStrategy
= $introspectionStrategy;
67 * @return IntrospectionStrategy
69 public function getIntrospectionStrategy()
71 return $this->introspectionStrategy
;
75 * Set explicit classes
77 * @param array $explicitClasses
79 public function setExplicitClasses(array $explicitClasses)
81 $this->explicitLookups
= true;
82 foreach ($explicitClasses as $eClass) {
83 $this->classes
[$eClass] = true;
85 $this->classes
= $explicitClasses;
89 * @param string $class
91 public function forceLoadClass($class)
93 $this->processClass($class);
99 public function getClasses()
101 return array_keys($this->classes
);
107 public function hasClass($class)
109 if ($this->explicitLookups
=== true) {
110 return (array_key_exists($class, $this->classes
));
113 return class_exists($class) ||
interface_exists($class);
119 public function getClassSupertypes($class)
121 if (!array_key_exists($class, $this->classes
)) {
122 $this->processClass($class);
125 return $this->classes
[$class]['supertypes'];
131 public function getInstantiator($class)
133 if (!array_key_exists($class, $this->classes
)) {
134 $this->processClass($class);
137 return $this->classes
[$class]['instantiator'];
143 public function hasMethods($class)
145 if (!array_key_exists($class, $this->classes
)) {
146 $this->processClass($class);
149 return (count($this->classes
[$class]['methods']) > 0);
155 public function hasMethod($class, $method)
157 if (!array_key_exists($class, $this->classes
)) {
158 $this->processClass($class);
161 return isset($this->classes
[$class]['methods'][$method]);
167 public function getMethods($class)
169 if (!array_key_exists($class, $this->classes
)) {
170 $this->processClass($class);
173 return $this->classes
[$class]['methods'];
179 public function hasMethodParameters($class, $method)
181 if (!isset($this->classes
[$class])) {
185 return (array_key_exists($method, $this->classes
[$class]['parameters']));
191 public function getMethodParameters($class, $method)
193 if (!is_array($this->classes
[$class])) {
194 $this->processClass($class);
197 return $this->classes
[$class]['parameters'][$method];
201 * @param string $class
203 protected function processClass($class)
205 $strategy = $this->introspectionStrategy
; // localize for readability
207 /** @var $rClass \Zend\Code\Reflection\ClassReflection */
208 $rClass = new Reflection\
ClassReflection($class);
209 $className = $rClass->getName();
210 $matches = null; // used for regex below
212 // setup the key in classes
213 $this->classes
[$className] = array(
214 'supertypes' => array(),
215 'instantiator' => null,
216 'methods' => array(),
217 'parameters' => array()
220 $def = &$this->classes
[$className]; // localize for brevity
222 // class annotations?
223 if ($strategy->getUseAnnotations() == true) {
224 $annotations = $rClass->getAnnotations($strategy->getAnnotationManager());
226 if (($annotations instanceof AnnotationCollection
)
227 && $annotations->hasAnnotation('Zend\Di\Definition\Annotation\Instantiator')) {
228 // @todo Instantiator support in annotations
233 $supertypes = array();
235 $supertypes = array_merge($supertypes, $rTarget->getInterfaceNames());
236 if (!($rTargetParent = $rTarget->getParentClass())) {
239 $supertypes[] = $rTargetParent->getName();
240 $rTarget = $rTargetParent;
243 $def['supertypes'] = $supertypes;
245 if ($def['instantiator'] == null) {
246 if ($rClass->isInstantiable()) {
247 $def['instantiator'] = '__construct';
251 if ($rClass->hasMethod('__construct')) {
252 $def['methods']['__construct'] = Di
::METHOD_IS_CONSTRUCTOR
; // required
253 $this->processParams($def, $rClass, $rClass->getMethod('__construct'));
256 foreach ($rClass->getMethods(Reflection\MethodReflection
::IS_PUBLIC
) as $rMethod) {
258 $methodName = $rMethod->getName();
260 if ($rMethod->getName() === '__construct' ||
$rMethod->isStatic()) {
264 if ($strategy->getUseAnnotations() == true) {
265 $annotations = $rMethod->getAnnotations($strategy->getAnnotationManager());
267 if (($annotations instanceof AnnotationCollection
)
268 && $annotations->hasAnnotation('Zend\Di\Definition\Annotation\Inject')) {
270 // use '@inject' and search for parameters
271 $def['methods'][$methodName] = Di
::METHOD_IS_EAGER
;
272 $this->processParams($def, $rClass, $rMethod);
277 $methodPatterns = $this->introspectionStrategy
->getMethodNameInclusionPatterns();
279 // matches a method injection pattern?
280 foreach ($methodPatterns as $methodInjectorPattern) {
281 preg_match($methodInjectorPattern, $methodName, $matches);
283 $def['methods'][$methodName] = Di
::METHOD_IS_OPTIONAL
; // check ot see if this is required?
284 $this->processParams($def, $rClass, $rMethod);
291 // by setter pattern,
296 $interfaceInjectorPatterns = $this->introspectionStrategy
->getInterfaceInjectionInclusionPatterns();
298 // matches the interface injection pattern
299 /** @var $rIface \ReflectionClass */
300 foreach ($rClass->getInterfaces() as $rIface) {
301 foreach ($interfaceInjectorPatterns as $interfaceInjectorPattern) {
302 preg_match($interfaceInjectorPattern, $rIface->getName(), $matches);
304 foreach ($rIface->getMethods() as $rMethod) {
305 if (($rMethod->getName() === '__construct') ||
!count($rMethod->getParameters())) {
306 // constructor not allowed in interfaces
307 // Don't call interface methods without a parameter (Some aware interfaces define setters in ZF2)
310 $def['methods'][$rMethod->getName()] = Di
::METHOD_IS_AWARE
;
311 $this->processParams($def, $rClass, $rMethod);
321 * @param \Zend\Code\Reflection\ClassReflection $rClass
322 * @param \Zend\Code\Reflection\MethodReflection $rMethod
324 protected function processParams(&$def, Reflection\ClassReflection
$rClass, Reflection\MethodReflection
$rMethod)
326 if (count($rMethod->getParameters()) === 0) {
330 $methodName = $rMethod->getName();
332 // @todo annotations here for alternate names?
334 $def['parameters'][$methodName] = array();
336 foreach ($rMethod->getParameters() as $p) {
338 /** @var $p \ReflectionParameter */
339 $actualParamName = $p->getName();
341 $fqName = $rClass->getName() . '::' . $rMethod->getName() . ':' . $p->getPosition();
343 $def['parameters'][$methodName][$fqName] = array();
345 // set the class name, if it exists
346 $def['parameters'][$methodName][$fqName][] = $actualParamName;
347 $def['parameters'][$methodName][$fqName][] = ($p->getClass() !== null) ?
$p->getClass()->getName() : null;
348 $def['parameters'][$methodName][$fqName][] = !($optional = $p->isOptional() && $p->isDefaultValueAvailable());
349 $def['parameters'][$methodName][$fqName][] = $optional ?
$p->getDefaultValue() : null;