First need to understand what is dependency injection, and here I can only copy relatively good is defined as follows:
Dependency injection ( Dependency Injection) , dependencies between components at run time is determined by the vessel, said the image that will be injected by the container dynamic dependencies into a component. Dependency injection is not intended to bring more functionality to software systems, but to enhance component reuse frequencies, and for the system to build a flexible, scalable platform. (Reference: Inversion of Control and appreciated dependency injection (plain) )
Yii2 by reading the source code, which the Container class uses technology to manage di class (Oh ~), yii2 supported the definition of a class (definition) format rich:
/**
* Registers a class definition with this container.
*
* For example,
*
* ```php
* // register a class name as is. This can be skipped.
* $container->set('yii\db\Connection');
*
* // register an interface
* // When a class depends on the interface, the corresponding class
* // will be instantiated as the dependent object
* $container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer');
*
* // register an alias name. You can use $container->get('foo')
* // to create an instance of Connection
* $container->set('foo', 'yii\db\Connection');
*
* // register a class with configuration. The configuration
* // will be applied when the class is instantiated by get()
* $container->set('yii\db\Connection', [
* 'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
* 'username' => 'root',
* 'password' => '',
* 'charset' => 'utf8',
* ]);
*
* // register an alias name with class configuration
* // In this case, a "class" element is required to specify the class
* $container->set('db', [
* 'class' => 'yii\db\Connection',
* 'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
* 'username' => 'root',
* 'password' => '',
* 'charset' => 'utf8',
* ]);
*
* // register a PHP callable
* // The callable will be executed when $container->get('db') is called
* $container->set('db', function ($container, $params, $config) {
* return new \yii\db\Connection($config);
* });
* ```
*
*/
Definition of a so-called class (Definition) refers to a class name of class (yii2 namespace utilized to determine the class name).
The so-called dependency injection, that is no longer create an instance (of course, not so superficial purpose) by a class of new Class () to display, but the definition or configuration once, get an instance of the class again when it is only necessary a method can be achieved.
Creating an instance of a class DB process is: Initialization new new DB (), or the DB class have dependencies such as: new DB (string dsn, string username = 'xxx' ...), this time, when an instance when DB object, you need to give the value of a dependency of its constructor, dependencies may be a value type or an object, the value type, then we give the value on the line, for the object, then, of course, by di (dependency injection) to create a (if not particularly true, according to the constructor to be defined), but about the object class definition also needs to be cached in a container class, or container can not find the definition of dependent objects, making it impossible to complete the DB (analogy) examples of the dependency item.
To techniques: nested object's dependency on a recursive algorithm, and parameters reflecting the class constructor (the ReflectionClass) acquisition, the instantiation of a class is achieved by ReflectionClass :: newInstanceArgs ($ args) method.
Directory Structure:
/index.php entry file
Implementation file /auto_loader.php load classes
/di/Container.php container class definition
/ Common / * generic class
/web/Application.php application class
code show as below:
index.php
<?php
use \dhope\web\Application;
require 'auto_loader.php';
spl_autoload_register('auto_loader');
(new Application())->run();
auto_loader.php
<?php
defined('PATH') or define('PATH', __DIR__);
function auto_loader($class)
{
$path = str_replace(
'\\',
DIRECTORY_SEPARATOR,
substr($class, strpos($class, '\\', 1)+1)
);
if(file_exists(PATH.DIRECTORY_SEPARATOR.$path.'.php')) {
require PATH.DIRECTORY_SEPARATOR.$path.'.php';
} else {
throw new Exception(sprintf('not found `%s`', $class));
}
return class_exists($class);
}
web/Application.php
<?php
namespace dhope\web;
use \dhope\di\Container;
class Application
{
public function run()
{
$container = new Container();
$container->set('db', '\dhope\common\DB', [
'dsn' => 'dh.dhope.com',
'port' => 80
]);
$container->set('mysql', '\dhope\common\Mysql', [
'db' => [
'class' => 'db'
],
'mode' => 0
]);
$container->set('t_user', '\dhope\common\TableUser', [
'connect' => [
'class' => 'mysql'
]
]);
$handle = $container->get('t_user');
echo json_encode($handle->showTable(), JSON_UNESCAPED_UNICODE);
}
}
di/Container.php
<?php
namespace dhope\di;
use ReflectionClass;
use Exception;
define('PARAMETER_OBJ', 'class');
define('PARAMETER_VAR', 'var');
/**
* 容器类,支持依赖注入
* @author zhangchao02
* @date 2019-08-11
*/
class Container
{
//class 类的定义
private $_definitions;
//class 类的实例
private $_instances;
//class 类构造函数的初始化参数
private $_params;
//class 类的反射
private $_classReflection;
//class 类的依赖
private $_dependencies;
public function __construct()
{
//变量初始化
$this->_definitions = [];
$this->_instances = [];
$this->_params = [];
$this->_classReflection = [];
$this->_dependencies = [];
}
/**
* 初始化类的定义
* @param $class 类的别名
* @param $definition 类的定义
* @param array $params 类构造函数的初始化值
* @throws Exception
*/
public function set($class, $definition, $params = [])
{
//类别名到类名的映射
$this->_definitions[$class] = $this->normalizeDefinition($class, $definition);
//类构造参数初始值的存储
$this->_params[$class] = $params;
}
/**
* 获取实例
* @param $class 类的别名
* @param bool $cover 是否复用实例
* @return mixed
*/
public function get($class, $cover = false)
{
if($cover) {
if($this->_instances[$class] && is_object($this->_instances[$class])) {
return $this->_instances[$class];
}
}
$instance = $this->build($class);;
if($cover) {
$this->_instances[$class] = $instance;
}
return $instance;
}
/**
* 创建一个实例
* @param $class 类的别名
* @return mixed
* @throws Exception
*/
public function build($class)
{
$params = $this->_params[$class];
$this->setDependencies($class, $params);
$dependencies = $this->_dependencies[$class];
$reflection = $this->_classReflection[$class];
$args = [];
foreach ($dependencies as $index => $dependency) {
if(is_array($dependency)) {
$key = key($dependency);
$value = current($dependency);
if($key == PARAMETER_OBJ) {
$args[$index] = $this->get($value);
} elseif ($key === PARAMETER_VAR) {
$args[$index] = $dependency[$value];
} else {
throw new Exception(sprintf('class `%s` not exists parameter type `%s`', $class, $key));
}
} else {
$args[$index] = $dependency;
}
}
return $reflection->newInstanceArgs($args);
}
/**
* 设置类的依赖
* @param $class
* @param $params
* @throws \ReflectionException
*/
public function setDependencies($class, $params)
{
if(!isset($this->_definitions[$class])) {
throw new Exception(sprintf('not found the definition of class `%s`', $class));
}
$definition = $this->_definitions[$class]['class'];
if(isset($this->_classReflection[$class])) {
$reflection = $this->_classReflection[$class];
} else {
try{
$reflection = new ReflectionClass($definition);
} catch (\ReflectionException $e) {
throw new Exception(sprintf('Failed to instantiate component or class "%s"', $class));
}
}
$constructor = $reflection->getConstructor();
$dependency = [];
if($constructor !== null) {
foreach ($constructor->getParameters() as $param) {
$name = $param->getName();
if(isset($params[$name])) {
if(is_array($params[$name])) {
$key = key($params[$name]);
if($key === PARAMETER_OBJ) {
$dependency[] = [
PARAMETER_OBJ => current($params[$name])
];
} else {
$dependency[] = [
PARAMETER_VAR => current($params[$name])
];
}
} else {
$dependency[] = $params[$name];
}
} elseif ($param->isDefaultValueAvailable()) {
$dependency[] = $param->getDefaultValue();
} else {
throw new Exception(sprintf('class `%s` construct miss parameter `%s`',
$class,
$name
));
}
}
}
$this->_classReflection[$class] = $reflection;
$this->_dependencies[$class] = $dependency;
}
/**
* 支持多种类的定义格式
* @param $class
* @param $definition
* @return array
* @throws Exception
*/
public function normalizeDefinition($class, $definition)
{
$normalize_definition = [];
if(!$definition) {
if(!class_exists($class)) {
throw new Exception(sprintf(
'class `%s` not exists',
$class
));
}
$normalize_definition['class'] = $class;
} elseif (is_string($definition) && class_exists($definition)) {
$normalize_definition['class'] = $definition;
} elseif (is_array($definition)) {
if(isset($definition['class']) && class_exists($definition['class'])) {
$normalize_definition['class'] = $definition['class'];
} else {
throw new Exception(sprintf('class `%s` definition not found', $class));
}
} else {
throw new Exception(sprintf('class `%s` not definition', $class));
}
return $normalize_definition;
}
}
common/DB.php
<?php
namespace dhope\common;
class DB
{
private $dsn;
private $port;
public function __construct(string $dsn, int $port)
{
$this->dsn = $dsn;
$this->port = $port;
}
public function toString()
{
return [
'dsn' => $this->dsn,
'port' => $this->port
];
}
}
common/Mysql.php
<?php
namespace dhope\common;
define('MODE_RA', 0);
define('MODE_RW', 1);
class Mysql
{
private $db;
private $mode;
public function __construct(DB $db, $mode = MODE_RA)
{
$this->db = $db;
$this->mode = $mode;
}
public function toString()
{
return json_encode([
'db' => $this->db->toString(),
'mode' => $this->mode
]);
}
}
common / TableUser.php
<?php
namespace dhope\common;
class TableUser
{
private $handle;
private $table;
public function __construct(Mysql $connect)
{
$this->handle = $connect;
$this->table = 'user';
}
public function showTable()
{
return [
'table' => $this->table,
'handle' => $this->handle->toString()
];
}
}
The above code is implemented using a container class dependent processes + implantation techniques to create an instance of the class