轉載請註明文章出處: https://tlanyan.me/dynamic-ne...
前幾天有人在PHP的QQ羣裏問生成對象的問題:php
use A\B; $b = new B(); // 正確 $str = "B"; $b = new $str(); // 錯誤,提示:類"B"未找到
相似問題五六年前碰到過,所以印象深入。熱心提示要用 "徹底限定類名" 形式,惋惜連說兩遍,提問題的人都沒理解我說的(或者認爲個人回覆與其問題無關):函數
不得已下,寫下示範代碼並 @ 提問題的人,終於讓其明白:spa
問題解決了,背後的原理是什麼?.net
從人的角度看,代碼意圖很是明顯:動態生成類B
的實例。但從執行引擎的角度,徹底是另一回事。其實new $classname()
背後的運做行爲相似於:code
// 僞代碼 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
。rem
那麼PHP可否改進一下class_exists
和ReflectionClass
的行爲,讓其根據當前上下文判斷?字符串
能夠這麼作,可是代價很大,緣由包括:get
class_exists
和ReflectionClass
都沒有指示程序上下文Context
的參數;
無論採起哪一種行爲,都會招致吐槽。it
保持目前的狀況,除動態生成實例時須要徹底限定類名,並沒有其餘槽點。而且實現上簡單,行爲明確且一致。
做爲一門腳本語言,PHP很是的靈活,但也會帶來一些使用上的困惑。本文所討論的根據類名動態生成對象,就要無視當前所在或引入的名字空間,必須使用徹底限定類名形式。
做爲對比,C++不能動態生成對象。Java要用Class.forName
的方式獲取class
對象,而後再調用構造函數生成。Java不能直接new
類名,避免了PHP中的坑,但Class.forName
一樣須要徹底限定類名,避免不明確行爲。