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