The Laravel framework instantiates the Illuminate\Foundation\Application class via require in the entry index.php file, which serves as the “glue” that connects the framework’s various services, components and container for IoC inversion of control.
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH']???? dirname(__DIR__));Copy the code
Find the Laravel package in the Vendor directory. The Laravel core code is also a Composer package. Find the definition file for the Illuminate Foundation Application class.
The Application class inherits the service Container class and interfaces ApplicationContract and HttpKernelInterface. Let’s say I have three classes, A, B, and C. The implementation of B depends on A, and the implementation of C depends on B. Using the traditional design pattern, when instantiating C, we should first require the definition file of A and B, and then instantiate A and B, and then inject these two objects into the instance of C. This is obviously a lot of work, and if the dependencies between classes were more complex, the programmer would spend a lot of energy dealing with the dependencies. Inversion of Control (IoC) is A new design pattern that automatically injects dependent classes A and B each time class C is instantiated. The Container class does just that. It takes objects of class C from the Container, instantiates the dependencies A, B, and inject them into C, and returns the instantiated C objects. Find the Container Container class definition file in the directory \vendor\laravel\framework\ SRC \Illuminate\Container:
Container implements the ArrayAccess interface and ContainerContract interface. ArrayAccess is a predefined interface in PHP that accesses objects like arrays. Implementing this interface requires implementing four methods: php.net/manual/zh/c…
I’m going to focus on the implementation of the ContainerContract interface. The Container class defines a number of protected properties:
protected static $instance; The currently existing container instance.
protected $resolved = []; Save the parsed class
protected $bindings = []; Closures, objects bound to containers
protected $methodBindings = []; Methods that bind to containers
protected $instances = []; Shared instances in containers
protected $aliases = []; Save the alias of the class
protected $abstractAliases = []; Alias to class name mapping of a class
protected $extenders = []; Service Extension of the service class
protected $tags = []; The label
protected $buildStack = []; The stack that holds objects during the current instantiation
protected $with = []; Parameters used when instantiating
public $contextual = []; Context context
protected $reboundCallbacks = []; The callback function that needs to be called when rebinding
protected $globalResolvingCallbacks = []; The callback function to be called when all classes are parsed
protected $globalAfterResolvingCallbacks = []; Callback function to be called after all class parsing is complete
protected $resolvingCallbacks = []; A callback function to be called when a class is parsed
protected $afterResolvingCallbacks = []; A callback function to be called after a class has been parsed
Methods in the Container class:
Public function when($concrete) defines a context binding.
Public function bound($abstract) checks whether the current class name or alias is already bound
Public Function Resolved ($abstract) Determines whether the class name or alias has resolved
Public function isShared($abstract) checks whether the instance isShared
Public function isAlias($name) Check whether it is an alias
public function bind(Concrete = null, $shared = false) register binding
protected function getClosure(Concrete) gets a closure that is called at parsing time
.
To get an instance of a class from the container, simply call the make method:
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
Copy the code
The make method takes two parameters, the string parameter $abstract and the array parameter $parameters. The first parameter is the class name or alias of the class, and the second parameter is the parameter that needs to be passed in to instantiate the class. You can see that the make method is actually a call to the resolve method:
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
protected function resolve($abstract, $parameters = [])
{
$abstract = $this->getAlias($abstract);
$needsContextualBuild = ! empty($parameters) || ! is_null(
$this->getContextualConcrete($abstract)
);
// If an instance of the type is currently being managed as a singleton we'll
// just return an existing instance instead of instantiating new instances
// so the developer can keep using the same objects instance every time.
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
$this->with[] = $parameters;
$concrete = $this->getConcrete($abstract);
// We're ready to instantiate an instance of the concrete type registered for
// the binding. This will instantiate the types, as well as resolve any of
// its "nested" dependencies recursively until all have gotten resolved.
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
// If we defined any extenders for this type, we'll need to spin through them
// and apply them to the object being built. This allows for the extension
// of services, such as changing configuration or decorating the object.
foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object, $this);
}
// If the requested type is registered as a singleton we'll want to cache off
// the instances in "memory" so we can return it later without creating an
// entirely new instance of an object on each subsequent request for it.
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
$this->fireResolvingCallbacks($abstract, $object);
// Before returning, we will also set the resolved flag to "true" and pop off
// the parameter overrides for this build. After those two things are done
// we will be ready to return back the fully constructed class instance.
$this->resolved[$abstract] = true;
array_pop($this->with);
return $object;
}
Copy the code
$this->instances = $this->instances = $this->instances = $this->instances = $this->instances = $this->instances $this->with; $this->with; $this->with; $this->with; If so, call the build method. During the build process, all the dependencies required by the class will be resolved and injected into the final required instance.
/**
* Instantiate a concrete instance of the given type.
*
* @param string $concrete
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function build($concrete)
{
// If the concrete type is actually a Closure, we will just execute it and
// hand back the results of the functions, which allows functions to be
// used as resolvers for more fine-tuned resolution of these objects.
if ($concrete instanceof Closure) {
return $concrete($this.$this->getLastParameterOverride());
}
$reflector = new ReflectionClass($concrete);
// If the type is not instantiable, the developer is attempting to resolve
// an abstract type such as an Interface or Abstract Class and there is
// no binding registered for the abstractions so we need to bail out.
if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
}
$this->buildStack[] = $concrete;
$constructor = $reflector->getConstructor();
// If there are no constructors, that means there are no dependencies then
// we can just resolve the instances of the objects right away, without
// resolving any other types or dependencies out of these containers.
if (is_null($constructor)) {
array_pop($this->buildStack);
return new $concrete;
}
$dependencies = $constructor->getParameters();
// Once we have all the constructor's parameters we can create each of the
// dependency instances and then use the reflection instances to make a
// new instance of this class, injecting the created dependencies in.
$instances = $this->resolveDependencies(
$dependencies
);
array_pop($this->buildStack);
return $reflector->newInstanceArgs($instances);
}
Copy the code
If $concrete is a closure, call the closure directly. If it is not a closure, use PHP’s ReflectionClass to resolve the dependencies, and inject the dependencies needed by the __construct constructor by parsing the parameters. For a detailed description of reflection classes, see the official PHP documentation: php.net/manual/zh/b… Once the dependencies are parsed, the whole IoC is complete by calling the reflector’s newInstanceArgs method and passing in the parsed dependencies to instantiate the required classes.