ioc.php 5.51 KB
Newer Older
1
<?php namespace Laravel; use Closure;
2 3 4 5

class IoC {

	/**
6
	 * The registered dependencies.
7 8 9
	 *
	 * @var array
	 */
10
	public static $registry = array();
11 12

	/**
13
	 * The resolved singleton instances.
14 15 16
	 *
	 * @var array
	 */
17
	public static $singletons = array();
18 19

	/**
20
	 * Register an object and its resolver.
21 22
	 *
	 * @param  string   $name
23
	 * @param  mixed    $resolver
Phill Sparks committed
24
	 * @param  bool     $singleton
25 26
	 * @return void
	 */
Taylor Otwell committed
27
	public static function register($name, $resolver = null, $singleton = false)
28
	{
Taylor Otwell committed
29 30
		if (is_null($resolver)) $resolver = $name;

31
		static::$registry[$name] = compact('resolver', 'singleton');
32 33 34
	}

	/**
35
	 * Determine if an object has been registered in the container.
36 37 38 39
	 *
	 * @param  string  $name
	 * @return bool
	 */
40
	public static function registered($name)
41
	{
42
		return array_key_exists($name, static::$registry);
43 44 45
	}

	/**
46
	 * Register an object as a singleton.
47
	 *
Taylor Otwell committed
48
	 * Singletons will only be instantiated the first time they are resolved.
49
	 *
50 51 52 53
	 * @param  string   $name
	 * @param  Closure  $resolver
	 * @return void
	 */
Taylor Otwell committed
54
	public static function singleton($name, $resolver = null)
55
	{
56
		static::register($name, $resolver, true);
57 58 59
	}

	/**
60
	 * Register an existing instance as a singleton.
61
	 *
62 63
	 * <code>
	 *		// Register an instance as a singleton in the container
64
	 *		IoC::instance('mailer', new Mailer);
65 66
	 * </code>
	 *
67 68 69 70
	 * @param  string  $name
	 * @param  mixed   $instance
	 * @return void
	 */
71
	public static function instance($name, $instance)
72
	{
73
		static::$singletons[$name] = $instance;
74 75 76
	}

	/**
77
	 * Resolve a given type to an instance.
Taylor Otwell committed
78 79
	 *
	 * <code>
80 81
	 *		// Get an instance of the "mailer" object registered in the container
	 *		$mailer = IoC::resolve('mailer');
Taylor Otwell committed
82
	 *
83 84
	 *		// Get an instance of the "mailer" object and pass parameters to the resolver
	 *		$mailer = IoC::resolve('mailer', array('test'));
Taylor Otwell committed
85 86
	 * </code>
	 *
87
	 * @param  string  $type
Taylor Otwell committed
88 89
	 * @return mixed
	 */
90
	public static function resolve($type, $parameters = array())
Taylor Otwell committed
91
	{
92 93 94 95 96 97 98 99
		// If an instance of the type is currently being managed as a singleton, we will
		// just return the existing instance instead of instantiating a fresh instance
		// so the developer can keep re-using the exact same object instance from us.
		if (isset(static::$singletons[$type]))
		{
			return static::$singletons[$type];
		}

Taylor Otwell committed
100 101 102 103 104 105 106 107 108 109 110
		// If we don't have a registered resolver or concrete for the type, we'll just
		// assume the type is the concrete name and will attempt to resolve it as is
		// since the container should be able to resolve concretes automatically.
		if ( ! isset(static::$registry[$type]))
		{
			$concrete = $type;
		}
		else
		{
			$concrete = array_get(static::$registry[$type], 'resolver', $type);
		}
111 112 113 114 115 116

		// We're ready to instantiate an instance of the concrete type registered for
		// the binding. This will instantiate the type, as well as resolve any of
		// its nested dependencies recursively until they are each resolved.
		if ($concrete == $type or $concrete instanceof Closure)
		{
117
			$object = static::build($concrete, $parameters);
118 119 120 121 122 123 124 125 126
		}
		else
		{
			$object = static::resolve($concrete);
		}

		// If the requested type is registered as a singleton, we want to cache off
		// the instance in memory so we can return it later without creating an
		// entirely new instances of the object on each subsequent request.
127
		if (isset(static::$registry[$type]['singleton']) && static::$registry[$type]['singleton'] === true)
128 129 130 131
		{
			static::$singletons[$type] = $object;
		}

Taylor Otwell committed
132 133
		Event::fire('laravel.resolving', array($type, $object));

134
		return $object;
Taylor Otwell committed
135 136 137
	}

	/**
138
	 * Instantiate an instance of the given type.
139
	 *
140
	 * @param  string  $type
141
	 * @param  array   $parameters
142 143
	 * @return mixed
	 */
144
	protected static function build($type, $parameters = array())
145
	{
146 147 148 149
		// If the concrete type is actually a Closure, we will just execute it and
		// hand back the results of the function, which allows functions to be
		// used as resolvers for more fine-tuned resolution of the objects.
		if ($type instanceof Closure)
150
		{
151
			return call_user_func_array($type, $parameters);
152
		}
153

154
		$reflector = new \ReflectionClass($type);
155

156
		// If the type is not instantiable, the developer is attempting to resolve
Chris Berthe committed
157
		// an abstract type such as an Interface of an Abstract Class and there is
158 159
		// no binding registered for the abstraction so we need to bail out.
		if ( ! $reflector->isInstantiable())
160
		{
161
			throw new Exception("Resolution target [$type] is not instantiable.");
162 163
		}

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
		$constructor = $reflector->getConstructor();

		// If there is no constructor, that means there are no dependencies and
		// we can just resolve an instance of the object right away without
		// resolving any other types or dependencies from the container.
		if (is_null($constructor))
		{
			return new $type;
		}

		$dependencies = static::dependencies($constructor->getParameters());

		return $reflector->newInstanceArgs($dependencies);
	}

	/**
	 * Resolve all of the dependencies from the ReflectionParameters.
	 *
Pascal Borreli committed
182
	 * @param  array  $parameters
183 184 185 186 187 188 189 190 191 192 193
	 * @return array
	 */
	protected static function dependencies($parameters)
	{
		$dependencies = array();

		foreach ($parameters as $parameter)
		{
			$dependency = $parameter->getClass();

			// If the class is null, it means the dependency is a string or some other
194
			// primitive type, which we can not resolve since it is not a class and
195 196 197 198 199 200 201 202 203 204
			// we'll just bomb out with an error since we have nowhere to go.
			if (is_null($dependency))
			{
				throw new Exception("Unresolvable dependency resolving [$parameter].");
			}

			$dependencies[] = static::resolve($dependency->name);
		}

		return (array) $dependencies;
205 206
	}

207
}