Dependency injection implemented using php

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

Published 31 original articles · won praise 3 · views 10000 +

Guess you like

Origin blog.csdn.net/qq_36557960/article/details/99172917
Recommended