通过类名动态生成对象
source link: https://www.tlanyan.me/dynamic-new-class-object/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
问题
前几天有人在PHP的QQ群里问生成对象的问题:
use A\B; $b = new B(); // 正确 $str = "B"; $b = new $str(); // 错误,提示:类"B"未找到
类似问题五六年前碰到过,因此印象深刻。热心提示要用 “ 完全限定类名 ” 形式,可惜连说两遍,提问题的人都没理解我说的(或者认为我的回复与其问题无关):
不得已下,写下示范代码并 @ 提问题的人,终于让其明白:
原理
问题解决了,背后的原理是什么?
从人的角度看,代码意图非常明显:动态生成类 B
的实例。但从执行引擎的角度,完全是另外一回事。其实 new $classname()
背后的运作行为类似于:
// 伪代码 if (class_exists($str)) { $b = new $str(); return $b; } throw ClassNotFoundException; // 或者用反射 try { $reflectionClass = new ReflectionClass($str); $b = $reflectionClass.newInstance(); return $b; } throw ClassNotFoundException;
要根据类名动态生成示例,首先要判断类是否存在吧?PHP中与之相关的是 class_exists
函数和 ReflectionClass
类。在上面的例子中,只传入字符串 “B”
, class_exists
回返回 true
吗?
答案是否定的。 class_exists
和 ReflectionClass
只会在全局类列表中根据名字查找,不会理会调用函数所在(或引入)的名字空间。同理,如果使用 use
引入类名并做别名( as
),别名类在 class_exists
中也会返回 false
。
那么PHP能否改进一下 class_exists
和 ReflectionClass
的行为,让其根据当前上下文判断?
可以这么做,但是代价很大,原因包括:
-
class_exists
和ReflectionClass
都没有指示程序上下文Context
的参数; - PHP比较坑的一点: 类名 不会像函数、常量一样往上逐级查找;
-
如果存在多个同名的类,加载哪个?如以下代码所示:
不管采取哪种行为,都会招致吐槽。
保持目前的情况,除动态生成实例时需要完全限定类名,并无其他槽点。并且实现上简单,行为明确且一致。
总结
作为一门脚本语言,PHP非常的灵活,但也会带来一些使用上的困惑。本文所讨论的根据类名动态生成对象,就要无视当前所在或引入的名字空间,必须使用完全限定类名形式。
作为对比,C++不能动态生成对象。Java要用 Class.forName
的方式获取 class
对象,然后再调用构造函数生成。Java不能直接 new
类名,避免了PHP中的坑,但 Class.forName
同样需要完全限定类名,避免不明确行为。
参考
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK