PHP container and the idea of dependency injection

Keywords: PHP Container

Dependency injection

When class a needs to rely on class B, that is, it needs to instantiate class B objects in class A for use, if the functions in class B change, the places where class B is used in class a will also be modified, resulting in high coupling between Class A and class B. The solution at this time is that class A should rely on the interface of class B and hand over the instantiation of specific classes to the outside.

Take the notification module commonly used in our business.

/**

 * Defines a message class

 * Class Message 

 */

class  Message{

  public function seed()

  {

      return 'seed email';

  }

}

/*

 * You need to send a message when an order is generated

 */

class Order{

    protected $messager = '';

    function __construct()

    {

        $this->messager = new Message();

    }

    public function seed_msg()

    {

        return $this->messager->seed();

    }

}

$Order = new Order();

$Order->seed_msg();

The above code is our traditional way of writing. The first class sent by a Message. Then call the Message sending interface where we need to send the Message. One day you need to add an interface to send SMS to meet different needs. Then you will find that you need to modify the Message class. You also need to modify the Order class. This is very troublesome. At this time, we have the idea of dependency injection. Let's make an adjustment to the code

<?php

/**

 * To constrain, we first define a message interface

 * Interface Message

 */

interface  Message{

  public function seed();

}

/**

 * There is a class that sends mail

 * Class SeedEmail

 */

class SeedEmail implements Message

{

    public function seed()

    {

        return  'seed email';

        // TODO: Implement seed() method.

    }

}

/** 

 *Add a class to send SMS

 * Class SeedSMS

 */

class SeedSMS implements Message

{

    public function seed()

    {

        return 'seed sms';

        // TODO: Implement seed() method.

    }

}

/*

 * You need to send a message when an order is generated

 */

class Order{

    protected $messager = '';

    function __construct(Message $message)

    {

        $this->messager = $message;

    }

    public function seed_msg()

    {

        return $this->messager->seed();

    }

}

//When we need to send mail

$message = new SeedEmail();

//Pass the mail sending object as a parameter to Order

$Order = new Order($message);

$Order->seed_msg();

//When we need to send text messages

$message = new SeedSMS();

$Order = new Order($message);

$Order->seed_msg();

In this way, we have realized the idea of dependency injection. Is it convenient to expand.

Service container

The service container I understand is a factory that automatically generates classes.

<?php

/**

 * To constrain, we first define a message interface

 * Interface Message

 */

interface  Message{

    public function seed();

}

/**

 * There is a class that sends mail

 * Class SeedEmail

 */

class SeedEmail implements Message

{

    public function seed()

    {

        return  'seed email';

        // TODO: Implement seed() method.

    }

}

/**

 *Add a class to send SMS

 * Class SeedSMS

 */

class SeedSMS implements Message

{

    public function seed()

    {

        return 'seed sms';

        // TODO: Implement seed() method.

    }

}

/**

 * This is a simple service container

 * Class Container

 */

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);

    }

}

//Create a message factory

$message = new  Container();

//Bind SMS registration to the factory

$message->bind('SMS',function (){

     return   new  SeedSMS();

});

//Bind send mail registration to factory

$message->bind('EMAIL',function (){

   return new  SeedEmail();

});

//When you need to send text messages

$SMS  = $message->make('SMS');

$SMS->seed();

Container is a simple service container with two methods: bind and make

Bind is to bind a service object to a container. make takes the object from the container.

bind

In the bind method, we need to pass in a concrete. We can pass in an instance object or a closure function.

You can see that I use closure functions. In fact, it can also be written like this

$sms = new  SeedSMS();

$message->bind('SMS',$sms);

The difference between the latter method and closure is that we need to instantiate the object before binding the service to the easy. Closures instantiate objects when we use the service. It can be seen that closures have many advantages.

make

The make method exits the container. It first determines whether there are current and existing service objects in the instances variable. If so, it will be returned directly. If not, call_user_func_array returns an object

Posted by le007 on Thu, 04 Nov 2021 17:24:58 -0700