PHP Dependency Injection

Source: Internet
Author: User
Tags autoload unique id

Talk about this topic before talking about a more high-end thinking-' dependency inversion principle '

"Dependency inversion is a software design idea, in the traditional software, the upper code relies on the lower code, when the underlying code changes, the upper layer code should be changed accordingly, so the maintenance cost is higher." The idea of dependency inversion principle is that the upper layer should not rely on the lower layer and should depend on the interface. The upper code defines the interface, the lower code implements the interface, which makes the lower layer dependent on the upper interface, reduces the coupling degree, and improves the system elasticity "

The above explanation is a little bit empty, so let's explain the theory in real code.

For example, if there is such a requirement, the user will send a message after the registration is completed, and then you have the following code:

First message class ' Email.class.php '

Class mail{public    function Send ()    {/        * Here is the code to send the message */    }}

Then register the class ' Register.class.php '

Class register{    private $_emailobj;    Public Function Doregister ()    {/        * Here is how to register */        $this->_emailobj = new Mail ();        $this->_emailobj->send ();//Send Mail    }}

Then start registering

Include ' Mail.class.php '; include ' Register.class.php '; $reg = new Register (); $reg->doregister ();

It seems to be very simple, you quickly put this feature on the line, looks peaceful ... after xxx days, the product personnel said that send the mail is not good, to use Send text messages, and then you say this simple I put ' Mail ' class changed ...

After a few days, the product personnel said that the cost of sending text messages is too high, or instead of using mail good ... At this time 10,000 grass mud horse galloping and past ...

This kind of thing, often occurs in the product dog, flowers fall off ...

The problem with the above scenario is that every time you have to modify the ' Mail ' class, the code reusability is low and the top level relies heavily on the underlying. Then we will consider the ' dependency inversion principle ', which allows the underlying to inherit the interface established by the upper layer, and the upper layer depends on the interface.

Interface mail{public    function Send ();}
Class Email implements Mail () {public    function send ()    {        //Send email    }}
Class Smsmail implements Mail () {public    function send ()    {        //Send SMS    }}
Class register{    private $_mailobj;    Public function __construct (Mail $mailObj)    {        $this->_mailobj = $mailObj;    }    Public Function Doregister ()    {/        * Here is how to register */        $this->_mailobj->send ();//Send Message    }}

Start sending information below

/* Omit several lines here */$reg = new Register (), $EMAILOBJ = new Email (), $SMSOBJ = new Smsmail (); $reg->doregister ($EMAILOBJ);//Use EMA Il sends $reg->doregister ($SMSOBJ);//use SMS////You can even send text messages after sending the message * *

The above code solves the ' Register ' dependency on the information-sending class, using the method of constructor injection, so that it relies only on the interface to send the SMS, as long as it implements the ' send ' method in its interface, no matter how you send it. The previous example uses the idea of " injection ", like a syringe, to inject an instance of a class into an instance of another class, and inject whatever it needs. Of course, the " dependency inversion principle " is always carried out inside. " injection " can be injected not only through the constructor function, but also through the attribute injection, you can use a "setter" to dynamically assign a value to the "Mailobj" property.

It looked a lot, but the reader may find the title " no longer consider the loading order " The word, you do not have to consider the loading order? Do you want to introduce the message class first, then introduce the registration class and then instantiate it? If you have more than one class, don't faint!

Indeed, there are many such cases in the real world, at the beginning of the class is so many, slowly function more and more, more and more people, writing a lot of classes, to use this class must first introduce that class, and must ensure that the order is correct. For example, "A depends on B, B depends on C, C depends on D, D depends on E", to get an instance of ' a ', you have to introduce ' e,d,c,b ' in turn and instantiate in turn, and the old employee knows the pit and jumps over it. One day a new person, he wanted to instantiate ' a ' but always error, he did not build, at this time can only look at the business logic of ' a ', and then know to get ' B ' instance, and then look at ' B ' business logic, then ... A day passed, he still did not get the example of ' a ', then the leader came ...

Is the new technology low, or is the level of the architect low?

Now cut into the topic, to realize how not to consider the loading order, before implementing it is necessary to understand that if the loading order is not considered means that the program automatically loaded automatically instantiation. class to instantiate, as long as the complete pass to the ' __construct ' function necessary parameters are OK, in the class if you want to refer to other classes, you must also inject in the constructor, otherwise the call will still occur error. Then we need a class to hold the parameters required for the instantiation of the class, other classes or objects that depend on them, and the references after each class instantiation

The class is named box ' Container.class.php ', which reads as follows:

/*** Dependency Injection Class */class container{/** * @var Array stores the definitions of each class with the name of the class */private $_definitions = Array ();    /** * @var Array stores the arguments required for each class instantiation with the class name key */Private $_params = Array ();    /** * @var Array stores the references for each class instantiation */Private $_reflections = Array ();    /** * @var Array classes depend on the class */private $_dependencies = Array (); /** * Set Dependent * @param string $class class, method name * @param mixed $defination class, method definition * @param array $params class, method initialization required Parameter */Public function set ($class, $defination = Array (), $params = Array ()) {$this->_params[$class] =        $params;    $this->_definitions[$class] = $this->initdefinition ($class, $defination); /** * Get instance * @param string $class class, method name * @param array $params Instantiate required parameters * @param array $properties for instance Property * @return Mixed */Public function get ($class, $params = Array (), $properties = Array ()) {if (!iss    ET ($this->_definitions[$class])) {//If you have not declared it again, create it directly.        return $this->bulid ($class, $params, $properties);        } $defination = $this->_definitions[$class]; if (Is_callable ($defination, True)) {//If the declaration is a function $params = $this->parsedependencies ($this->mergeparam            S ($class, $params));        $obj = Call_user_func ($defination, $this, $params, $properties);            } elseif (Is_array ($defination)) {$originalClass = $defination [' class '];            unset ($definition [' class ']);            The difinition in addition to the ' class ' element is treated as an attribute of the instance $properties = Array_merge (array) $definition, $properties);            The parameter $params = $this->mergeparams ($class, $params) when merging the class, function declaration; if ($originalClass = = = $class) {//If the name of the class in the Declaration and the name of the keyword are the same, the object is generated directly $obj = $this->bulid ($class,            $params, $properties); } else {//if different, it is possible to get an alias from the container $obj = $this->get ($originalClass, $params, $properti  ES);          }} elseif (Is_object ($defination)) {//If it is an object that returns the return $defination directly; } else {throw new Exception ($class.        ' Declaration error! ');    } return $obj; }/** * Merge parameters * @param string $class class, function name * @param array $params parameter * @return Array * * Protected fu Nction mergeparams ($class, $params = Array ()) {if (Empty ($this->_params[$class])) {return        $params;        } if (empty ($params)) {return $this->_params;        } $result = $this->_params[$class];        foreach ($params as $key = = $value) {$result [$key] = $value;    } return $result; }/** * Initialization declaration * @param string $class class, function name * @param array $defination class, function definition * @return Mixed */PR otected function Initdefinition ($class, $defination) {if (empty ($defination)) {return Array (' Class ' = ' $class);        } if (Is_string ($defination)) {return Array (' class ' = = $defination);        } if (Is_callable ($defination) | | is_object ($defination)) {return $defination; } if (Is_array ($defination)) {if (!isset ($defination [' class ')]) {$defin            Ition[' class '] = $class;        } return $defination; } throw new Exception ($class.    ' declaration error '); /** * Create class instance, function * @param string $class class, function name * @param array $params parameters when initializing * @param array $properties Genus Sex * @return Mixed */protected function bulid ($class, $params, $properties) {list ($reflection, $depend        encies) = $this->getdependencies ($class);         The foreach (array) $params as $index + $param) {//depends on not only the object's dependence but also the dependence of the common parameters $dependencies [$index] = $param;        } $dependencies = $this->parsedependencies ($dependencies, $reflection); $obj = $REFLEction->newinstanceargs ($dependencies);        if (empty ($properties)) {return $obj;        The foreach ((array) $properties as $name = + $value) {$obj-$name = $value;    } return $obj; }/** * Gets the dependent * @param string $class class, function name * @return Array */protected function getdependencies ($class {if (Isset ($this->_reflections[$class])) {//If it has been instantiated, get the return array directly from the cache ($this->_ref        lections[$class], $this->_dependencies[$class]);        } $dependencies = Array (); $ref = new Reflectionclass ($class);//Gets an instance of the object $constructor = $ref->getconstructor ();//Gets the constructor of the object if ($constr Uctor!== null) {//If the constructor method has parameter foreach ($constructor->getparameters () as $param) {//Gets the parameter of the constructor method Number if ($param->isdefaultvalueavailable ()) {//If the default value is defaulted directly $dependencies [      ] = $param->getdefaultvalue ();          } else {//Instantiate the arguments in the constructor $temp = $param->getclass ();                    $temp = ($temp = = = = null? NULL: $temp->getname ());                $temp = Instance::getinstance ($temp);//The Instance class is used here to instantiate and store the name of the class $dependencies [] = $temp;        }}} $this->_reflections[$class] = $ref;        $this->_dependencies[$class] = $dependencies;    Return Array ($ref, $dependencies);    }/** * Parse dependent * @param array $dependencies dependent arrays * @param array $reflection instance * @return Array $dependencies  */protected function parsedependencies ($dependencies, $reflection = null) {foreach ((array) $dependencies as $index = = $dependency) {if ($dependency instanceof Instance) {if ($de       Pendency->id!== null) {$dependencies [$index] = $this->get ($dependency->id);         } elseif ($reflection!== null) {$parameters = $reflection-&gt                    ; GetConstructor ()->getparameters ();                    $name = $parameters [$index]->getname ();                    $class = $reflection->getname (); throw new Exception (' instantiated class '. $class. ' When necessary parameters are missing: '.                $NAME);    }}} return $dependencies; }}

The following is the content of the ' Instance ' class, which is primarily used to record the name of a class, indicating whether an instance needs to be obtained

Class instance{    /**     * @var only marked */public    $id;    /** *     Constructor     * @param string $id class unique ID     * @return void *     /public    function __construct ($id)    { c13/> $this->id = $id;    }    /**     * Gets an instance of the class     * @param string $id class unique ID     * @return Object Instance */public    static function Getin Stance ($id)    {        return new self ($id);}    }

Then we implemented the ability to add properties dynamically for instances of the class in ' Container.class.php ', to add properties dynamically, using the Magic method ' __set ', so that all classes that use dependent loading need to implement the method, we first define a base class ' Base.class.php ', the contents are as follows

Class base{    /** *    Magic Method    * @param string $name    * @param string $value    * @return void    */Public function __set ($name, $value)    {        $this->{$name} = $value;}}    

We then implement the ' A,b,c ' class, where instances of Class A depend on instances of Class B, and instances of Class B depend on instances of class C

' A.class.php '

Class A extends base{    private $instanceB;    Public function __construct (B $instanceB)    {        $this->instanceb = $instanceB;    }    Public function test ()    {        $this->instanceb->test ();    }}

' B.class.php '

Class B  extends base{    private $instanceC;    Public function __construct (C $instanceC)    {        $this->instancec = $instanceC;    }    Public function test ()    {        return $this->instancec->test ();}    }

' C.class.php '

Class C  extends base{public    function test ()    {        echo ' this is c! ';    }} De

Then we get an instance of ' a ' in ' index.php ', for automatic loading, we need to use the ' spl_autoload_register ' method of the SPL class library, the code is as follows

function AutoLoad ($className) {    include_once $className. '. class.php ';} Spl_autoload_register (' AutoLoad ', true, true); $container = new container; $a = $container->get (' a '); $a->test (); /Output ' This is c! '

The above example seems to be not very cool, there is no need to consider ' B ', ' C ' (of course, here b,c except to use the corresponding class instance, there are no other parameters, if there are other parameters, must be prominent call ' $container->set (XX) ' method to register, To instantiate the necessary parameters for it). Having a careful classmate may think, for example, I get an instance of ' A ' earlier, I'm going to get an instance of ' a ' in another place, but the instance of this place ' a ' needs one of these attributes, how do I do that?

You can see that the ' get ' method of the ' Container ' class has the other two parameters, ' $params ' and ' $properties ', this ' $properties ' can implement just the requirements, which all depend on the ' __set ' magic method, of course here you can not only register the class , you can also register a method or object, just use a callback function when registering a method, such as

$container->set (' foo ', function ($container, $params, $config) {    print_r ($params);    Print_r ($config);}); $container->get (' foo ', Array (' name ' = ' + ' foo '), Array (' key ' = ' test '));

You can also register an instance of an object, such as

Class test{public    function mytest ()    {        echo ' This is a Test ';    }} $container->set (' Testobj ', New Test ()), $test = $container->get (' testobj '); $test->mytest ();

The above automatic loading, the general idea of dependent control is to inject the instance of the class to be referenced by the constructor into its internal, when the instance of the class is fetched through the parameters of the built-in reflection parsing constructor in PHP to load the required class, then instantiate, and cache it to get directly from memory on the next fetch

The above code is only for learning and experimentation, not strictly tested, please do not use in production environment, so as not to produce unknown bugs

PHP Dependency Injection (RPM)

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.