Dependency injection container — PHP version of the Phalcon container

Time:2021-8-2

The PHP version of Phalcon relies on the injection container

Environmental requirements

php7.0+

install

composer require fanqingxuan/di 

If you want to install the PHP extension container, please refer todi-extThe usage is as like as two peas.

Basic Usage

require_once 'vendor/autoload.php';

use JsonDi\Di;

class Test
{
}

$di = new Di;
//Injection mode
$di->set('test', 'Test');
$di->set("test2", function () {
    return new Test;
});
$di->set("test3", Test::class);
$di->set('test4', new Test);

As you can see, there are several ways to inject containers

  • character string
$di->set('test','Test');
$di->set("test3",Test::class);
  • Instance object
$di->set('test5',new Test);
  • Closure / anonymous function
$di->set("test2",function() {
return new Test;
});

You can also pass additional parameters to anonymous functions

require_once 'vendor/autoload.php';

use JsonDi\Di;
use JsonDi\Config;
$di = new Di;
$di->set('config',new Config(
    [
        'database'  =>  [
            'host'      =>  'localhost',
            'username'  =>  'root',
            'password'  =>  '111111'
        ]
    ]
));
class MysqlDb
{
    public function __construct($config) 
    {
        print_r($config);
    }
}
$di->set('db',function () {
    return new MysqlDb($this->get('config')->database);//get the database config from container
});

You can also use the use keyword

$config = [
    'host'      =>  'localhost',
    'username'  =>  'root',
    'password'  =>  '111111'
];
$di->set('db',function () use ($config) {
    return new MysqlDb($config);
});

Advanced Usage

  • Constructor Inject

This injection type can inject parameters into the constructor

class UserService 
{
    protected $userDao;
    protected $userType;
    
    public function __construct(UserDao $userDao,$userType) 
    {
        $this->userDao  = $userDao;
        $this->userType = $userType;
    }
}

Inject in the following way

$di->set(
    'userDao',
    [
        'className'    =>    UserDao::class
    ]
);
$di->set(
    'userService',
    [
        'className'    =>    UserService::class,
        'arguments'    =>    [
            [
                'type'    =>    'service',
                'name'    =>    'userDao',//another service name in the container
            ],
            [
                'type'    =>    'parameter',
                'value'    =>    3
            ]
        ]
    ]
);
  • Setter mode injection

class UserService 
{
    protected $userDao;
    protected $userType;
    
    public function setUserDao(UserDao $userDao) 
    {
        $this->userDao = $userDao;
    }
    
    public function setUserType($userType) 
    {
        $this->userType = $userType;
    }
}

Inject parameters into the setter in the following way

$di->set(
    'userService',
    [
        'className'    =>    'UserService',
        'calls'        =>    [
            [
                'method'    =>    'setUserDao',
                'arguments'    =>    [
                    [
                        'type'    =>    'service',
                        'name'    =>    'userDao',
                    ]
                ]
            ],
            [
                'method'    =>    'setUserType',
                'arguments'    =>    [
                    [
                        'type'    =>    'parameter',
                        'value'    =>    3
                    ]
                ]
            ]
        ]
    ]
);
  • Attribute injection

Inject parameters into public attributes

class UserService 
{
    public $userDao;
    public $userType;
    public $tempObj;
}
$di->set(
    'userService',
    [
        'className'    =>    UserService::class,
        'properties'=>[
            [
                'name'    =>    'userDao',
                'value'    =>    [
                    'type'    =>    'service',
                    'name'    =>    'userDao',//service name in the container
                ]
            ],
            [
                'name'    =>    'userType',
                'value'    =>    [
                    'type'    =>    'parameter',
                    'value'    =>    2,
                ]
            ],
            [
                'name'    =>    'tempObj',
                'value'    =>    [
                    'type'    =>    'instance',
                    'className'    =>    'StdClass',
                    'arguments'    =>    []
                ]
            ]
        ]
    ]
);

More advanced usage

The following is injection through PHP file

//service.php
<?php
return [
    'testA' => [
        'className' => Test::class,
        'shared'    => true,
    ],
];

Inject by

require_once 'vendor/autoload.php';

use Json/Config;

$di = new Di;
$di->loadFromPhp('service.php');

Array mode injection

We introduced the method of using set function injection above. In fact, we can also use array key value injection

$di['db'] = new StdClass;
$di['db'] = function() {
    return new StdClass;
}
$di['db'] = 'StdClass';
$di['db'] = [
    'className'    =>    'StdClass'
]

Attribute mode injection

You can use instance attribute injection, which is essentially a container implementation__ set,__ Get magic method

$di->db = new StdClass;
$di->db = function() {
    return new StdClass;
}
$di->db = StdClass::class;
$di->db = [
    'className'    =>    'stdClass'
];

Get service

  • Get method
$di->get('db');
  • Magic method
$di->getDb();
  • Array key
$di['db'];
  • Instance properties
$di->db;

Singleton service

The service can be injected into the container as a singleton pattern, that is, there is only one instance of the same service during execution

$di->setShared(
    'db',
    function() {
        return new MysqlDb();
    }
);

You can also use the set method to set the value of the third parameter to true

$di->set(
    'db',
    function() {
        return new MysqlDb();
    },
    true
);

Modify the service in the container

After the service is injected into the container, you can also modify it

class Test 
{
    
}
//register service
$di->set('test','StdClass');
//get service
$test = $di->getService('test');
//change the definition
$test->setDefinition(function() {
    return new Test;
});
//resolve the service
$test->resolve();

Inject containers into services

The container is used to inject other services into the container, but sometimes it is necessary to inject the container into the service, so that all services in the container can be accessed through the container in the service to realize decoupling. To achieve this, you need to inherit your service from the jsondi \ di \ abstractinjectionaware class

require_once 'vendor/autoload.php';

use JsonDi\Di;
use JsonDi\Di\AbstractInjectionAware;

class Mysql
{
    public function select()
    {   
       return "this is select";
    }   
}

class HomeController extends AbstractInjectionAware
{
    public function say()
    {   
       echo $this->container->get('db')->select();
    }   
}

$di = new Di; 
$di->set('db', Mysql::class);

$di->set('home', HomeController::class);
$di->get('home')->say();

Service provider

Using the jsondi \ di \ serviceproviderinterface interface interface, you can write your injection container code to the register method.

require_once 'vendor/autoload.php';

use JsonDi\Di\DiInterface;
use JsonDi\Di\ServiceProviderInterface;
class SessionServiceProvider implements ServiceProviderInterface 
{
    public function register(DiInterface $di):void
    {
        $di->set(
            'session',
            'SessionClass'
        );
    }
}
$di->register(new SessionServiceProvider());
$di->get('session');