PHP 依赖注入 (DI) 和容器 (IoC) 的简单实现

1,116 阅读1分钟

百闻不如一 Run 直接上代码

<?php
/**
 * Copyright (C) wujunze.com 2017 All rights reserved.
 *
 * @author     :Panda(itwujunze@163.com)
 * @date       :2017/08/31 16:08:13
 * @version    :1.0
 * @brief      : PHP依赖注入(DI)和容器(IoC)的简单实现
 */

namespace App;

use ReflectionClass;
use ReflectionParameter;

use Closure;

class Car
{
    public function driveCar($type = '法拉利')
    {
        return "人开{$type}";
    }
}

class People
{
    private $car;

    public function __construct(Car $car)
    {
        $this->car = $car;
    }

    public function run($type)
    {
        return $this->car->driveCar($type);
    }
}

class Container
{
    /**
     *  容器绑定,用来装提供的实例或者 提供实例的回调函数
     * @var array
     */
    public $building = [];

    /**
     * 注册一个绑定到容器
     *
     * @param $abstract
     * @param null $concrete
     * @param bool $shared
     */
    public function bind($abstract, $concrete = null, $shared = false)
    {
        if (is_null($concrete)) {
            $concrete = $abstract;
        }

        if (!$concrete instanceOf Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }
        $this->building[$abstract] = compact("concrete", "shared");
    }

    /**注册一个共享的绑定 单例
     *
     * @param $abstract
     * @param $concrete
     * @param bool $shared
     */
    public function singleton($abstract, $concrete, $shared = true)
    {
        $this->bind($abstract, $concrete, $shared);
    }

    /**
     * 默认生成实例的回调闭包
     *
     * @param $abstract
     * @param $concrete
     * @return Closure
     */
    public function getClosure($abstract, $concrete)
    {
        return function ($c) use ($abstract, $concrete) {
            $method = ($abstract == $concrete) ? 'build' : 'make';

            return $c->$method($concrete);
        };
    }

    /** 生成实例
     * 
     * @param $abstract
     * @return mixed|object
     */
    public function make($abstract)
    {
        var_dump($abstract);
        echo '--------' . PHP_EOL;
        $concrete = $this->getConcrete($abstract);
        if ($this->isBuildable($concrete, $abstract)) {
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }

        return $object;
    }

    /**
     * 获取绑定的回调函数
     */
    public function getConcrete($abstract)
    {
        if (!isset($this->building[$abstract])) {
            return $abstract;
        }

        return $this->building[$abstract]['concrete'];
    }

    /**
     * 判断 是否 可以创建服务实体
     */
    public function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }

    /**
     * 根据实例具体名称实例具体对象
     */
    public function build($concrete)
    {
        if ($concrete instanceof Closure) {
            return $concrete($this);
        }

        //创建反射对象
        $reflector = new ReflectionClass($concrete);

        if (!$reflector->isInstantiable()) {
            //抛出异常
            throw new \Exception('无法实例化');
        }

        $constructor = $reflector->getConstructor();
        if (is_null($constructor)) {
            return new $concrete;
        }

        $dependencies = $constructor->getParameters();
        $instance = $this->getDependencies($dependencies);
        var_dump($instance);
        return $reflector->newInstanceArgs($instance);

    }

    //通过反射解决参数依赖
    public function getDependencies(array $dependencies)
    {
        $results = [];
        foreach ($dependencies as $dependency) {
            $results[] = is_null($dependency->getClass())
                ? $this->resolvedNonClass($dependency)
                : $this->resolvedClass($dependency);
        }

        return $results;
    }

    //解决一个没有类型提示依赖
    public function resolvedNonClass(ReflectionParameter $parameter)
    {
        if ($parameter->isDefaultValueAvailable()) {
            return $parameter->getDefaultValue();
        }
        throw new \Exception('出错');

    }

    //通过容器解决依赖
    public function resolvedClass(ReflectionParameter $parameter)
    {
        return $this->make($parameter->getClass()->name);

    }

}

/**
 * 服务容器运行流程
 */

//实例化容器类
$app = new Container();
//向容器中填充Car
$app->bind('Car', 'App\Car');
//填充People
$app->bind('People', 'App\People');
//通过容器实现依赖注入,完成类的实例化;
$people = $app->make('People');
//调用方法
echo $people->run('昌河') . PHP_EOL;


高清原图