thinkphp5.1和thinkphp6容器的缺陷

容器实现类的实例自动绑定依靠了反射,但是5.1和6的代码,我认为还是有缺陷。话不多说直接上代码,以下是我单独做测试,将容器源码单独弄出来删除某些分支后的代码,保留了容器最基本的功能:

    class Container{
    
    private static $instance;   //容器实例

    public $instances = []; //注册池

    //单例模式,以静态的方式调用对象以及其方法
    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new static;
        }
        return self::$instance;
    }

    //注册池注册实例
    public function set($className, $class)
    {
        $this->instances[$className] = $class;
    }

    public function get($className,$vars=[])
    {
        $this->make($className,$vars);
    }

    public function make($abstract, $vars = [])
    {
        $object = $this->invokeClass($abstract, $vars);

        return $object;
    }

    public function invokeClass($class, $vars = [])
    {
            $reflect = new \ReflectionClass($class);

            $constructor = $reflect->getConstructor();

            $args = $constructor ? $this->bindParams($constructor, $vars) : [];

            return $reflect->newInstanceArgs($args);
    }

    public function bindParams($reflect, $vars = [])
    {
        if ($reflect->getNumberOfParameters() == 0) {
            return [];
        }
        //這個参数列表是必须要传入的否则,遇到递归的分析(即依赖注入的参数列表中带有对象类型参数)是绝对是报错的
        // 判断数组类型 数字数组时按顺序绑定参数
        reset($vars);
        $type   = key($vars) === 0 ? 1 : 0;
        $params = $reflect->getParameters();

        foreach ($params as $param) {
            $name      = $param->getName();
            $lowerName = self::parseName($name);
            $class     = $param->getClass();

            if ($class) {
                $args[] = $this->getObjectParam($class->getName(), $vars);
            } elseif (1 == $type && !empty($vars)) {
                $args[] = array_shift($vars);
            } elseif (0 == $type && isset($vars[$name])) {
                $args[] = $vars[$name];
            } elseif (0 == $type && isset($vars[$lowerName])) {
                $args[] = $vars[$lowerName];
            } elseif ($param->isDefaultValueAvailable()) {
                $args[] = $param->getDefaultValue();
            }
        }

        return $args;
    }

    public static function parseName($name, $type = 0, $ucfirst = true)
    {
        if ($type) {
            $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
                return strtoupper($match[1]);
            }, $name);
            return $ucfirst ? ucfirst($name) : lcfirst($name);
        }

        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
    }


    public function getObjectParam($className, &$vars)
    {
        $array = $vars;
        $value = array_shift($array);

        if ($value instanceof $className) {
            $result = $value;
            array_shift($vars);
        } else {
            $result = $this->make($className);
        }

        return $result;
    }

}

调用的代码:

spl_autoload_register('autoLoading');
function autoLoading()
{
    include './Car.php';
}
//echo person::class;
Container::getInstance()->get('person',[1,2]);

依赖注入的类:

class person {
    public function __construct(Car $obj,$a,$b)
    {
        $sub = $this->add($a,$b);
        $obj->speed($sub);
    }

    public function add($a,$b) {
        return $a+$b;
    }
}

class Car{
    public function __construct()
    {
        $app->show();
    }

    public function speed($sub) {
        echo '我的马力很猛,高达:'.$sub;
    }
}

缺陷:

1.参数分析不是非常合理,请看这段代码:

        reset($vars);
        $type   = key($vars) === 0 ? 1 : 0;
        $params = $reflect->getParameters();

        foreach ($params as $param) {
            $name      = $param->getName();
            $lowerName = self::parseName($name);
            $class     = $param->getClass();

            if ($class) {
                $args[] = $this->getObjectParam($class->getName(), $vars);
            } elseif (1 == $type && !empty($vars)) {
                $args[] = array_shift($vars);
            } elseif (0 == $type && isset($vars[$name])) {
                $args[] = $vars[$name];
            } elseif (0 == $type && isset($vars[$lowerName])) {
                $args[] = $vars[$lowerName];
            } elseif ($param->isDefaultValueAvailable()) {
                $args[] = $param->getDefaultValue();
            }
        }

