Home  >  Article  >  php教程  >  Understanding PHP Dependency Injection Container Series (1) What is

Understanding PHP Dependency Injection Container Series (1) What is

黄舟
黄舟Original
2016-12-28 10:22:011907browse

This article is the first chapter of the series on the implementation of PHP dependency injection container.
Today, let’s not talk about the container (container). First, we will use some specific examples to introduce the concept of dependency injection to prove what problems the dependency injection model can solve and what benefits it can bring to developers.
If you already know the concept of dependency injection, you can skip this article.

Dependency injection may be one of the simplest design patterns I know. In many cases, you may have used dependency injection unconsciously. But it's also one of the hardest to explain. I think part of the reason is that most of the examples that introduce dependency injection lack practical meaning and are difficult to understand. Because PHP is mainly used for web development, let's first look at a simple web development example.

HTTP itself is a stateless connection protocol. In order to support the application to store user information when the client initiates a WEB request, we need to use a technology to achieve storage state interaction. Of course the easiest way is to use cookies, and a better way is PHP's built-in Session mechanism.

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

The above code stores the user language in the Session variable named language, so in subsequent requests by the user, the language can be obtained through the global array $_SESSION:

$user_language = $_SESSION['language'];

Dependencies Injection is mainly used for object-oriented development. Now let us assume that we have a SessionStorage class, which encapsulates the PHP Session mechanism:

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];
  }
  // ...
}

There is also a User class that provides a more advanced encapsulation:

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');
  }
  // ...
}

The code is very simple, and it is also very simple to use the User class:

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

Everything is beautiful, unless your program needs better scalability. Suppose now you want to change the COOKIE key value that saves session_id. Here are some alternative methods:

When creating a SessionStorage instance in the User class, use the string 'SESSION_ID' hard-coded in the SessionStorage constructor:

class User
{
    function __construct()
    {
        $this->storage = new SessionStorage('SESSION_ID');
    }
    // ...
}

Set a constant outside the User class (named STORAGE_SESSION_NAME)

class User
{
  function __construct()
  {
    $this->storage = new SessionStorage(STORAGE_SESSION_NAME);
  }
  // ...
}
define('STORAGE_SESSION_NAME', 'SESSION_ID');

Pass the Session name through the parameters in the User class constructor

class User
{
  function __construct($sessionName)
  {
    $this->storage = new SessionStorage($sessionName);
  }
  // ...
}
$user = new User('SESSION_ID');

Still pass the Session name through the parameters in the User class constructor, but this time the parameters are in the form of an array

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

The above methods are all bad.
Hardcoding the session name in the user class does not really solve the problem. If you need to change the COOKIE key value that saves the session_id in the future, you have to modify the user class again (the User class should not care about the COOKIE key value) .
The way to use constants is also bad, causing the User class to depend on a constant setting.
It is relatively better to pass the session name through the parameters or array of the User class constructor, but it is not perfect. This interferes with the parameters of the User class constructor, because how to store the Session is not what the User class needs to care about. The User class should not be associated with them.

In addition, there is another problem that is not easy to solve: how do we change the SessionStorage class. There are many application scenarios for this, for example, you want to use a Session simulation class for testing, or you want to store the Session in a database or memory. With the current implementation, it is difficult to do this without changing the User class.

Now, let’s use dependency injection. Recall that we created the SessionStorage object inside the User class before. Now we modify it and pass the SessionStorage object through the constructor of the User class.

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

This is the most classic case of dependency injection, no one. Now there are some small changes to using the User class. First you need to create the SessionStorage object.

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

Now, it is very simple to configure the session storage object, and it is also very simple to change the session storage object. All of this does not need to update the User class, reducing the coupling between business classes.
Pico Container's website describes dependency injection like this:

Dependency injection is a method of passing dependent components to a class through the class's constructor, method, or direct writing.

So dependency injection is not limited to constructor injection. Let’s take a look at several injection methods:

Constructor injection

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

setter method injection

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

Direct attribute injection

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


根据经验,一般通过构造函数注入的是强依赖关系的组件,setter方式用来注入可选的依赖组件。 

现在,大多数流行的PHP框架都采用了依赖注入的模式实现业务组件间的高内聚低耦合。

// symfony: 构造函数注入的例子
$dispatcher = new sfEventDispatcher();
$storage = new sfMySQLSessionStorage(array('database' => 'session', 'db_table' => 'session'));
$user = new sfUser($dispatcher, $storage, array('default_culture' => 'en'));
// Zend Framework: setter方式注入的例子
$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);

 以上就是理解PHP依赖注入容器系列(一) 什么是的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn