Interpretation of the Laravel service container (Ioccontainer)

Source: Internet
Author: User
Tags php class reflector
This article mainly introduces the Laravel service container (Ioccontainer) interpretation, has a certain reference value, now share to everyone, the need for friends can refer to

The core of Laravel is Ioccontainer, which is referred to as a "service container", a service container that is a powerful tool for managing class dependencies and performing dependency injection, Laravel function modules such as Route, eloquent ORM, Request, Response and so on, and so on, are actually related to the core-independent class modules provided, these classes from registration to instantiation, and ultimately used by us, in fact, is the Laravel service container responsible.

If there is no clear concept of what the service container is, recommend a blog to learn the ins and outs of the service container: Laravel Magical Service Container

There are two conceptual control reversals (IOC) and Dependency Injection (DI) in the service container:

Dependency Injection and control inversion are different descriptions of the same thing, and they describe different angles. Dependency Injection is a description from the perspective of the application, and the application relies on the container to create and inject the external resources it needs. While control inversion is described from the perspective of the container, the container controls the application, and the container reverses the external resources required to inject the application into the application.

In Laravel, the framework binds the various services it brings to the service container, and we can also bind custom services to the container. When an application needs to use a service, the service container will tell the service to resolve it and automatically resolve the dependencies between the services and then hand it over to the application.

This article explores how service bindings and parsing are implemented in Laravel

Service bindings

Common methods of binding services to containers are instance, bind, Singleton, alias. Let's take a look at the following separately.

Instance

When an existing object is bound to a service container, and then the service is resolved by name, the container will always return the instance of the binding.

$api = new Helpspot\api (new HttpClient), $this->app->instance (' Helpspot\api ', $API);

The object is registered in the $instnces property of the service container.

[     ' Helpspot\api ' + $api//$API is the object of the Api class, abbreviated here]

Bind

Bind Service to service container

There are three ways to bind:

1. Bind itself $this->app->bind (' Helpspot\api ', null); 2. Bind the closure $this->app->bind (' Helpspot\api ', function () {    return new Helpspot\api ();}); /closures directly provide class implementations $this->app->bind (' Helpspot\api ', function ($app) {    return new Helpspot\api ($app->make (' HttpClient ');}); /Closure Returns the class 3 that requires dependency injection. Binding interface and Implementation $this->app->bind (' illuminate\tests\container\icontainercontractstub ', ' illuminate\tests\container\ Containerimplementationstub ');

For the first case, in fact, in the Bind method inside the binding service before the service generated by the getClosure() closure, we look at the Bind method source code.

Public function bind ($abstract, $concrete = null, $shared = False) {$abstract = $this->normalize ($abstract);    $concrete = $this->normalize ($concrete); If $abstract is similar to an array [' illuminate/servicename ' = ' service_alias ']//extract alias "Service_alias" and register to $aliases[]// Note: The way the array binds aliases is removed in 5.4, alias bindings use the following alias method if (Is_array ($abstract)) {list ($abstract, $alias) = $this->extractali        As ($abstract);    $this->alias ($abstract, $alias);    } $this->dropstaleinstances ($abstract);    if (Is_null ($concrete)) {$concrete = $abstract; }//If only $abstract is provided, generate concrete closures here if (! $concrete instanceof Closure) {$concrete = $this->getclosure (    $abstract, $concrete);    } $this->bindings[$abstract] = Compact (' concrete ', ' shared ');    if ($this->resolved ($abstract)) {$this->rebound ($abstract); }}protected function Getclosure ($abstract, $concrete) {//$c is $container, the service container, which is passed to the variable return function ($c, $p Arameters = []) use ($abstract, $concrete) {$method = ($abstract = = $concrete)?        ' Build ': ' Make ';    return $c $method ($concrete, $parameters); };}

Bind registers the service in the $bindings property of the service container like this:

$bindings = [    ' helpspot\api ' = = [  ////Closure binding        ' concrete ' = = function ($app, $paramters = []) {            return $ap P->build (' Helpspot\api ');        },        ' shared ' = = false//If it is a singleton binding, this value is true    ]            ' illuminate\ Tests\container\icontainercontractstub ' =//Interface Implementation binding        ' concrete ' = ' illuminate\tests\container\ Containerimplementationstub ',        ' shared ' = False    ]]

Singleton

Public Function Singleton ($abstract, $concrete = null) {    $this->bind ($abstract, $concrete, true);}

The Singleton method is a variant of the Bind method, binding a class or interface that needs to be parsed only once to the container, and then the service will return the same instance for the call to the container.

Alias

Register the service and service aliases to the container:

Public Function alias ($abstract, $alias) {    $this->aliases[$alias] = $this->normalize ($abstract);}

The alias method, which is useful in the Bind method above, registers the corresponding relationship between the service alias and the service class in the $aliases attribute of the service container.
For example:

$this->app->alias (' \illuminate\servicename ', ' Service_alias ');

After you have bound the service, you can use it

$this->app->make (' Service_alias ');

Parsing the service object so that make does not have to write the longer class names, the use of the Make method has a great improvement in the experience.

Service parsing

Make: Resolves a service object from the service container that receives the class name or interface name you want to parse as a parameter

/** * Resolve The given type from the container. * * @param string $abstract * @param array $parameters * @return mixed */public function make ($abstract, array $param Eters = []) {The//getalias method assumes that $abstract is the alias of the binding, the True type name of the mapping is found from $aliases//If there is no mapping, $abstract is the real type name, and the $abstract is returned as is $abstrac    t = $this->getalias ($this->normalize ($abstract)); If the service is bound by instance (), it resolves directly to the service if (Isset ($this->instances[$abstract]) that returns the binding, {return $this->instan    ces[$abstract];    }//Get the $abstract interface corresponding to the $concrete (Implementation of the interface) $concrete = $this->getconcrete ($abstract);    if ($this->isbuildable ($concrete, $abstract)) {$object = $this->build ($concrete, $parameters); } else {//If the interface implements this binding method, it is required to make one more time after the interface is implemented ($abstract = = = $concrete) $object =    $this->make ($concrete, $parameters);    } foreach ($this->getextenders ($abstract) as $extender) {$object = $extender ($object, $this); }//If the service is registered in singleton modeCome in, put the built-in service object into the $instances,//Avoid rebuilding if ($this->isshared ($abstract)) {$this->instances[on next use $abstract    ] = $object;    } $this->fireresolvingcallbacks ($abstract, $object);    $this->resolved[$abstract] = true; return $object;}        protected function Getconcrete ($abstract) {if (! is_null ($concrete = $this->getcontextualconcrete ($abstract))) {    return $concrete; }//If there is no registration class implemented into the service container before $abstract, then the service container will assume that $abstract itself is the interface class implementation if (! Isset ($this->bindings[$abstract])) {RE    Turn $abstract; } return $this->bindings[$abstract [' Concrete '];} protected function isbuildable ($concrete, $abstract) {return $concrete = = = $abstract | | $concrete instanceof CL Osure;}

By combing the make method, we find that the function of the build method is to build the object of the parsed service, and look at the specific process of building the object. (The build process uses the reflection of the PHP class to implement the dependency injection of the service)

Public function build ($concrete, array $parameters = []) {//If the closure is executed directly and returns (corresponding to the closure binding) if ($concrete instanceof Closure    {return $concrete ($this, $parameters);    }//Use reflection Reflectionclass to reverse engineer the implementation class $reflector = new Reflectionclass ($concrete); If not instantiated, this should be an interface or abstract class, or the constructor is private if (! $reflector->isinstantiable ()) {if (! empty ($this->buildsta            CK) {$previous = implode (', ', $this->buildstack);        $message = "Target [$concrete] is not instantiable while building [$previous].";        } else {$message = "Target [$concrete] is not instantiable.";    } throw new Bindingresolutionexception ($message);    } $this->buildstack[] = $concrete;    Gets the constructor $constructor = $reflector->getconstructor ();        If the constructor is empty, the description does not have any dependencies, and the direct new returns if (Is_null ($constructor)) {Array_pop ($this->buildstack);    return new $concrete; }//Gets the dependency (formal parameter) of the constructor, and returns an array of Reflectionparameter objects representing each parameterNumber $dependencies = $constructor->getparameters ();    $parameters = $this->keyparametersbyargument ($dependencies, $parameters);    The dependency $instances required to build the constructor = $this->getdependencies ($dependencies, $parameters);    Array_pop ($this->buildstack); Return $reflector->newinstanceargs ($instances);}    Gets the dependent protected function getdependencies (array $parameters, array $primitives = []) {$dependencies = [];        foreach ($parameters as $parameter) {$dependency = $parameter->getclass (); A dependency value in $primitives (that is, the $parameters parameter of the build method) has been provided//$parameter->name returns the parameter name if (Array_key_exists ($parameter        ->name, $primitives)) {$dependencies [] = $primitives [$parameter->name]; } elseif (Is_null ($dependency)) {//parameter reflectionclass is NULL, the description is a basic type, such as ' int ', ' string ' $depen        dencies[] = $this->resolvenonclass ($parameter);  } else {//parameter is an object of a class, then use Resolveclass to parse the object out          $dependencies [] = $this->resolveclass ($parameter); }} return $dependencies;} Resolves objects of dependent class protected function resolveclass (Reflectionparameter $parameter) {try {//$parameter->getclass ()-&        Gt;name returns the class name (the type that the parameter declares in Typehint)//And then proceeds recursively to make (the dependency class will continue to be dependent on make, and then the dependent dependency is continued//until all dependencies have been resolved and the build ends)    return $this->make ($parameter->getclass ()->name); } catch (Bindingresolutionexception $e) {if ($parameter->isoptional ()) {return $parameter->getde        Faultvalue ();    } throw $e; }}

The service container is the core of Laravel, which solves the interdependence of objects through dependency injection, and defines the specific behavior by controlling the anti-transfer external (Route, eloquent these are external modules, they define the code of conduct themselves, These classes are responsible for you using the service container from registration to instantiation.

A class must be registered to this container before it can be extracted by the container. Since Laravel calls this container a service container, then we need a service to register, bind the service to the container, then provide the service and bind the service to the container, that is, the service provider (serviceprovider). The service provider is divided into two parts, register (registration) and boot (boot, initialize) due to the length of the content of the Laravel service provider, see another Laravel core interpretation-service provider (serviceprovider).

The above is the whole content of this article, I hope that everyone's learning has helped, more relevant content please pay attention to topic.alibabacloud.com!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.