服务容器是一个用于管理类依赖和执行依赖注入的强大工具。

依赖注入和控制反转是对同一件事情的不同描述,它们描述的角度不同。依赖注入是从应用程序的角度在描述,应用程序依赖容器创建并注入它所需要的外部资源。而控制反转是从容器的角度在描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

<?php

/**
 * Interface Log
 * 面向接口编程
 */
interface Log
{
    public function write();
}

class FileLog implements Log
{
    public function write()
    {
        echo 'filelog write...' . PHP_EOL;
    }
}

class DataBaseLog implements Log
{
    public function write()
    {
        echo 'dblog write...' . PHP_EOL;
    }
}

/**
 * Class Request
 * 模拟请求类
 */
class Request
{
    public function toArray()
    {
        return ['name' => 'value'];
    }
}

/**
 * User类依赖Log接口的实现
 */
class User
{
    private $log;
    private $extra;

    public function __construct(Log $log, $extra)
    {
        $this->log = $log;
        $this->extra = $extra;
    }

    /**
     * 模拟用户登录写入登录日志
     */
    public function login(Request $request)
    {
        echo '接收登录请求的参数json:' . json_encode($request->toArray()) . PHP_EOL;
        echo 'user log success...' . PHP_EOL;
        $this->log->write();
    }
}


/**
 * Class Ioc
 * 模拟IoC容器
 * 类从注册到实例化,最终被我们所使用,都是服务容器负责
 */
class Ioc
{
    protected $bindings = [];
    protected $instances = [];
    protected static $ioc;

    protected function __construct()
    {
    }

    public static function getInstance()
    {
        if (is_null(self::$ioc)) {
            self::$ioc = new self();
        }
        return self::$ioc;
    }

    /**
     * 注册绑定 (绑定自身、闭包、接口)
     * 也就是服务
     * @param $abstract
     * @param null $concrete
     */
    public function bind($abstract, $concrete = null, $params = [])
    {
        if (is_null($concrete)) {
            $concrete = $abstract;
        }

        if (!$concrete instanceof Closure) {
            $this->bindings[$abstract]['concrete'] = function (Ioc $ioc) use ($concrete, $params) {
                return $ioc->build($concrete, $params);
            };
        } else {
            $this->bindings[$abstract]['concrete'] = $concrete;
        }
    }

    /**
     * 返回对象
     * @param $abstract
     * @return mixed
     */
    public function make($abstract)
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        if (isset($this->bindings[$abstract]['concrete'])) {
            $concrete = $this->bindings[$abstract]['concrete'];
            $instance = $concrete($this);
            $this->instances[$abstract] = $instance;
            return $instance;
        }

        throw new RuntimeException($abstract . ' is not bound yet');
    }

    /**
     * 创建对象
     * @param $concrete
     * @return object
     * @throws ReflectionException
     */
    public function build($concrete, $params)
    {
        $reflectionClass = new ReflectionClass($concrete);
        $constructor = $reflectionClass->getConstructor();
        if (is_null($constructor)) {
            return $reflectionClass->newInstance();
        }
        $isInstantiable = $reflectionClass->isInstantiable();
        if (!$isInstantiable) {
            throw new ReflectionException("$concrete cant construct");
        }
        $parameters = $constructor->getParameters();
        $dependencies = $this->getDependencies($parameters, $params);
        return $reflectionClass->newInstanceArgs($dependencies);
    }

    /**
     * 获取参数的依赖
     * @param array $parameters
     * @return array
     * @throws ReflectionException
     */
    public function getDependencies(array $parameters, $params = [])
    {
        $dependencies = [];
        /**
         * @var ReflectionParameter $parameter
         */
        foreach ($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if (is_null($dependency)) {
                if ($parameter->isDefaultValueAvailable()) {
                    $dependency[] = $parameter->getDefaultValue();
                } else {
                    if (!isset($params['$' . $parameter->getName()])) {
                        throw new ReflectionException('The constructor of the ' . $parameter->getDeclaringClass()->getName() . ' class has no default value of $' . $parameter->getName());
                    }
                    $dependencies[] = $params['$' . $parameter->getName()];
                }
            } else {
                $dependencies[] = $this->make($dependency->name);
            }
        }
        return $dependencies;
    }
}

/**
 * Class Facade
 * 模拟门面
 */
class Facade
{
    protected static function getFacadeClass()
    {
        return '';
    }

    public static function __callStatic($name, $arguments)
    {
        $instance = Ioc::getInstance()->make(static::getFacadeClass());
        return call_user_func_array([$instance, $name], $arguments);
    }
}

class UserFacade extends Facade
{
    protected static function getFacadeClass()
    {
        return 'User';
    }
}

// run
/*
 | 模拟容器绑定
 */
$ioc = Ioc::getInstance();
$ioc->bind(Request::class);//绑定类
$ioc->bind(Log::class, FileLog::class);//绑定接口
//$ioc->bind(Log::class, DataBaseLog::class);
$ioc->bind(User::class, null, ['$extra' => '额外参数']);//绑定类 且支持额外参数绑定
$ioc->bind('test', function () {//绑定闭包
    return 'test' . PHP_EOL;
});

/*
 | 模拟路由访问 User 控制器下的 login 方法
 */
$reflectionMethod = new ReflectionMethod(User::class, 'login');
$parameters = $reflectionMethod->getParameters();
$dependencies = $ioc->getDependencies($parameters);
$user = $ioc->make('test');
$user = $ioc->make(User::class);
// 普通调用
echo '普通调用:' . PHP_EOL;
call_user_func_array([$user, 'login'], $dependencies);
// facade门面调用
echo '门面调用:' . PHP_EOL;
call_user_func_array([UserFacade::class, 'login'], $dependencies);
02-13 03:59