依赖注入demo

让我们看一个例子:

class UserProvider{
    protected $connection;

    public function __construct(){
        $this->connection = new Connection;
    }

    public function retrieveByCredentials( array $credentials ){
        $user = $this->connection
                        ->where( 'email', $credentials['email'])
                        ->where( 'password', $credentials['password'])
                        ->first();

        return $user;
    }
}

如果你要测试或者维护这个类,你必须访问数据库的实例来进行一些查询。为了避免必须这样做,你可以将此类与其他类进行 解耦 ,你有三个选项之一,可以将 Connection 类注入而不需要直接使用它。

将组件注入类时,可以使用以下三个选项之一:

构造方法注入

class UserProvider{
    protected $connection;

    public function __construct( Connection $con ){
        $this->connection = $con;
    }
}

Setter 方法注入

同样,我们也可以使用 Setter 方法注入依赖关系:

class UserProvider{
    protected $connection;
    public function __construct(){
        ...
    }

    public function setConnection( Connection $con ){
        $this->connection = $con;
    }
}

接口注入

interface ConnectionInjector{
    public function injectConnection( Connection $con );
}

class UserProvider implements ConnectionInjector{
    protected $connection;

    public function __construct(){
        ...
    }

    public function injectConnection( Connection $con ){
        $this->connection = $con;
    }
}

当一个类实现了我们的接口时,我们定义了 injectConnection 方法来解决依赖关系。

优势

现在,当测试我们的类时,我们可以模拟依赖类并将其作为参数传递。每个类必须专注于一个特定的任务,而不应该关心解决它们的依赖性。这样,你将拥有一个更专注和可维护的应用程序。

======================= 华丽的分割线 ===================
 
目录结构:

部分代码:

index.php

<?php

spl_autoload_register('autoLoad', true, true);

use a\TestA;
use b\TestB;

// 要调用的类
$class = TestA::class;
// 调用类中的方法
$method = 'hello';

$obj = createObj($class);
$methodParam = parseMethod(TestA::class, $method);
call_user_func_array([$obj, $method], $methodParam);

/**
 * 根据类名 创建对象 并自动注入
 * @param string $className
 * @return object
 * @throws ReflectionException
 */
function createObj(string $className): object
{
    $reflction = new ReflectionClass($className);
    $paramList = parseConstructor($reflction);
    $obj = $reflction->newInstanceArgs($paramList);
    return $obj;
}

/**
 * 解析类的构造函数,返回参数列表
 * @param ReflectionClass $reflction
 * @return array
 */
function parseConstructor(ReflectionClass $reflction): array
{
    $paramList = [];

    // 如果构造方法存在,返回 object(ReflectionMethod) 反之返回 null
    $constructor = $reflction->getConstructor();

    if (!is_null($constructor)) {
        // 返回数组 object(ReflectionParameter) 参数列表
        $params = $constructor->getParameters();
        foreach ($params as $param) {
            // 获取参数的类型提示类,类型为 object(ReflectionClass) 若没有返回 null
            if (!is_null($param->getClass())) {
                // 获取类名
                $paramList[] = createObj($param->getClass()->getName());
            } else {
                // 获取默认值
                $paramList[] = $param->getDefaultValue();
            }
        }
    }
    return $paramList;
}

/**
 * 解析返回类的某方法参数
 *
 * @param string $className
 * @param string $methodName
 * @return array
 * @throws ReflectionException
 */
function parseMethod(string $className, string $methodName)
{
    $paramList = [];
    // object(ReflectionMethod)
    $reflection = new ReflectionMethod($className, $methodName);
    $params = $reflection->getParameters();
    foreach ($params as $param) {
        // 获取参数的类型提示类,类型为 object(ReflectionClass) 若没有返回 null
        if (!is_null($param->getClass())) {
            // 获取类名
            $paramList[] = createObj($param->getClass()->getName());
        } else {
            // 获取默认值
            $paramList[] = $param->getDefaultValue();
        }
    }
    return $paramList;
}

function autoLoad($className)
{
    require_once __DIR__.'/'.$className.'.php';
}

function dump($data)
{
    echo '<pre>';
    var_dump($data);
    exit();
}

class TestA

<?php

namespace a;

use b\TestB;
use face\Test;

class TestA implements Test
{
    const VERSION = 'A';
    public $name;
    public $objB;

    public function __construct(TestB $b, $name='aaa')
    {
        $this->objB = $b;
        $this->name = $name;
    }

    public function hello(TestB $b)
    {
        var_dump($b);
    }
}

class TestB

<?php

namespace b;

use face\Test;
use c\TestC;

class TestB implements Test
{
    const VERSION = 'B';
    public $name;
    public $objC;

    public function __construct($name = 'bbb', TestC $c)
    {
        $this->objC = $c;
        $this->name = $name;
    }
}

class TestC

<?php

namespace c;

use face\Test;

class TestC implements Test
{
    const VERSION = 'C';

    public function __construct($name = 'ccc')
    {
    }
}

class Face

<?php

namespace face;

interface Test
{
    
}

 

另一个实例:

<?php

class Point
{
    public $x;
    public $y;

    public function __construct($x = 0, $y = 0)
    {
        $this->x = $x;
        $this->y = $y;
    }
}

class Circle
{
    protected $radius;

    protected $center;

    public function __construct(Point $point, $raduis = 1)
    {
        $this->center = $point;
        $this->radius = $raduis;
    }

    public function printCenter()
    {
        printf('center coordinate is (%d, %d)', $this->center->x, $this->center->y);
    }

    public function area()
    {
        return 3.14 * pow($this->radius, 2);
    }
}

class DI
{
    public function make($className)
    {
        if (class_exists($className)) {
            $reflectionClass = new ReflectionClass($className);
            $constructor = $reflectionClass->getConstructor();
            if (is_null($constructor)) {
                return $reflectionClass->newInstance();
            } else {
                $parameters = $constructor->getParameters();
                $dependencies = $this->getDependencies($parameters);

                return $reflectionClass->newInstanceArgs($dependencies);
            }

        } else {
            printf('%s', 'class is not exist');
        }
    }

    // 依赖解析
    protected function getDependencies($parameters)
    {
        $dependencies = [];
        foreach ($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if (is_null($dependency)) {
                if ($parameter->isDefaultValueAvailable()) {
                    $dependencies[] = $parameter->getDefaultValue();
                } else {
                    //不是可选参数的为了简单直接赋值为字符串0
                    //针对构造方法的必须参数这个情况
                    //laravel是通过service provider注册closure到IocContainer,
                    //在closure里可以通过return new Class($param1, $param2)来返回类的实例
                    //然后在make时回调这个closure即可解析出对象
                    //具体细节我会在另一篇文章里面描述
                    $dependencies[] = '0';
                }
            } else {
                //递归解析出依赖类的对象
                $dependencies[] = $this->make($parameter->getClass()->name);
            }
        }

        return $dependencies;
    }
}


echo '<pre>';
$obj = (new DI)->make(Circle::class);
$obj->printCenter();

 

参考文档:

https://juejin.im/post/5cac58ecf265da03a85a9f9a#heading-0

https://juejin.im/post/5ae292e7518825673446c1fe

https://www.insp.top/learn-laravel-container

https://github.com/kevinyan815/Learning_Laravel_Kernel/blob/master/articles/reflection.md

posted @ 2019-04-11 11:10  白開水  阅读(379)  评论(0编辑  收藏  举报