PHP

(四)从零编写PHP容器-添加绑定解析

2020-02-19  本文已影响0人  FinalZero

项目源码

功能实现

  1. 代码优化
  2. 添加绑定解析,优先从绑定实例中获取参数实例

代码实现 Container

use Closure;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use ReflectionClass;
use ReflectionException;
use ReflectionParameter;

class Container
{
    /**
     * @var array
     */
    protected $binds = [];

    /**
     * @param string $abstract
     * @param mixed $instance
     * @return $this
     */
    public function bind(string $abstract, $instance)
    {
        $this->binds[$abstract] = $instance;
        return $this;
    }
    
    /**
     * create a new object
     * @param string $abstract
     * @return Closure|object
     * @throws ReflectionException
     * @throws Exception
     */
    public function create($abstract)
    {
        return $this->createInstance($abstract);
    }
    
    /**
     * create new instance
     * @param string $abstract
     * @param array $tmp
     * @return Closure|object
     * @throws ReflectionException
     * @throws Exception
     */
    protected function createInstance(string $abstract, array $tmp = [])
    {
        if (isset($this->binds[$abstract])) {
            return $this->binds[$abstract];
        } else if (isset($tmp[$abstract])) {
            return $tmp[$abstract];
        } else if (array_key_exists($abstract, $tmp)) {
            throw new class('can not create a circular dependencies class object.') extends Exception implements ContainerExceptionInterface{ };
        } else {
            $tmp[$abstract] = null;
        }
        $refClass = new ReflectionClass($abstract);
        if ($refClass->isInterface() || $refClass->isAbstract()) {
            throw new class('can not create a class in interface or abstract.') extends Exception implements ContainerExceptionInterface{ };
        } elseif ($refClass->getName() === Closure::class) {
            return function () {};
        } elseif ($refClass->hasMethod('__construct')) {
            $_constructParams = array_map(function (ReflectionParameter $param) use ($abstract, $tmp) {
                if ($result = $this->paramsHandle($param) instanceof ReflectionClass) {
                    return $this->createInstance($param->getClass()->getName(), $tmp);
                } else {
                    return $result;
                }
            }, $refClass->getMethod('__construct')->getParameters());
        }
        return $tmp[$abstract] = $refClass->newInstance(... ($_constructParams ?? []));
    }
    
    /**
     * @param ReflectionParameter $param
     * @return mixed|ReflectionClass|null
     * @throws ReflectionException
     */
    private function paramsHandle(ReflectionParameter $param)
    {
        if ($param->getClass()) {
            return $param->getClass();
        } elseif ($param->isDefaultValueAvailable()) {
            return $param->getDefaultValue();
        } elseif ($param->getType()) {
            return [
                    'string' => '',
                    'int' => 0,
                    'array' => [],
                    'bool' => false,
                    'float' => 0.0,
                    'iterable' => [],
                    'callable' => function() {}
                ][$param->getType()->getName()] ?? null;
        } else {
            return null;
        }
    }
}

实现思路

实现思路

  1. 将基础数据类型判断先做分离(方便后期会做基础数据的动态映射)
  2. 在容器类中添加一个binds存储绑定的类
  3. 在创建方法createInstance中读取变量

示例

class E
{
    protected $msg;

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

    public function show()
    {
        return $this->msg;
    }
}

class D
{
    protected $e;

    public function __construct(E $e)
    {
        $this->e = $e;
    }

    public function show()
    {
        $this->e->show();
    }
}
$container = new Container();

$container->bind(E::class, new E('123'));

# 将获取到绑定的`new E('123')`的实例
$e = $container->create(E::class)
$e->show(); // 123

$d = $container->create(D::class)
$d->show(); // 123
上一篇 下一篇

猜你喜欢

热点阅读