1. Configure the environment
- The source code of laravel5.1.46
- PHPstorm development tools
- Install the XDEBUG extension in the server integration environment (MAMP, WAMP)
2. The key point
- [Fixed] When using PHPstorm+ XDEBUG to read the source code, click (F7 button) to enter the next method to execute the code;
- Binding services in the Laravel framework binds the abstract service provider and a callback function to the bingings property of the service container;
- The Laravel framework resolves services by resolving and instantiating the specific services of service providers that are bindings in the Bindings attribute.
I’ll break down the key points below.
3. Bind services
1. Bind services
The binding service in Laravel framework uses the bind method to bind an abstract class (interface) and a concrete class binding to the service container and record them in the Bindings property of the service container. The code is as follows:
File Illuminate, the Container, the Container. PHP publicfunction bind($abstract.$concrete = null, $shared = false) {// Check whether the abstract class is an arrayif (is_array($abstract)) {
list($abstract.$alias) = $this->extractAlias($abstract);
$this->alias($abstract.$alias); } // Delete old instances and aliases$this->dropStaleInstances($abstract); // Determine whether the concrete class is emptyif (is_null($concrete)) {
$concrete = $abstract; } // Determine if the concrete class is an instance of a callback functionif (! $concrete instanceof Closure) {
$concrete = $this->getClosure($abstract.$concrete); } // Bind abstract and concrete classes to the service container$this->bindings[$abstract] = compact('concrete'.'shared'); // Determine whether the abstract class has been resolvedif ($this->resolved($abstract)) {
$this->rebound($abstract); }}Copy the code
Of course, I just write the method of the implementation of the code, due to the space is limited, did not call the method of the code listed, please see the source code.
As can be seen from the above code, the binding service is to record an abstract class and a concrete class in the Bindings attribute in the service container, where the abstract class is the key name and the key value is an array. The key values in the array are Concrete and shared respectively and the key value isThe bindings attribute can be queried by xdebug, as shown in the following figure:
2. Parse services
The Laravel framework parsing service uses: When parsing, determine whether to share the obtained instance in the Singleton property array of the service container by using the shared property. The resolved abstract class will be recorded in the Resolved property array.
File Illuminate, the Container, the Container. PHP publicfunction make($abstract.$parameters= []) {// Get the alias$abstract = $this->getAlias($abstract); // Check if an instance of an abstract class is in a singleton groupif (isset($this->instances[$abstract]) {return $this->instances[$abstract]; } // Get the concrete class of the binding based on the abstract class name$concrete = $this->getConcrete($abstract); // Instantiate the concrete classif ($this->isBuildable($concrete.$abstract)) {
$object = $this->build($concrete.$parameters);
} else {
$object = $this->make($concrete.$parameters); } // Execute the abstract class extension foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object.$this); } // Determine whether the abstract class is shareableif ($this->isShared($abstract)) {
$this->instances[$abstract] = $object; } // Parse all callbacks to the abstract class$this->fireResolvingCallbacks($abstract.$object); // Record the parsed abstract class in the parsed array$this->resolved[$abstract] = true; // Return the instance objectreturn $object;
}
Copy the code
Theories need to be tested in practice, and I’ll use a complicated service binding and parsing process for Illuminate\Contracts\Http\Kernel and App\Http\Kernel.
3. Specific examples
Again, follow the code sequence above, looking at the binding service first and then parsing the service.
3.1) Binding services
The first is to bind the service to the app.php file as follows:
File the bootstrap \ app. PHP$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
# At this point, someone will say, there is no binding action, and then look downFile Illuminate, the Container, the Container. PHP publicfunction singleton($abstract.$concrete = null)
{
$this->bind($abstract.$concrete.true);
}
The # bind method is the one that binds the service in the code above
Copy the code
As you can see from the code above, the shared value of Illuminate\Contracts\Http\Kernel in the Bindings array is true when the Singleton binding service is used, which means it ends up in the shared Singleton group.
Let’s talk about parsing services, which is a bit more complicated
3.2) Parsing services
File public \ index. PHP$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); File Illuminate, Foundation and Application. PHP publicfunction make($abstract, array $parameters= []) {// Get the alias$abstract = $this->getAlias($abstract); // Check if it is in a lazy-loaded array of service providersif (isset($this->deferredServices[$abstract]) {$this->loadDeferredProvider($abstract); } // call the make method of the parent classreturn parent::make($abstract.$parameters);
}
Copy the code
The service is parsed using the make method. First, the make method passes in an interface. The make method runs in the Application file to get the alias of the interface, checks if it is in a lazily loaded array of service providers, and finally calls the make method of the parent class. That’s the make method in the Container file;
The make method on the parent class also gets the alias and determines if it’s instantiated, and then gets the concrete class that binds to Illuminate\Contracts\Http\Kernel, which as we know is App\Http\Kernel, and determines if it’s instantiable. The build method is called when it can be instantiated. The code for the build method is as follows:
File Illuminate, the Container, the Container. PHP publicfunction build($concrete, array $parameters= []) {// Determine whether a concrete class is an instance of a callback functionif ($concrete instanceof Closure) {
return $concrete($this.$parameters); } // instantiate the reflection class$reflector = new ReflectionClass($concrete); // Determine whether a concrete class can be instantiatedif (! $reflector->isInstantiable()) {
$message = "Target [$concrete] is not instantiable.";
throw new BindingResolutionContractException($message); } // Record the concrete class in the instantiation stack$this->buildStack[] = $concrete; // Get the constructor of the concrete class from the reflection class$constructor = $reflector->getConstructor(); // Check whether the constructor is emptyif (is_null($constructor)) {
array_pop($this->buildStack);
return new $concrete; } // Get the constructor arguments$dependencies = $constructor->getParameters(); // Re-write the parameter with the parameter name$parameters = $this->keyParametersByArgument(
$dependencies.$parameters); // Get dependent parameters$instances = $this->getDependencies(
$dependencies.$parameters); // Push concrete classes out of stack array_pop($this->buildStack); // Instantiate the concrete class with the parameters of the reflection classreturn $reflector->newInstanceArgs($instances);
}
Copy the code
Now that we know we need to call the build method, we pass in the callback function, which is encapsulated by the App\Http\Kernel class, and is executed when the build method code above is executedthis, $parameters); , and this is the callback function obtained when binding the concrete class, the code is as follows:
File Illuminate, the Container, the Container. PHP protectedfunction getClosure($abstract.$concrete)
{
return function ($c.$parameters = []) use ($abstract.$concrete) {
$method = ($abstract= =$concrete)?'build' : 'make';
return $c->$method($concrete.$parameters);
};
}
Copy the code
The value of $abstract is Illuminate\Contracts\Http\Kernel and the value of $concrete is: App\Http\Kernel, so we call make, and now when we call make we pass App\Http\Kernel, and then we run the make method in the Illuminate\Foundation\ application.php file, Call the make method of the parent class to check whether it is in the singleton group. Then get the concrete class bound to this class. If it is not, return itself. Pass in the name of the class, determine whether the class can be instantiated, get the constructor, and get the constructor parameters,Router resolves all dependencies according to the parameters of the reflection class (Gets an instance of a dependent class), the code is as follows:
File Illuminate, the Container, the Container. PHP publicfunction getDependencies(array $parameters, array $primitives = [])
{
$dependencies = [];
foreach ($parameters as $parameter) {
$dependency = $parameter->getClass();
if (array_key_exists($parameter->name, $primitives)) {
$dependencies[] = $primitives[$parameter->name];
} elseif (is_null($dependency)) {
$dependencies[] = $this->resolveNonClass($parameter);
} else {
$dependencies[] = $this->resolveClass($parameter); }}return (array) $dependencies;
}
public function resolveClass(ReflectionParameter $parameter)
{
try {
return $this->make($parameter->getClass()->name);
} catch (BindingResolutionContractException $e) {
if ($parameter->isOptional()) {
return $parameter->getDefaultValue();
}
throw $e; }}Copy the code
The code above addresses a class dependency, and the App\Http\Kernel dependencies are Illuminate\Contracts\Foundation\Application and Illuminate\Routing\Router, So pass in the resolveClass method $app to get an instance of Illuminate\Contracts\Foundation\Application and pass in $router to get an instance of Illuminate\Routing\ router.
$app->getClass()->name $app->getClass()->name Illuminate\Contracts\Foundation\Application, and this interface has an alias, and the alias is app, and app is already instantiated and stored inIn the instances variable, instantiate the App\ http\ Kernel class in the last line of the build method. Return to the make method of the Container and record App\ http\ Kernel in the parsed array. Return the App\Http\Kernel instance object or return to the make method (in this case, the make method that parses Illuminate\Contracts\Http\Kernel), Illuminate\Contracts\Http\Kernel is also recorded in the parsed array.
# # 4. Summary
The core process of binding and resolving a service is described above. The following methods are involved in the binding and resolving process:
1). The service bind method and Singleton method are shared in the Singleton group after the service is finally parsed; The main process of resolving a service is to make it in the Container class and instantiate it in the build class, which is instantiated by the reflection class.