First, what is dependency injection?

This is an article I translated from a website abroad. What is Dependency Injection? For the first time, excuse me.

This article is part of a series of articles on dependency injection and PHP lightweight container implementations:
Part 1: What is Dependency Injection?
Part 2: Do you need a Dependency Injection Container?
Part 3: Introduction to the Symfony Service Container
Part 4: Symfony Service Container: Using a Builder to create Services
Part 5: Symfony Service Container: Using XML or YAML to describe Services
Part 6: The Need for Speed

Today, I won't talk about containers at first. I want to introduce the concept of dependency injection and the problems it tries to solve and the benefits it can bring to developers through some concrete examples. If you already know about dependency injection, you can skip this article and go to the next one.
Dependency injection is probably one of the simplest design patterns I know, probably you've used it, but it's also the hardest to explain, probably because most of the examples used in articles about dependency injection are boring. I've come up with an example that is more suitable for the PHP field, because PHP is mainly used for web development, so let's look at a simple web example.
In order to solve the stateless problem of http protocol, web applications need a way to record user information between web requests, which can be solved simply by cookie or session:

$_SESSION['language'] = 'fr';

The above code stores the user's language in the session variable. In this way, for the same user's request, the language used will be stored in the $_SESSION array, which we can obtain as follows:

$user_language = $_SESSION['language'];

Since dependency injection only makes sense in an object-oriented world, we pretend that we have a class called SessionStorage that encapsulates the methods for handling session s:

class SessionStorage
{
  function __construct($cookieName = 'PHP_SESS_ID')
  {
    session_name($cookieName);
    session_start();
  }
  function set($key, $value)
  {
    $_SESSION[$key] = $value;
  }
  function get($key)
  {
    return $_SESSION[$key];
  }
  // ...
}

... and an easy-to-use User class that provides advanced interfaces

class User
{
  protected $storage;

  function __construct()
  {
    $this->storage = new SessionStorage();
  }

  function setLanguage($language)
  {
    $this->storage->set('language', $language);
  }

  function getLanguage()
  {
    return $this->storage->get('language');
  }

  // ...
}

These classes are simple enough to use the User class very easily:

$user = new User();
$user->setLanguage('fr');
$user_language = $user->getLanguage();

So far, everything has been fine... unless you want more flexibility. What if you want to change the cookie name in the session? You may use the following methods:

  1. Hard-coded names in the Session Storage constructor

    class User
     {
       function __construct()
       {
         $this->storage = new SessionStorage('SESSION_ID');
       }
    
       // ...
     }
  2. Define a constant outside the User class

    define('STORAGE_SESSION_NAME', 'SESSION_ID');
    class User
     {
       function __construct()
       {
         $this->storage = new SessionStorage(STORAGE_SESSION_NAME);
       }
    
       // ...
     }
  3. Pass a name as a parameter in the User class constructor

    class User
     {
       function __construct($sessionName)
       {
         $this->storage = new SessionStorage($sessionName);
       }
    
       // ...
     }
    
     $user = new User('SESSION_ID');
  4. Pass an array option in the User class constructor

     class User
     {
       function __construct($storageOptions)
       {
         $this->storage = new SessionStorage($storageOptions['session_name']);
       }
    
       // ...
     }
    
     $user = new User(array('session_name' => 'SESSION_ID'));

All of the above options are bad. Hard-coded names don't really solve the problem because you may change your mind at any time in the future and you have to change the User class. Using constants is also a bad idea, because you rely on a constant again. Passing an array parameter may be a good solution, but it's still not so good. It couples the User constructor with something that's not related to itself.
And there's another problem that can't be solved easily: How do I change the SessionStorage class? For example, test with a mock object, or you want to save session s in a database or memory. Unless you change the User class in the current code, it cannot be implemented.

Dependency Injection

Instead of creating SessionStorage objects in User classes, we create SessionStorage objects outside classes and pass them in as parameters through constructors:

class User
{
  function __construct($storage)
  {
    $this->storage = $storage;
  }

  // ...
}

That's Dependency Injection, that's all!

$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);

Now, it's very simple to configure a session storage object, and it's easy to replace it. You can implement other functions without changing the User class.
Pico Container website Dependency Injection is described as "Dependency Injection is the acquisition of required elements through constructors, methods, and attributes"
Dependency injection is not limited to this:

  • Constructor injection:

    class User
      {
        function __construct($storage)
        {
          $this->storage = $storage;
        }
    
        // ...
      }
  • Setter injection:

    class User
      {
        function setSessionStorage($storage)
        {
          $this->storage = $storage;
        }
    
        // ...
      }
  • Attribute injection:

    class User
      {
        public $sessionStorage;
      }
    
      $user->sessionStorage = $storage;

In general, constructor injection is best suited for necessary dependencies, as in the example, Setter injection is better suited for optional dependencies, such as caching objects. Today, many modern PHP frameworks heavily use dependency injection to provide a series of components that are both decoupled and cohesive:

// symfony: A constructor injection example
$dispatcher = new sfEventDispatcher();
$storage = new sfMySQLSessionStorage(array('database' => 'session', 'db_table' => 'session'));
$user = new sfUser($dispatcher, $storage, array('default_culture' => 'en'));
// Zend Framework: A setter injection example
$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
  'auth'     => 'login',
  'username' => 'foo',
  'password' => 'bar',
  'ssl'      => 'ssl',
  'port'     => 465,
));
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);

If you want to know more about dependency injection, I strongly recommend that you read it. Martin Fowler introduction perhaps Jeff More presentation . You can also see what I did last year about dependency injection. speech More details are given here.

Well, that's all. I hope you now have a better understanding of Dependency Injection. In the next chapter of this series, I'll talk about Dependency Injection Containers.

Keywords: Session PHP Database SSL

Added by Buglish on Mon, 10 Jun 2019 23:23:40 +0300