Official explanation: IOC-Inversion of control DI-dependency injection

Xiao Ming used to be very poor, homeless and homeless. Now rich, they also want to have their own house, this time Xiao Ming thought, or go back home to build a house, one can live, two can honor the family, this time, Xiao Ming needs to build a house; Later, Xiao Ming thought, why not directly buy a flat in the city, life is more colorful and convenient. Therefore, Xiao Ming found a real estate agent (IOC container) to buy a house (dependency injection), eventually Xiao Ming soon lived in his own house, very happy…

Ming depends on the house. Ming goes from building his own house (he “controls” the house himself) to buying a house with an agent (letting the agent “control” the house), which is called inversion of control, or IOC; The real estate agent directly provides the house to Xiao Ming according to his needs (of course, Xiao Ming pays for it), which is called dependency injection, or DI.

Of course, the house is not built by the real estate agent, but by the developer, who is the service provider. Thirty years later, The layout of xiaoming’s house is out of step with The Times, and he lives in an uncomfortable place. He wants to transform/renovate the house, but the cost of time is too high. Therefore, Xiaoming finds a real estate agent to buy a house, and xiaoming soon moves to a new house… This also embodies the single responsibility principle of classes in object orientation.

Objective To adopt IOC thought and DI design mode, the main purpose is: decoupled driving: long-distance love. Even if there is a distance between, but also does not affect the true love. Native code implementation

The traditional writing