如果你实例化的类构造函数里基本很少参数或者没有,那么皆大欢喜,如果你的构造函数参数列表不少,那么请看这样的3个调用:

Container::getInstance()->get('person',[1,2]);
Container::getInstance()->get('person',[new Car(),1,2]);
Container::getInstance()->get('person',[1,'name'=>2]);

这3个调用是完全可以运行的,并且在foreach分析中走的逻辑将会走$type==1,原因是这三个的第一个元素键都是0也就是数字索引数组的第一个下标.但是如果你这样去写

Container::getInstance()->get('person',['name'=>1,2]);

会直接报错。

原因是第一个参数将会走到$type==0的操作,但是第二个参数分析不了没有返回,导致第二个参数赋值不成功报错。这是第一个缺陷的地方,因为作者的意图是想分析数字索引和字符索引的数组,当然正常情况$vars参数列表传入的都是一个普通的数字数组。所以参数列表传入一定要注意对齐它这个foreach

 

缺陷二:只能递归一层,来上个代码就知道了:

<?php

class person {
    public function __construct(Car $obj,$a,$b)
    {
        $sub = $this->add($a,$b);
        $obj->speed($sub);
    }

    public function add($a,$b) {
        return $a+$b;
    }
}

class Car{
    public function __construct(App $app,$a,$b)
    {
        $app->show();
    }

    public function speed($sub) {
        echo '我的马力很猛,高达:'.$sub;
    }
}

class App{
    public function show() {
        echo "我是app类";
    }
}

现在变成了三个类,细心点你就发现,Car类我再次存放了一个依赖注入类以及一些参数。如果不对源码改造,这会直接报错,这也是一个不通用的地方,原因就是在这里:

protected function getObjectParam($className, &$vars)
    {
        $array = $vars;
        $value = array_shift($array);

        if ($value instanceof $className) {
            $result = $value;
            array_shift($vars);
        } else {
            $result = $this->make($className);
        }

        return $result;
    }

走make逻辑的时候没有传第二个参数,也就是参数列表数组,这也就导致Car实例化的时候报错,但是如果我这么改:

<?php

class person {
    public function __construct(Car $obj,$a,$b)
    {
        $sub = $this->add($a,$b);
        $obj->speed($sub);
    }

    public function add($a,$b) {
        return $a+$b;
    }
}

class Car{
    public function __construct(App $app)
    {
        $app->show();
    }

    public function speed($sub) {
        echo '我的马力很猛,高达:'.$sub;
    }
}

class App{
    public function show() {
        echo "我是app类";
    }
}

car类除了依赖注入的类参数,其他参数不要的话,源码是可以继续递归分析下去,感兴趣的可以在APP加一个构造函数测试。

<?php

class person {
    public function __construct(Car $obj,$a,$b)
    {
        $sub = $this->add($a,$b);
        $obj->speed($sub);
    }

    public function add($a,$b) {
        return $a+$b;
    }
}

class Car{
    public function __construct(App $app)
    {
        $app->show();
    }

    public function speed($sub) {
        echo '我的马力很猛,高达:'.$sub;
    }
}

class App{

    public function __construct(Soft $app)
    {
        $app->show();
    }
    public function show() {
        echo "我是app类";
    }
}

class Soft{
    public function show() {
        echo "我是Soft类";
    }
}

这段代码正常执行。

思路:要想真正变的通用,这段代码一个要规范$vars数组的传入格式,可能还要改造下代码,将对应的参数赋值进来,因为这个分析的模型就是一颗不定型的多叉树,根节点开始,需要对节点的孩子分析,如果是孩子节点是对象,则要递归分析,如果是普通参数,则使用该层的变量(该层栈)保存参数列表。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值