Browse Source

Merged Linject into the core IoC container.

Taylor Otwell 12 years ago
parent
commit
348ff778ed
2 changed files with 96 additions and 43 deletions
  1. 1 0
      laravel/documentation/changes.md
  2. 95 43
      laravel/ioc.php

+ 1 - 0
laravel/documentation/changes.md

@@ -71,6 +71,7 @@
 - Added Response::json method for creating JSON responses.
 - Added Response::eloquent method for creating Eloquent responses.
 - Fixed bug when using many-to-many relationships on non-default database connection.
+- Added true reflection based IoC to container.
 
 <a name="upgrade-3.2"></a>
 ## Upgrading From 3.1

+ 95 - 43
laravel/ioc.php

@@ -20,11 +20,11 @@ class IoC {
 	 * Register an object and its resolver.
 	 *
 	 * @param  string   $name
-	 * @param  Closure  $resolver
+	 * @param  mixed    $resolver
 	 * @param  bool     $singleton
 	 * @return void
 	 */
-	public static function register($name, Closure $resolver, $singleton = false)
+	public static function register($name, $resolver, $singleton = false)
 	{
 		static::$registry[$name] = compact('resolver', 'singleton');
 	}
@@ -72,70 +72,122 @@ class IoC {
 	}
 
 	/**
-	 * Register a controller with the IoC container.
-	 *
-	 * @param  string   $name
-	 * @param  Closure  $resolver
-	 * @return void
-	 */
-	public static function controller($name, $resolver)
-	{
-		static::register("controller: {$name}", $resolver);
-	}
-
-	/**
-	 * Resolve a core Laravel class from the container.
+	 * Resolve a given type to an instance.
 	 *
 	 * <code>
-	 *		// Resolve the "laravel.router" class from the container
-	 *		$input = IoC::core('router');
+	 *		// Get an instance of the "mailer" object registered in the container
+	 *		$mailer = IoC::resolve('mailer');
 	 *
-	 *		// Equivalent resolution of the router using the "resolve" method
-	 *		$input = IoC::resolve('laravel.router');
+	 *		// Get an instance of the "mailer" object and pass parameters to the resolver
+	 *		$mailer = IoC::resolve('mailer', array('test'));
 	 * </code>
 	 *
-	 * @param  string  $name
-	 * @param  array   $parameters
+	 * @param  string  $type
 	 * @return mixed
 	 */
-	public static function core($name, $parameters = array())
+	public static function resolve($type, $parameters = array())
 	{
-		return static::resolve("laravel.{$name}", $parameters);
+		// 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];
+		}
+
+		$concrete = array_get(static::$registry, "{$type}.resolver", $type);
+
+		// 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)
+		{
+			$object = static::build($concrete);
+		}
+		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.
+		if (isset(static::$registry[$type]['singleton']))
+		{
+			static::$singletons[$type] = $object;
+		}
+
+		return $object;
 	}
 
 	/**
-	 * Resolve an object instance from the container.
-	 *
-	 * <code>
-	 *		// Get an instance of the "mailer" object registered in the container
-	 *		$mailer = IoC::resolve('mailer');
-	 *
-	 *		// Get an instance of the "mailer" object and pass parameters to the resolver
-	 *		$mailer = IoC::resolve('mailer', array('test'));
-	 * </code>
+	 * Instantiate an instance of the given type.
 	 *
-	 * @param  string  $name
+	 * @param  string  $type
 	 * @param  array   $parameters
 	 * @return mixed
 	 */
-	public static function resolve($name, $parameters = array())
+	protected static function build($type, $parameters = array())
 	{
-		if (array_key_exists($name, static::$singletons))
+		// 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)
 		{
-			return static::$singletons[$name];
+			return call_user_func_array($type, $parameters);
 		}
 
-		$object = call_user_func_array(static::$registry[$name]['resolver'], $parameters);
+		$reflector = new \ReflectionClass($type);
 
-		// If the resolver is registering as a singleton resolver, we will cache
-		// the instance of the object in the container so we can resolve it next
-		// time without having to instantiate a brand new instance.
-		if (static::$registry[$name]['singleton'])
+		// If the type is not instantiable, the developer is attempting to resolve
+		// an abstract type such as an Interface of Abstract Class and there is
+		// no binding registered for the abstraction so we need to bail out.
+		if ( ! $reflector->isInstantiable())
 		{
-			return static::$singletons[$name] = $object;
+			throw new Exception("Resolution target [$type] is not instantiable.");
 		}
 
-		return $object;
+		$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.
+	 *
+	 * @param  array  $parameterrs
+	 * @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
+			// primitive type, which we can not esolve since it is not a class and
+			// 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;
 	}
 
 }