<? php /** * Create by PhpStorm * User : Actor * Date : 2019-11-01 * Time : 22:03 */ /** ** private $private; Public function __construct($name) {$this-> name = $name; } public function house () {$house = new house ('0001', '0001', '180m2 '); $this-> $this-> $this-> $this-> $this-> "; Echo 'I bought '. $new -> get house ().' house '."\r\n"; Echo 'I bought '. $new house -> get area ().' house '. }} /** * private $private $private; Private $size; Private $size; Public function __construct($this-> house id = $this-> house ID; $this-> house id = $this-> house ID; If ($this-> 0) {$this-> 0; $this-> area = $this-> area; } public function getSize () {return $this-> getsize; $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> }} $daming = new buyer (' daming '); $daming -> buy a house (); ? >Copy the code

The above code output

[actor20:49:55] /projects/phpAdvanced$PHP iocdrivecar.php /projects/phpAdvanced$Copy the code

Adopt the idea of IOC and DI to achieve

<? php /** * Create by PhpStorm * User : Actor * Date : 2019-11-01 * Time : 22:03 */ /** ** private $private; Public function __construct($name) {$this-> name = $name; } public function ($this-> name) {$this-> name; Echo 'I bought '. $new -> get house ().' house '."\r\n"; Echo 'I bought '. $new house -> get area ().' house '. }} /** * private $private $private; Private $size; Private $size; Public function __construct($this-> house id = $this-> house ID; $this-> house id = $this-> house ID; If ($this-> 0) {$this-> 0; $this-> area = $this-> area; } public function getSize () {return $this-> getsize; $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> }} /** * real estate agent, is we say ioc * Class real estate agent */ Class real estate agent {private $in sale source = []; $Bindings private $bindings = []; $resolved private $resolved = []; $instances private $instances private $instances = []; $aliases / $abstractAliases private $abstractAliases = []; Public function ($this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> $this-> } public function return ($this-> $this-> $this-> $this-> $this-> $this-> $this)(); $this-> {$this-> {$this-> {$this-> {$this-> {$this-> {$this-> {$this-> {$this-> {$this-> {$this-> { } public function $this-> $this-> $this-> $this-> $this-> $this-> $this-> } $app = new ();} $app = new (); $app-> function(){return new house (' 100m2 ', '100m2 '); }); $app-> function(){return new house ('1002', '1002', '150m2 '); }); $app-> $app-> $app-> $app-> $app-> $app-> $app-> $app; }); $app-> = function(){$app-> = function(){$app-> = function(); }); / / echo $app - > intention person information (' xiaoming) - > to buy a house ($app - > access to the source in the housing (' four rooms two hall ')); $app-> $app-> $app-> $app-> $app $new = $app-> get the source of the house in sale (' four rooms two rooms '); $intention person -> buy a house ($new house); ? >Copy the code

The above program output

[actor20:49:43] /projects/phpAdvanced$PHP iocdrivecar.php /projects/phpAdvanced$Copy the code

An essential analysis of IOC and DI

From the code above, we can see that the real estate agent, as IOC, is essentially an array (can be a one-dimensional array, can be a multidimensional array).

In fact, in the Laravel framework, the attributes $bindings, $resolved, $instances, $aliases and $abstractAliases in the Container object are managed from different dimensions.

In the above example, if from the business logic point of view, it is nothing more than buyers want to buy a house, the main categories are: buyers, commercial housing.

If it is implemented according to the traditional code, then the buyer object is strongly dependent on the commodity housing object, because new commodity housing is needed in the buyer category ().

If the idea of IOC and DI is adopted and the IOC container of real estate agent is added, the pre-sale registration of commercial houses should be carried out at the real estate agent first, and the intention registration of buyers should also be carried out at the real estate agent. The buyers should rely on the commercial houses and adopt dependency injection, namely: Let the real estate agent directly after the instantiation of commercial housing object into the object of the buyer, the buyer object does not need to pay attention to how to instantiate, just take over the use of the line. Laravel framework IOC core source code — binding we come to a simple look at the Laravel framework core container binding is how to achieve?

The following code is in the Illuminate Container Container class

  1. Binding via the instance method
/**
        * Register an existing instance as shared in the container.
        *
        * @param  string  $abstract
        * @param  mixed   $instance
        * @return mixed
        */
       public function instance($abstract, $instance)
       {
           $this->removeAbstractAlias($abstract);
   
           $isBound = $this->bound($abstract);
   
           unset($this->aliases[$abstract]);
   
           // We'll check to determine if this type has been bound before, and if it has
           // we will fire the rebound callbacks registered with the container and it
           // can be updated with consuming classes that have gotten resolved here.
           $this->instances[$abstract] = $instance;
   
           if ($isBound) {
               $this->rebound($abstract);
           }
   
           return $instance;
       }
Copy the code

Use this method to register that object instances bound to containers are shared.

2. The bind method

/** * Register a binding with the container. * * @param string $abstract * @param \Closure|string|null $concrete * @param bool $shared * @return void */ public function bind($abstract, $concrete = null, $shared = false) { $this->dropStaleInstances($abstract); // If no concrete type was given, we will simply set the concrete type to the // abstract type. After that, the concrete type to be registered as shared // without being forced to state their classes in both of the parameters. if (is_null($concrete)) { $concrete = $abstract; } // If the factory is not a Closure, it means it is just a class name which is // bound into this container to the abstract type and we will just wrap it // up inside its own Closure to give us more convenience when extending. if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact('concrete', 'shared'); // If the abstract type was already resolved in this container we'll fire the // rebound listener so that any objects which have already gotten resolved // can have their copy of the object updated via the listener callbacks. if ($this->resolved($abstract)) { $this->rebound($abstract); }}Copy the code

How do you use the bind method to bind an object registration to a container? As follows, the bind method binds the closure to the container

$this->app->bind('User\API', function ($app) {
        return new User\API($app->make('UserLogin'));
  });
Copy the code

Similarly, the bind method removes the old instance, then places the new instance in a closure and binds it to the container. If the second argument is not a closure, the class name is wrapped in the closure using the getClosure method, and the bound class is resolved in the closure using either the make or build methods. The closure and whether it is shared will be put into the $this->bind[] array when it is parsed. \

The singleton method

/**
       * Register a shared binding in the container.
       *
       * @param  string  $abstract
       * @param  \Closure|string|null  $concrete
       * @return void
       */
      public function singleton($abstract, $concrete = null)
      {
          $this->bind($abstract, $concrete, true);
      }
Copy the code

$this->bind() ¶ The singleton method ends up calling $this->bind(), but the object bound to the container by the Singleton () method is parsed only once, and all subsequent calls return the same instance.

Laravel framework IOC core source code – analysis

Resolve the given type from the container. ** @param string $abstract * @param array $parameters * @return mixed * * @throws \Illuminate\Contracts\Container\BindingResolutionException */ public function make($abstract, array $parameters = []) { return $this->resolve($abstract, $parameters); } /** * Resolve the given type from the container. * * @param string $abstract * @param array $parameters * @param bool $raiseEvents * @return mixed * * @throws \Illuminate\Contracts\Container\BindingResolutionException */ protected function resolve($abstract, $parameters = [], $raiseEvents = true) { $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; } if ($raiseEvents) { $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; } /** * 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()); } try { $reflector = new ReflectionClass($concrete); } catch (ReflectionException $e) { throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e); } // 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. try { $instances = $this->resolveDependencies($dependencies); } catch (BindingResolutionException $e) { array_pop($this->buildStack); throw $e; } array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); }Copy the code

As you can see from the build() method in the third paragraph above, if the binding registers a closure function, it returns the execution of the closure function directly

if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }
Copy the code

If the binding registers a class name, the object is instantiated using PHP’s ReflectionClass and returned to the caller. The rest of the bind() method does just that.

Finally, in the resolve() method

$this->resolved[$abstract] = true;
Copy the code

The representation has been resolved successfully.

The above content hopes to help you, more free PHP factory PDF, PHP advanced architecture video materials, PHP wonderful good article can be wechat search concerns: PHP open source community

2021 Jinsanyin four big factory interview real questions collection, must see!

Four years of PHP technical articles collation collection – PHP framework

A collection of four years’ worth of PHP technical articles – Microservices Architecture

Distributed Architecture is a four-year collection of PHP technical articles

Four years of PHP technical essays – High Concurrency scenarios

Four years of elite PHP technical article collation collection – database