ioc.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php namespace Laravel; use Closure;
  2. class IoC {
  3. /**
  4. * The registered dependencies.
  5. *
  6. * @var array
  7. */
  8. public static $registry = array();
  9. /**
  10. * The resolved singleton instances.
  11. *
  12. * @var array
  13. */
  14. public static $singletons = array();
  15. /**
  16. * Register an object and its resolver.
  17. *
  18. * @param string $name
  19. * @param mixed $resolver
  20. * @param bool $singleton
  21. * @return void
  22. */
  23. public static function register($name, $resolver = null, $singleton = false)
  24. {
  25. if (is_null($resolver)) $resolver = $name;
  26. static::$registry[$name] = compact('resolver', 'singleton');
  27. }
  28. /**
  29. * Unregister an object
  30. *
  31. * @param string $name
  32. */
  33. public static function unregister($name)
  34. {
  35. if (array_key_exists($name, static::$registry)) {
  36. unset(static::$registry[$name]);
  37. unset(static::$singletons[$name]);
  38. }
  39. }
  40. /**
  41. * Determine if an object has been registered in the container.
  42. *
  43. * @param string $name
  44. * @return bool
  45. */
  46. public static function registered($name)
  47. {
  48. return array_key_exists($name, static::$registry) || array_key_exists($name, static::$singletons);
  49. }
  50. /**
  51. * Register an object as a singleton.
  52. *
  53. * Singletons will only be instantiated the first time they are resolved.
  54. *
  55. * @param string $name
  56. * @param Closure $resolver
  57. * @return void
  58. */
  59. public static function singleton($name, $resolver = null)
  60. {
  61. static::register($name, $resolver, true);
  62. }
  63. /**
  64. * Register an existing instance as a singleton.
  65. *
  66. * <code>
  67. * // Register an instance as a singleton in the container
  68. * IoC::instance('mailer', new Mailer);
  69. * </code>
  70. *
  71. * @param string $name
  72. * @param mixed $instance
  73. * @return void
  74. */
  75. public static function instance($name, $instance)
  76. {
  77. static::$singletons[$name] = $instance;
  78. }
  79. /**
  80. * Resolve a given type to an instance.
  81. *
  82. * <code>
  83. * // Get an instance of the "mailer" object registered in the container
  84. * $mailer = IoC::resolve('mailer');
  85. *
  86. * // Get an instance of the "mailer" object and pass parameters to the resolver
  87. * $mailer = IoC::resolve('mailer', array('test'));
  88. * </code>
  89. *
  90. * @param string $type
  91. * @param array $parameters
  92. * @return mixed
  93. */
  94. public static function resolve($type, $parameters = array())
  95. {
  96. // If an instance of the type is currently being managed as a singleton, we will
  97. // just return the existing instance instead of instantiating a fresh instance
  98. // so the developer can keep re-using the exact same object instance from us.
  99. if (isset(static::$singletons[$type]))
  100. {
  101. return static::$singletons[$type];
  102. }
  103. // If we don't have a registered resolver or concrete for the type, we'll just
  104. // assume the type is the concrete name and will attempt to resolve it as is
  105. // since the container should be able to resolve concretes automatically.
  106. if ( ! isset(static::$registry[$type]))
  107. {
  108. $concrete = $type;
  109. }
  110. else
  111. {
  112. $concrete = array_get(static::$registry[$type], 'resolver', $type);
  113. }
  114. // We're ready to instantiate an instance of the concrete type registered for
  115. // the binding. This will instantiate the type, as well as resolve any of
  116. // its nested dependencies recursively until they are each resolved.
  117. if ($concrete == $type or $concrete instanceof Closure)
  118. {
  119. $object = static::build($concrete, $parameters);
  120. }
  121. else
  122. {
  123. $object = static::resolve($concrete);
  124. }
  125. // If the requested type is registered as a singleton, we want to cache off
  126. // the instance in memory so we can return it later without creating an
  127. // entirely new instances of the object on each subsequent request.
  128. if (isset(static::$registry[$type]['singleton']) && static::$registry[$type]['singleton'] === true)
  129. {
  130. static::$singletons[$type] = $object;
  131. }
  132. Event::fire('laravel.resolving', array($type, $object));
  133. return $object;
  134. }
  135. /**
  136. * Instantiate an instance of the given type.
  137. *
  138. * @param string $type
  139. * @param array $parameters
  140. * @return mixed
  141. * @throws \Exception
  142. */
  143. protected static function build($type, $parameters = array())
  144. {
  145. // If the concrete type is actually a Closure, we will just execute it and
  146. // hand back the results of the function, which allows functions to be
  147. // used as resolvers for more fine-tuned resolution of the objects.
  148. if ($type instanceof Closure)
  149. {
  150. return call_user_func_array($type, $parameters);
  151. }
  152. $reflector = new \ReflectionClass($type);
  153. // If the type is not instantiable, the developer is attempting to resolve
  154. // an abstract type such as an Interface of an Abstract Class and there is
  155. // no binding registered for the abstraction so we need to bail out.
  156. if ( ! $reflector->isInstantiable())
  157. {
  158. throw new \Exception("Resolution target [$type] is not instantiable.");
  159. }
  160. $constructor = $reflector->getConstructor();
  161. // If there is no constructor, that means there are no dependencies and
  162. // we can just resolve an instance of the object right away without
  163. // resolving any other types or dependencies from the container.
  164. if (is_null($constructor))
  165. {
  166. return new $type;
  167. }
  168. $dependencies = static::dependencies($constructor->getParameters(), $parameters);
  169. return $reflector->newInstanceArgs($dependencies);
  170. }
  171. /**
  172. * Resolve all of the dependencies from the ReflectionParameters.
  173. *
  174. * @param array $parameters
  175. * @param array $arguments that might have been passed into our resolve
  176. * @return array
  177. */
  178. protected static function dependencies($parameters, $arguments)
  179. {
  180. $dependencies = array();
  181. foreach ($parameters as $parameter)
  182. {
  183. $dependency = $parameter->getClass();
  184. // If the person passed in some parameters to the class
  185. // then we should probably use those instead of trying
  186. // to resolve a new instance of the class
  187. if (count($arguments) > 0)
  188. {
  189. $dependencies[] = array_shift($arguments);
  190. }
  191. else if (is_null($dependency))
  192. {
  193. $dependency[] = static::resolveNonClass($parameter);
  194. }
  195. else
  196. {
  197. $dependencies[] = static::resolve($dependency->name);
  198. }
  199. }
  200. return (array) $dependencies;
  201. }
  202. /**
  203. * Resolves optional parameters for our dependency injection
  204. * pretty much took backport straight from L4's Illuminate\Container
  205. *
  206. * @param ReflectionParameter
  207. * @return default value
  208. * @throws \Exception
  209. */
  210. protected static function resolveNonClass($parameter)
  211. {
  212. if ($parameter->isDefaultValueAvailable())
  213. {
  214. return $parameter->getDefaultValue();
  215. }
  216. else
  217. {
  218. throw new \Exception("Unresolvable dependency resolving [$parameter].");
  219. }
  220. }
  221. }