PHP Dependency Injection Deep understanding

Source: Internet
Author: User

How PHP programmers understand dependency injection containers (Dependency injection container)


Background Knowledge

The traditional idea is that the application uses an Foo class, the Foo class is created and the method of the Foo class is called, and if a bar class is needed within the method, the bar class is created and the bar class is called, and the method requires a BIM class, creates the Bim class, and does some other work.

<?php
Code "1"
Class Bim
{
Public Function dosomething ()
{
echo __method__, ' | ';
}
}

Class Bar
{
Public Function dosomething ()
{
$bim = new Bim ();
$bim->dosomething ();
echo __method__, ' | ';
}
}

Class Foo
{
Public Function dosomething ()
{
$bar = new Bar ();
$bar->dosomething ();
Echo __method__;
}
}

$foo = new Foo ();
$foo->dosomething (); Bim::d osomething| Bar::d osomething| Foo::d osomething

The idea of using dependency injection is that the application uses the Foo class, the Foo class needs the bar class, the bar class needs the Bim class, then creates the Bim class, creates the bar class and injects the BIM, creates the Foo class, injects the bar class, calls the Foo method, and Foo calls the Bar method. Then do some other work.

<?php
Code "2"
Class Bim
{
Public Function dosomething ()
{
echo __method__, ' | ';
}
}

Class Bar
{
Private $bim;

Public function __construct (Bim $bim)
{
$this->bim = $bim;
}

Public Function dosomething ()
{
$this->bim->dosomething ();
echo __method__, ' | ';
}
}

Class Foo
{
Private $bar;

Public function __construct (Bar $bar)
{
$this->bar = $bar;
}

Public Function dosomething ()
{
$this->bar->dosomething ();
Echo __method__;
}
}

$foo = new Foo (new Bim ());
$foo->dosomething (); Bim::d osomething| Bar::d osomething| Foo::d osomething

This is the control reversal mode. The control of the dependency is reversed to the starting point of the call chain. This allows you to fully control the dependencies, by adjusting the different injection objects to control the behavior of the program. For example, the Foo class uses Memcache, and you can switch to Redis without modifying the Foo class code.

The idea behind using a dependency injection container is that the application needs to go to the Foo class, the Foo class is obtained from the container, the container creates the Bim class, creates the bar class and injects the BIM, creates the Foo class, injects the bar, the application calls the Foo method, Foo calls the Bar method, and then does some other work.

In short, the container is responsible for instantiating, injecting dependencies, handling dependencies, and so on.


Code Demo Dependency Injection container (Dependency injection container)

The simplest container class to explain, this code from the Twittee

<?php

Class Container
{
Private $s = array ();

function __set ($k, $c)
{
$this->s[$k] = $c;
}

function __get ($k)
{
return $this->s[$k] ($this);
}
}

This code uses a magic method that __set () is invoked when assigning a value to an inaccessible property. __get () is invoked when the value of an inaccessible property is read.

<?php

$c = new Container ();

$c->bim = function () {
return new Bim ();
};
$c->bar = function ($c) {
Return to New Bar ($c->bim);
};
$c->foo = function ($c) {
return new Foo ($c->bar);
};

Get Foo from container
$foo = $c->foo;
$foo->dosomething (); Bim::d osomething| Bar::d osomething| Foo::d osomething

This code uses an anonymous function

Here's a little bit of code to demonstrate that the container code comes from simple di container

<?php

Class IoC
{
protected static $registry = [];

public static function bind ($name, callable $resolver)
{
Static:: $registry [$name] = $resolver;
}

public static function make ($name)
{
if (Isset (static:: $registry [$name])) {
$resolver = static:: $registry [$name];
return $resolver ();
}
throw new Exception (' Alias does not exist in the IoC registry. ');
}
}

Ioc::bind (' Bim ', function () {
return new Bim ();
});
Ioc::bind (' Bar ', function () {
Return to New Bar (Ioc::make (' Bim '));
});
Ioc::bind (' foo ', function () {
return new Foo (Ioc::make (' Bar '));
});


Get Foo from container
$foo = Ioc::make (' foo ');
$foo->dosomething (); Bim::d osomething| Bar::d osomething| Foo::d osomething

