經過類名動態生成對象

轉載請註明文章出處: 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_existsReflectionClass只會在全局類列表中根據名字查找,不會理會調用函數所在(或引入)的名字空間。同理,若是使用use引入類名並作別名(as),別名類在class_exists中也會返回falserem

那麼PHP可否改進一下class_existsReflectionClass的行爲,讓其根據當前上下文判斷?字符串

能夠這麼作,可是代價很大,緣由包括:get

  1. class_existsReflectionClass都沒有指示程序上下文Context的參數;
  2. PHP比較坑的一點:類名不會像函數、常量同樣往上逐級查找;
  3. 若是存在多個同名的類,加載哪一個?如如下代碼所示:

    示例代碼

    無論採起哪一種行爲,都會招致吐槽。it

保持目前的狀況,除動態生成實例時須要徹底限定類名,並沒有其餘槽點。而且實現上簡單,行爲明確且一致。

總結

做爲一門腳本語言,PHP很是的靈活,但也會帶來一些使用上的困惑。本文所討論的根據類名動態生成對象,就要無視當前所在或引入的名字空間,必須使用徹底限定類名形式。

做爲對比,C++不能動態生成對象。Java要用Class.forName的方式獲取class對象,而後再調用構造函數生成。Java不能直接new類名,避免了PHP中的坑,但Class.forName一樣須要徹底限定類名,避免不明確行爲。

參考

  1. PHP回顧之反射
  2. PHP中的重載
  3. Using namespaces: fallback to global function/constant
相關文章
相關標籤/搜索