Laravel Service container IOC (control inversion) and DI (Dependency injection) IOC container, Laravel core
The core of Laravel is one IoC 容器
, according to the document, called "", which, as the 服务容器
name implies, provides a range of services required throughout the framework. As a beginner, many people will be puzzled by this concept, therefore, I intend to start from a number of basic content, through the understanding of object-oriented development depends on the emergence and solution, to gradually uncover the "dependency injection" veil, and gradually understand this magical design concept.
This article more than half the content by giving the reader an example of what is IoC(控制反转)
and DI(依赖注入)
, by understanding these concepts, to go deeper. For more information on the usage of the Laravel service container, read the documentation.
The story of the emergence of the IoC container
There are many articles on the IoC container, which I have written before. But now I'm going to start again with the inspiration of the moment, so let's get started.
Superman and the ability to rely on the production!
Object-oriented programming, there are a few things without a moment of contact: 接口
, and 类
对象
. In this, the interface is the prototype of the class, a class must abide by its implementation of the interface, the object is a class instantiation of the product, we call it an instance. Of course, this is certainly not conducive to understanding, we actually write some of the useless code-assisted learning.
In a world of monsters, you'll need some super characters to settle.
We put a "Superman" as a class,
Class Superman {}
We can imagine that a Superman must have at least one power when he was born, and this ability can be abstracted as an object, defining a class that describes him for this object. A super ability must have a variety of properties, (operation) methods, this full of imagination, but at present we first roughly define a property only "super ability", as to what can do, we will enrich later:
Class Power {/** * capability value */protected $ability; /** * Capacity range or distance */protected $range; Public function __construct ($ability, $range) {$this->ability = $ability; $this->range = $range; }}
At this point, we go back and revise the "Superman" class, so that a "Superman" is created with a super power:
Class superman{protected $power; Public Function __construct () {$this->power = new Power (999, 100); }}
That way, when we create a "Superman" instance, we also create a "super power" instance, but we see that there is inevitably a dependency between "Superman" and "Super power."
The so-called "dependence", that is, "if I rely on you, I can not leave you."
Such dependencies are ubiquitous in a project that implements object-oriented programming. A small amount of dependency is not too intuitive, and as this example unfolds, we slowly realize how a nightmare experience is when you rely on reaching a magnitude. Of course, I will naturally explain how to solve the problem.
A heap of mess--awful dependence
In the previous example, the ability to instantiate a class is a specific ability, but we know that Superman's ability is diversified, each ability of the method, attributes are not small differences, can not be described by a class completely. We are now making changes, and we assume that Superman can have the following kinds of abilities:
- Flight, the properties are: flight speed, continuous flight time
- Brute force, attributes are: Strength value
- Energy bombs, the properties are: damage, shooting distance, the number of simultaneous shots
We created the following classes:
Class flight{ protected $speed; protected $holdtime; Public function __construct ($speed, $holdtime) {}} class force{ protected $force; Public function __construct ($force) {}} class shot{ protected $atk; protected $range; protected $limit; Public function __construct ($ATK, $range, $limit) {}}
* For the sake of saving things I didn't write __construct()
out all of this constructor in detail, only the arguments that need to be passed.
OK, so our Superman is a little "busy". In the case of Superman initialization, we will instantiate the abilities that they have as needed, roughly as follows:
Class superman{ protected $power; Public function __construct () { $this->power = new Fight (9, +); $this->power = new Force; $this->power = new Shot (2); /* $this->power = Array ( new Force, new Shot (2) ); */ }}
It is not good that we need to instantiate a series of required classes manually within the constructor (or in other ways). It can be imagined that if demand changes (different monsters traverse the Earth), need more targeted new abilities, or need to change the ability of the method, we must 重新改造
Superman. In other words, I have to re-create a superman while changing my abilities. The efficiency is too low! The new Superman has not yet been created to complete the world has long been destroyed.
At this time, brainwave people think: why not? Superhuman ability can be changed at any time, only need to add or update a chip or other device what (think Iron Man not). That way, you don't have to do it all over again.
Yes, that's it.
We should not manually in the "Superman" class to solidify his "ability" to initialize the behavior, and transferred externally responsible, by the external creation of super-power modules, devices or chips (we collectively called "module"), implanted in the Superman body of an interface, this interface is an established, as long as the "module" Devices that satisfy this interface can be used by Superman to enhance and increase one's abilities. This behavior, which is externally responsible for its reliance on demand, can be called " 控制反转(IoC)
."
Factory mode, rely on transfer!
Of course, there are several ways to implement control inversion. Before that, let's get to know some fun things first.
We can think of components, tools (or Superman's modules), is a kind of production of gadgets, the production of course is "factory (Factory)", so someone put forward such a model: 工厂模式
.
The factory model, as the name implies, is an instance of an external thing that a class relies on, which can be created by one or more "factories" as a development pattern, that is, " 工厂模式
".
In order to give Superman the ability to create super-power modules, we created a factory that can produce a wide variety of modules and only need to pass one method:
Class supermodulefactory{public function Makemodule ($moduleName, $options) { switch ($moduleName) { Case ' fight ': return new Fight ($options [0], $options [1]); Case "Force": return new Force ($options [0]); Case ' Shot ': return new Shot ($options [0], $options [1], $options [2]);}}
At this time, Superman can use this factory at the beginning of creation!
Class superman{ protected $power; Public function __construct () { //initial chemical plant $factory = new Supermodulefactory; The required modules are manufactured through factory-provided methods $this->power = $factory->makemodule (' fight ', [9]); $this->power = $factory->makemodule (' Force ', [a]); $this->power = $factory->makemodule (' Shot ', [2]); /* $this->power = Array ( $factory->makemodule (' Force '), $factory->makemodule (' Shot ' , [2]) ; */ }}
It can be seen that we no longer need to initialize many third-party classes at the beginning of Superman initialization, simply initializing a factory class to meet the requirements. But that doesn't seem to be the same as before, just not so many new
keywords. In fact, if we just change this class A little, you'll see the real meaning and value of the factory class.
Class superman{ protected $power; Public function __construct (array $modules) { //initial chemical plant $factory = new Supermodulefactory; Manufacturing the required module ($modules as $moduleName + $moduleOptions) through factory-provided methods { $this->power[] = $factory Makemodule ($moduleName, $moduleOptions);}}} Create Superman $superman = new Superman ([' Fight ' = [9], ' Shot ' = [99, 50, 2]]);
The result of the modification is now satisfactory. Now, the creation of "Superman" no longer relies on any "super power" class, if we modify or add new abilities, we only need to modify SuperModuleFactory
. It's no longer necessary to re-edit Superman's class files as we expand our capabilities, making it easy for us. But that's just the beginning.
Go further! An important component of the IoC container--Dependency Injection!
The "Superman" dependence on "super Power" becomes "Superman" 's reliance on the "Super Power Module Factory", and it becomes more handy to deal with small monsters. But that, as you can see, depends on the fact that reliance on multiple external dependencies has become a dependency on a "factory." If the factory is in some trouble, the problem becomes very tricky.
In fact, in most cases, the factory model is sufficient. The disadvantage of the factory model is that the interface is unknown (that is, there is no good contract model, I will explain it immediately), and produce a single object type. In short, it is still not flexible enough. Despite this, the factory model is still excellent and is suitable for most situations. But in order to explain the back 依赖注入
, here we first exaggerate the defects of the factory model.
We know that the Superman relies on the module, we require a unified interface, so as to the Superman body injection interface docking, and ultimately to enhance the effect of super power.
In fact, I lied before, not just a bunch of little monsters, but more big monsters. Hey. Well, at this point it seems that the production capacity of the factory is somewhat inadequate-due to the Factory mode, all the modules have been arranged in the factory class, if there are new, advanced modules to join, we must modify the factory class (like adding new production lines):
Class supermodulefactory{public function Makemodule ($moduleName, $options) { switch ($moduleName) { Case ' fight ': return new Fight ($options [0], $options [1]); Case "Force": return new Force ($options [0]); Case ' Shot ': return new Shot ($options [0], $options [1], $options [2]); Case ' more ': ... .. Case ' and more ': ... Case ' and more ': ... Case ' Oh no! Its too many! ': ... }}
See no ... What a nightmare it feels like!
In fact, the inspiration is one step ahead! You might think of a more flexible approach! Yes, the next step is our main supporting role today--di (Dependency injection)
As the demand for super-power modules continues to grow, we need to assemble the entire world of high IQ talent, solve problems together, should not only have a few factory monopoly responsible. But the high IQ people are very conceited, think their ideas are right, the creation of the super-power module does not have a unified interface, natural can not be used normally. At this point we need to propose a contract, so that whoever created the module, all conform to such interface, natural can be used normally.
Interface supermoduleinterface{ /** * Super Ability Activation Method * any one of the abilities must have this method and have a parameter array $target target, Can be one or more, self or others */public function activate (array $target);}
Above, we have set up an interface (the specification, contract of the Super-power module), and all the created modules must abide by the specification before they can be produced.
In fact, this is the 接口( interface )
usefulness and significance of PHP! Many people think, why does PHP need interface This kind of thing? Isn't it a language like Java or C #? So, as long as a normal object-oriented programming language (although PHP can be process-oriented), it should have this feature. Because a 对象(object)
template or prototype itself is 类 (class)
a concrete thing created after instantiation, and sometimes there are a lot of classes (class) When implementing a unified approach and different functions (or features), there is a need for a contract, Let's write an interface that can be replaced at any time without having an impact. This rigid specification, which is presented by the programming language itself, increases the number of outstanding features.
Although some around, but through our next example, we will slowly understand the benefits of the interface.
At this time, those who put forward a better ability module of high IQ talent, following this interface, created the following (module) class:
/*** x Super Energy */class XPower implements supermoduleinterface{public function activate (array $target) { // This is just an example: Specific self-brain complement }}/*** Ultimate Bomb (so vulgar) */class Ultrabomb implements supermoduleinterface{public function activate (array $ Target) { //This is just an example: Specific self-brain supplement }}
At the same time, in order to prevent some "brick house" smart, or some traitor malicious mischief, do not abide by the contract random manufacturing module, affect Superman, we on the Superman initialization method to transform:
Class superman{ protected $module; Public function __construct (supermoduleinterface $module) { $this->module = $module }}
Makeover finished! Now, when we initialize the "Superman" class, the supplied module instance must be an implementation of an SuperModuleInterface
interface. Otherwise, the error will be indicated.
It is because of Superman's creation becomes easy, a Superman also does not need too many abilities, we can create multiple superhuman, and inject the need ability module separately. In this case, although a Superman has only one super ability, but Superman is more likely to become more, we are not afraid of monsters!
Now some people are wondering, what are you going to say 依赖注入
?
In fact, the above-mentioned content is exactly the dependency injection.
What's it called 依赖注入
?
From the beginning to the present series of dependencies, as long as not by internal production (such as initialization, constructor __construct
through the factory method, self-manual new), but by external parameters or other forms of injection, belong to 依赖注入(DI)
. Is it enlightened? In fact, it's that simple. The following is a typical dependency injection:
Super Power Module $supermodule = new XPower; Initialize a Superman and inject a power module dependent $superman = new Superman ($superModule);
There is so much to be said about the main supporting role of dependency injection in this article. With the understanding of dependency injection, we can continue to delve into the problem. Slowly approaching the protagonist of today ...
A more advanced Factory--IOC container!
Just listed a section of code:
$superModule = new XPower; $superMan = new Superman ($superModule);
Readers should be able to see, manually create a super-power module, manually create Superman and inject the ability to create a module just now. Oh, manual.
Modern society, should be efficient production, clean workshop, perfect automation assembly.
A bunch of monsters are coming, so inefficient production of Superman is unrealistic, we need automation--at most one command, a mighty army to meet. We need an advanced production workshop where we simply submit a script to the production floor and the factory is able to automate production through instructions. This more advanced plant is the sublimation of the factory model IoC 容器
.
Class container{ protected $binds; protected $instances; Public function bind ($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$ Abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } Public function make ($abstract, $parameters = []) { if (isset ($this->instances[$abstract])) { return $this->instances[$abstract]; } Array_unshift ($parameters, $this); Return Call_user_func_array ($this->binds[$abstract], $parameters);} }
At this time, a very coarse container was born. It is very humble now, but it does not prevent us from further promoting him. Look now and see how this container is used!
Create a container (later called a super Factory) $container = new Container; Add Superman's production script to the Super Factory $container->bind (' Superman ', function ($container, $moduleName) { return new Superman ($ Container->make ($moduleName));}); Add the production script $container->bind (' XPower ', function ($container) {return new XPower;}) of the super-power module to the Super Factory ;//Ibid. $container-& Gt;bind (' Ultrabomb ', function ($container) { return new Ultrabomb;});//****************** ornate split line *************** Start production $superman_1 = $container->make (' Superman ', [' XPower ']), $superman _2 = $container->make (' Superman ', [' Ultrabomb ']); $superman _3 = $container->make (' Superman ', [' XPower ']);//... Feel free to add
Did you see that? With the initial 绑定(bind)
operation, we registered some production scripts with the Super factory, which are executed when the production order is released. Did you find it? We have completely lifted the Superman and Super Power module dependency, and more importantly, the container class and they do not have any dependence! We register, bind to the container to add a section of the callback can be executed (can be anonymous functions, non-anonymous functions, methods of the Class) as an instance of the production of a script, only when the real 生产(make)
operation is invoked to execute, will be triggered.
Such a way makes it easier for us to create an instance while resolving its dependencies and be more flexible. When there is a new requirement, simply bind a "production script" separately.
In fact, the real IoC container is more advanced. In our case, we still need to manually provide the module parameters that Superman needs, but the real IoC container will automatically search for the matching dependency requirements in the registered, bound heap of instances based on the class's dependency requirements, and automatically inject it into the constructor parameters. This is exactly what the Laravel Framework service container does. This is not a problem in theory, but I will not write it in this article because ... I'm too lazy to write.
But I tell you, this kind of automatic search relies on the requirements of the function, through the 反射(Reflection)
implementation, exactly, PHP perfect support reflection mechanism! About reflection, PHP official documents have detailed information, and Chinese translation basic coverage, enough to study and study!
Now, so far, we are no longer afraid of monsters. High IQ talent brainstorming, orderly, according to the interface contract to create a standard super-capacity module. Superman starts mass production. In the end, everyone is Superman, you can also be Oh!
Return to the normal world. We began to re-examine the core of laravel.
Now, we begin to slowly read the core of Laravel. In fact, the core of Laravel is an IOC container, and it happens to be the advanced IOC container that I mentioned earlier.
It can be said that the core of Laravel itself is very lightweight, and there is nothing very magical and substantial application function. A lot of people use a variety of functional modules such as,, and so Route(路由)
Eloquent ORM(数据库 ORM 组件)
on, and so Request and Response(请求和响应)
on, are actually related to the core of the class module provided, these classes from registration to instantiation, and ultimately you use, in fact, are laravel service container responsible.
We use the most common Route
classes as examples. You might often see a route definition like this:
Route::get ('/', function () {//bla bla bla ...});
In fact, the Route
class is defined in this namespace: Illuminate\Routing\Router
, the file vendor/laravel/framework/src/Illuminate/Routing/Router.php
.
What is it that we find by opening that this series of methods of this class, such as, and get
post
any
so on, are not static methods? Don't worry, we continue.
Service Provider
In the previous section of the IoC container, we mentioned that a class needs to be bound and registered in a container before it can be "manufactured."
Yes, a class must be registered to this container before it can be extracted by the container. Since Laravel calls this container a service container, 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 服务提供者(ServiceProvider)
.
Although, binding a class to a container is not necessarily a pass 服务提供者(ServiceProvider)
.
However, we know that sometimes our classes, modules will need other classes and components of the situation, in order to ensure that the initialization phase does not occur when the required modules and components are not registered, laravel the registration and initialization behavior to split, registration can only register, initialization is initialized. The product of the split is now 服务提供者
.
The service provider is divided into two sections, register(注册)
and boot(引导、初始化)
, for specific reference documents. register
responsible for registering the "script" to the container, but note that the registration section does not have a dependency on the unknown, and if so, move to the boot
part.
Façade
Let's now answer Route
the question about how the method can be accessed statically. In fact, the question is written on the document, which simply means simulating a class, providing a static Magic method __callStatic
, and mapping that static method to a real method.
The classes we use Route
are actually Illuminate\Support\Facades\Route
class_alias()
created through functions 别名
, and this class is defined in the file vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php
.
We open the file to see ... Eh Why is there only such a simple piece of code?
namespace Illuminate\support\facades; /*** @see \illuminate\routing\router*/class Route extends facade { /** * Get The registered name of the component. * * @return String * /protected static function Getfacadeaccessor () { return ' router '; }}
In fact, look closely, you will find that this class inherits a Facade
class called, the answer is almost solved here.
In the simple definition above, we see that the getFacadeAccessor
method returns a route
, what does that mean? In fact, this value has been ServiceProvider
registered, you should know what is registered, of course, the real Route class!
Some people will ask, how facade is realized. I do not want to say too thin, one is my lazy, another reason is that they find something easier to understand, not easy to forget. I have already said a lot of details and suggest that we should study it on our own.
So far, we've talked about it almost.
Manuscripts: Diligent Learning qkxue.net
Extended reading:
Laravel Learning Notes-the Magical service container
Http://qkxue.net/info/81116/laravel
Laravel service Container IoC (control inversion) and DI (Dependency injection)