This code uses a late-static binding


Dependency Injection Container (Dependency injection container) Advanced features

The real Dependency injection container will provide more features, such as

Automatic binding (autowiring) or automatic parsing (Automatic resolution)
annotation parser (annotations)
Deferred injection (Lazy injection)

The following code implements the autowiring on the basis of twittee.

<?php

Class Bim
{
Public Function dosomething ()
{
echo __method__, ' | ';
}
}

Class Bar
{
Private $bim;

Public function __construct (Bim $bim)
{
$this->bim = $bim;
}

Public Function dosomething ()
{
$this->bim->dosomething ();
echo __method__, ' | ';
}
}

Class Foo
{
Private $bar;

Public function __construct (Bar $bar)
{
$this->bar = $bar;
}

Public Function dosomething ()
{
$this->bar->dosomething ();
Echo __method__;
}
}

Class Container
{
Private $s = array ();

Public Function __set ($k, $c)
{
$this->s[$k] = $c;
}

Public Function __get ($k)
{
return $this->s[$k] ($this);
return $this->build ($this->s[$k]);
}

/**
* Automatic binding (autowiring) automatic parsing (Automatic resolution)
*
* @param string $className
* @return Object
* @throws Exception
*/
Public function Build ($className)
{
If it is an anonymous function (Anonymous functions), also called a closure function (closures)
if ($className instanceof Closure) {
Execute the closure function and the result
Return $className ($this);
}

/** @var Reflectionclass $reflector * *
$reflector = new Reflectionclass ($className);

Check that the class is instantiated, excluding abstract class abstract and object interface interface
if (! $reflector->isinstantiable ()) {
throw new Exception ("Can ' t instantiate this.");
}

/** @var Reflectionmethod $constructor Get the constructor of the class.
$constructor = $reflector->getconstructor ();

If no constructor, directly instantiate and return
if (Is_null ($constructor)) {
return new $className;
}

Takes the constructor argument and returns the argument list by Reflectionparameter array
$parameters = $constructor->getparameters ();

Recursive resolution of parameters of constructors
$dependencies = $this->getdependencies ($parameters);

Creates a new instance of a class, and the arguments given are passed to the constructor of the class.
Return $reflector->newinstanceargs ($dependencies);
}

/**
* @param array $parameters
* @return Array
* @throws Exception
*/
Public Function getdependencies ($parameters)
{
$dependencies = [];

/** @var Reflectionparameter $parameter * *
foreach ($parameters as $parameter) {
/** @var Reflectionclass $dependency * *
$dependency = $parameter->getclass ();

if (Is_null ($dependency)) {
is a variable and has a default value to set the default value
$dependencies [] = $this->resolvenonclass ($parameter);
} else {
is a class, recursive parsing
$dependencies [] = $this->build ($dependency->name);
}
}

return $dependencies;
}

/**
* @param reflectionparameter $parameter
* @return Mixed
* @throws Exception
*/
Public Function Resolvenonclass ($parameter)
{
Default values are returned with default values
if ($parameter->isdefaultvalueavailable ()) {
return $parameter->getdefaultvalue ();
}

throw new Exception (' I have no idea what ');
}
}

// ----
$c = new Container ();
$c->bar = ' bar ';
$c->foo = function ($c) {
return new Foo ($c->bar);
};
Get Foo from container
$foo = $c->foo;
$foo->dosomething (); Bim::d osomething| Bar::d osomething| Foo::d osomething

// ----
$di = new Container ();

$di->foo = ' foo ';

/** @var Foo $foo * *
$foo = $di->foo;

Var_dump ($foo);
/*
FOO#10 (1) {
Private $bar =>
Class Bar#14 (1) {
Private $bim =>
Class Bim#16 (0) {
}
}
}
*/

$foo->dosomething (); Bim::d osomething| Bar::d osomething| Foo::d osomething

The principles of the above code refer to the official PHP Documentation: Reflection, PHP 5 has a full reflection API, adding the ability to reverse engineer classes, interfaces, functions, methods, and extensions. In addition, the reflection API provides a way to remove documentation comments from functions, classes, and methods.

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.