DI - Dependency Injection trong PHP (ok) 😁😁😁

https://xuanthulab.net/di-dependency-injection-trong-php.html

C:\xampp\htdocs\wpclidemo\index.php

<?php
require_once __DIR__ . '/vendor/autoload.php';
use DI\ContainerBuilder;
use User\User;
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions(__DIR__ . '/config.php');
$container = $containerBuilder->build();
$user = $container->get(User::class);

C:\xampp\htdocs\wpclidemo\Database.php

<?php
// Database.php
namespace Database;
class Database {
  public function connect() {
    print('connected');
  }
}
?>

C:\xampp\htdocs\wpclidemo\User.php

<?php
// User.php
namespace User;
use Database\Database;
class User {
  private $database;
  public function __construct(Database $database) {
    $this->database = $database;
    $this->database->connect();
  }
}

C:\xampp\htdocs\wpclidemo\config.php

<?php
require_once __DIR__ . '/Database.php';
require_once __DIR__ . '/User.php';
use Database\Database;
use function DI\create;
use User\User;
return [
  'database' => create(Database::class),
  'user'     => create(User::class),
];

A very basic guide to using PHP-DI for making DI Containers for your PHP applications with autowiring

There are lots of tutorials and guides online describing dependency injection and autowiring with PHP, but as someone who’s new to this whole concept and never even implemented a dependency injection container I had a hard time getting started with using it in my PHP application. In this article, I want to give a very short and simple example of how to use PHP-DI to get started with a simple DI container.

What is dependency injection?

Almost every PHP application consists of classes. We make objects and use those objects throughout our applications to tie all the logic together and make our applications work the way we want them to work, with our logic separated into classes. In order to use objects throughout our applications, we can choose to use dependency injection to pass objects around. A simple example of dependency injection:

Our dependency which is a Database class that connects to a given database:

// Database.phpnamespace Database;class Database{    public function connect() {        //...    }}

Our User class which depends on the Database object to connect to a given database:

// User.phpnamespace User;class User {    private $database;        public function __construct(Database $database) {        $this->database = $database;        $this->database->connect();    }}

Now we can create a User object and inject the Database dependency into it in our index.php:

// index.php$user = new User(new Database());

What we have done is:

  • We created a dependency, called Database. The Database dependency currently can only do one thing, which is connecting to a database.

  • We created a User class. This class can only do on thing, which is getting user data. In order to do this, the User class needs the Database dependency to connect to the database.

  • We created a User object, and passed a Database object (the dependency) to its constructor. By doing so, we’re injecting the Database dependency into the User object.

This looks nice, however, when your application gets more and more complicated, you end up passing around these objects from on place to another. This makes your code unreadable and hard to maintain. To fix this problem, we can use a DI Container, short for Dependency Injection Container. The DI Container is a single source of truth for all our dependencies and can automate the injection of dependencies throughout our code.

Let’s implement a DI Container using PHP-DI

Step 1: Let’s install PHP-DI using Composer

composer require php-di / php-di

Now we’ve included PHP-DI in our project. See https://php-di.org/ for more information about PHP-DI.

Step 2: Let’s modify our index.php and add a DI Container to it and create a User object with the Database dependency passed to it

require_once __DIR__.'/vendor/autoload.php';use DI\ContainerBuilder;use User\User;$containerBuilder = new ContainerBuilder();$containerBuilder->addDefinitions(__DIR__.'/config.php');$container = $containerBuilder->build();$container->get(User::class);

On this line $containerBuilder->addDefinitions(__DIR__.’/config.php’); we’re referring to a file called config.php. This file contains a list of our definitions. Consider definitions as being our dependencies (https://php-di.org/doc/definition.html).

Step 3: Let’s create our config with a list of definitions/dependencies

require_once __DIR__.'/Database.php';require_once __DIR__.'/User.php';use Database\Database;use User\User;use function DI\create;return [  'database' => create(Database::class),  'user' => create(User::class)];

Here we require our classes at the top. Next, we import the Database and User classes. Last but not least, we return an array with our definitions. We use the class magic constant, as is advised by the PHP-DI documentation (https://php-di.org/doc/php-definitions.html#syntax).

As we’re doing $container->get(User::class); in our index.php file, we’re instructing PHP-DI to employ autowiring. All dependencies will be loaded automatically, because the function signature of the User class constructor looks as follows:

public function __construct(Database $database)

PHP-DI will see that the User constructor requires a Database object and will inject that dependency automatically into each User object.

To tie it all together:

index.php

require_once __DIR__.'/vendor/autoload.php';use DI\ContainerBuilder;use User\User;$containerBuilder = new ContainerBuilder();$containerBuilder->addDefinitions(__DIR__.'/config.php');$container = $containerBuilder->build();$user = $container->get(User::class);

User.php

namespace User;use Database\Database;class User{  private $database;  public function __construct(Database $database)  {    $this->database = $database;    $this->database->connect();  }}

Database.php

namespace Database;class Database{  public function connect() {    print('connected');  }}

config.php

require_once __DIR__.'/Database.php';require_once __DIR__.'/User.php';use Database\Database;use User\User;use function DI\create;return [  'database' => create(Database::class),  'user' => create(User::class)];

Running this code, you should see the text “connected” on your screen. This means PHP-DI successfully injects the Database dependency into the User object and instantiates it.

With this simple example, you can start writing PHP code with a simple DI Container. Remember to add each dependency to the config.php and simply add your needed dependencies to the constructor of each class where you have dependencies, and PHP-DI will handle the rest using autowiring. In order to make autowiring work, you have to create objects by NOT using the new keyword. Instead, use $myObjWithDependencies = $container->get(myObjWithDependencies::class); and PHP-DI will know to check the constructor of myObjWithDependencies and inject all needed dependencies automatically.

Last updated