PHP動態(tài)實例化對象并向構(gòu)造函數(shù)傳遞參數(shù)

本博客的所有原創(chuàng)文章溯捆,遵循CC協(xié)議中的by-nc-sa許可協(xié)議丑搔,轉(zhuǎn)載請注明來自技研屋

在框架開發(fā)提揍,模塊化開發(fā)等場合啤月,我們可能有一種需求,那就是在PHP運行時動態(tài)實例化對象劳跃。

什么是動態(tài)實例化對象呢谎仲?我們先來看一下PHP有一種變量函數(shù)(可變函數(shù))的概念,例如如下代碼:

function foo() {

echo 'This is the foo function';

}

$bar = 'foo';

$bar();

運行上述代碼將會輸出“This is the foo function”刨仑。具體請參考PHP手冊:可變函數(shù)郑诺。當然,如果需要動態(tài)調(diào)用的話杉武,那么就使用call_user_func或call_user_func_array函數(shù)辙诞。這兩個函數(shù)的用法不是本文的重點,不懂的同學請查閱其它資料轻抱》赏浚回到本文的話題上:什么是動態(tài)實例化對象?本人認為動態(tài)實例化對象祈搜,即是:需要實例化的對象是在程序運行時(run-time)動態(tài)決定(由變量決定)需要實例化什么樣的對象较店,而不是直接寫死在代碼里。

通過上述例子我們已經(jīng)知道了如何在運行時動態(tài)調(diào)用一個函數(shù)了容燕,在現(xiàn)在面向?qū)ο蛉绱肆餍械慕裉炝撼剩谝恍┐a中,我們需要去動態(tài)去實例化一個類蘸秘,該怎么做呢官卡?

情況一:類的構(gòu)造函數(shù)沒有參數(shù)或參數(shù)的個數(shù)是確定的

如果類的構(gòu)造函數(shù)沒有參數(shù)或者我們要實例化的類根本沒有構(gòu)造函數(shù)蝗茁,似乎簡單一點,可以照上面的例子改一個嘛寻咒,嗯评甜,照葫蘆畫瓢,誰不會:

代碼示例:(構(gòu)造函數(shù)沒有參數(shù))

class FOO {

private $a, $b;

public function __construct() {

$this->a = 1;

$this->b = 2;

}

public function test() {

echo 'This is the method test of class FOO
';

echo '$this->a=', $this->a, ', $this->b=', $this->b;

}

}

$bar = 'FOO';

$foo = new $bar();

$foo->test();

運行一下仔涩,就看到了輸出了如下結(jié)果:

This is the method test of class FOO

$this->a=1, $this->b=2

嗯忍坷,我們要傳參的話,那么就這樣吧:

class FOO {

private $a, $b;

public function __construct($a, $b) {

$this->a = $a;

$this->b = $b;

}

public function test() {

echo 'This is the method test of class FOO
';

echo '$this->a=', $this->a, ', $this->b=', $this->b;

}

}

$bar = 'FOO';

$foo = new $bar('test', 5);

$foo->test();

一樣可以得到類似的結(jié)果:

This is the method test of class FOO$this->a=test, $this->b=5

很理想嘛熔脂。

情況二:類的構(gòu)造函數(shù)的參數(shù)個數(shù)不確定

這種情況就要麻煩很多了佩研,但是如果要寫得比較通用的話,就不得不考慮這種情況了霞揉。例如旬薯,我們有如下兩個類

class FOO {

public function test() {

echo 'This is the method test of class FOO';

}

}

class BAR {

private $a, $b;

public function __construct($a, $b) {

$this->a = $a;

$this->b = $b;

}

public function test() {

echo 'This is the method test of class BAR
';

echo '$this->a=', $this->a, ', $this->b=', $this->b;

}

}

我們想要一個通用的方式來實例化這兩個類。我們注意到FOO類是沒有寫構(gòu)造函數(shù)的适秩,或者是可以認為FOO類的構(gòu)造函數(shù)的參數(shù)個數(shù)為零绊序;而BAR類的構(gòu)造函數(shù)卻有參數(shù)。還好秽荞,PHP5已經(jīng)足夠強大了骤公,引入了反射的概念,具體請參考PHP手冊:反射扬跋,雖然手冊上也沒有什么可參考的:)阶捆。還好,命名寫得不錯钦听,從類名和方法名上面已經(jīng)可以看出大概的端倪洒试,不需要太多的文字。

那么好吧朴上,就讓我們用PHP5的反射來著手這個事情:

(還在用PHP4的同學請不要走開垒棋,如果你擁有一個沒有反射的PHP版本或者是你為了兼容也好還是不想升級也好,反正不想用反射的痪宰,下面有解決方案)

$class = new ReflectionClass('FOO');$foo = $class->newInstance(); //或者是$foo = $class->newInstanceArgs();$foo->test();

看到什么了沒有叼架?接著來:

$class = new ReflectionClass('BAR');$bar = $class->newInstanceArgs(array(55, 65));$bar->test();

OK,似乎可以了酵镜,那么就整理一下吧碉碉,來個通用函數(shù)柴钻,我們想這么設計淮韭,此函數(shù)的第一個函數(shù)是要實例化的類名,從第二個參數(shù)開始就是要實例化類的構(gòu)造函數(shù)的參數(shù)贴届,有幾個就寫幾個上去靠粪,沒有就不寫蜡吧。要想實現(xiàn)參數(shù)個數(shù)可變的函數(shù),我們有兩種方法:

第一種是類似于:

function foo($arg1, $arg2 = 123, $arg3 = 'test', $arg4 = null, ....... ) {????//some code;}

的辦法占键,這種方法有兩個缺點昔善,第一個是你如果需要傳100個參數(shù)難道就寫100個參數(shù)嗎?第二個是你還要在程序里判斷哪個參數(shù)是不是null畔乙,或是其它默認值君仆。(題外話:這種寫法的參數(shù)默認值必須放在最后,你不能將沒有默認值的參數(shù)插在有默認值的中間或前面牲距,否則你調(diào)用的時候也必須顯式地寫上有默認值參數(shù)的值)

另一種實現(xiàn)參數(shù)數(shù)量可變的方法是在函數(shù)里用PHP的內(nèi)置函數(shù)func_get_args(猛擊這里看手冊)取得傳給函數(shù)的參數(shù)返咱。類似的函數(shù)有func_get_num和func_get_arg,算了牍鞠,我懶咖摹,你們自己找手冊看吧。

那么难述,用這個函數(shù)似乎要方便很多萤晴,我們根據(jù)想象的函數(shù)參數(shù)的排列,代碼應該是這個樣子的:

function newInstance() {?? ?$arguments = func_get_args();?? ?$className = array_shift($arguments);?? ?$class = new ReflectionClass($className);?? ?return $class->newInstanceArgs($arguments);}

OK胁后,讓我們來看一下效果:

$foo = newInstance('FOO');$foo->test();//輸出結(jié)果://This is the method test of class FOO$bar = newInstance('BAR', 3, 5);$bar->test();//輸出結(jié)果://This is the method test of class BAR//$this->a=3, $this->b=5

短短四行代碼店读,效果相當完美啊。那么攀芯,如果應用到類里面两入,我們可以利用這種思想,直接寫成魔術(shù)方法敲才,可以讓我們的類更酷哦裹纳!

class INSTANCE {??? function __call($className, $arguments) {??? ??? $class = new ReflectionClass($className);??? ??? return $class->newInstanceArgs($arguments);??? }}$inst = new INSTANCE();$foo = $inst->foo();$foo->test();//輸出結(jié)果://This is the method test of class FOO$bar = $inst->bar('arg1', 'arg2');$bar->test();//輸出結(jié)果://This is the method test of class BAR//$this->a=3, $this->b=5

咔咔,爽吧紧武。

接下來討論一下不使用反射類的情況剃氧。例如PHP4中就沒有反射,而一些老項目就是運行在PHP4上面的阻星∨蟀埃或者是要保證項目對未知環(huán)境的兼容性,Whatever妥箕,來關(guān)心一下怎么動態(tài)傳參吧滥酥。PHP中動態(tài)傳參的函數(shù)只有一個:call_user_func_array(輕擊此處查看手冊)。這是一個動態(tài)調(diào)用函數(shù)的函數(shù)畦幢,作用是可以將函數(shù)的參數(shù)以數(shù)組的形式傳遞給要調(diào)用的函數(shù)坎吻。好吧,我自己也被自己繞暈了宇葱,直接來看實例:

function foo($a, $b) {??? echo '$a=', $a, '
';??? echo '$b=', $b;}call_user_func_array('foo', array(1, 'string'));//本例輸出結(jié)果://$a=1//$b=string

那么瘦真,要實現(xiàn)用這種方法來動態(tài)實例化對象并傳參刊头,呃……,只有曲線救國了诸尽,我們得先寫一個函數(shù)原杂,讓這個函數(shù)來實例化對象,而這個函數(shù)的參數(shù)就原原本本地傳給要實例化對象的類的構(gòu)造函數(shù)就好了您机。打状┮蕖!那這個函數(shù)得有幾個參數(shù)凹士础被碗?怎么實現(xiàn)傳遞不同個數(shù)的參數(shù)呢?嘿嘿仿村,我一聲冷笑锐朴,你忘了PHP里提供一個創(chuàng)建匿名函數(shù)的函數(shù)嗎?(又開始繞起來了……)create_function(手冊在此)蔼囊,照著手冊里面的例子直接畫就可以了焚志,我也懶得打字了,直接看下面的代碼畏鼓,注釋我寫清楚點大家都明白了:

function newInst() {??? //取得所有參數(shù)?? ?$arguments = func_get_args();??? //彈出第一個參數(shù)酱酬,這是類名,剩下的都是要傳給實例化類的構(gòu)造函數(shù)的參數(shù)了?? ?$className = array_shift($arguments);??? //給所有的參數(shù)鍵值加個前綴????$keys = array_keys($arguments);????array_walk($keys, create_function('&$value, $key, $prefix', '$value = $prefix . $value;'), '$arg_');??? //動態(tài)構(gòu)造實例化類的函數(shù)云矫,主要是動態(tài)構(gòu)造參數(shù)的個數(shù)????$paramStr = implode(', ',$keys);????$newClass=create_function($paramStr, "return new {$className}({$paramStr});");??? //實例化對象并返回????return call_user_func_array($newClass, $arguments);}

好了膳沽,至于效果是什么,就麻煩各位看官自己動動手让禀,運行一下看看挑社,是不是自己期望的結(jié)果。

如果改寫成類里面的魔術(shù)方法巡揍,哼哼痛阻,威力嘛,你懂的腮敌!然后呢阱当,我還是懶了,如果要寫成魔術(shù)方法的話糜工,相信這么easy的事情你很輕松就辦到了弊添。就當一個作業(yè)吧。另捌木,本文的代碼都是本人運行過的油坝,但是,寫文章的時候沒有使用復制/粘貼功能,所以免钻,你最好是也不要復制粘貼。如果從本文中的代碼copy下來運行出錯的話崔拥,還煩請各位自己debug一下极舔,編程不就是要自己寫么。

本來這個話題到這里就應該可以結(jié)束了链瓦,但是想到我在想到這個辦法之前用的方法(好繞……)拆魏,本著重在交流的態(tài)度,一起放出來慈俯。我例兩個關(guān)鍵函數(shù)吧:extracteval渤刃。只是我個人覺得用eval函數(shù)比較山寨,能不用最好不用贴膘。于是又想出了上面的辦法卖子,哈哈,編程最重要的是思想刑峡,不是嗎洋闽?好,又是一道作業(yè)題了突梦,大家可以試試用這兩個函數(shù)(當然也會用到別的函數(shù))來實現(xiàn)帶不定數(shù)量的參數(shù)動態(tài)實例化對象的函數(shù)诫舅。

本博客的所有原創(chuàng)文章,遵循CC協(xié)議中的by-nc-sa許可協(xié)議宫患,轉(zhuǎn)載請注明來自技研屋刊懈。

轉(zhuǎn)載地址:http://taoyh163.blog.163.com/blog/static/1958035620141371710957/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市娃闲,隨后出現(xiàn)的幾起案子虚汛,更是在濱河造成了極大的恐慌,老刑警劉巖皇帮,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泽疆,死亡現(xiàn)場離奇詭異,居然都是意外死亡玲献,警方通過查閱死者的電腦和手機殉疼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捌年,“玉大人瓢娜,你說我怎么就攤上這事±裨ぃ” “怎么了眠砾?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長托酸。 經(jīng)常有香客問我褒颈,道長柒巫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任谷丸,我火速辦了婚禮堡掏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刨疼。我一直安慰自己泉唁,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布揩慕。 她就那樣靜靜地躺著亭畜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迎卤。 梳的紋絲不亂的頭發(fā)上拴鸵,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音蜗搔,去河邊找鬼宝踪。 笑死,一個胖子當著我的面吹牛碍扔,可吹牛的內(nèi)容都是我干的瘩燥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼不同,長吁一口氣:“原來是場噩夢啊……” “哼厉膀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起二拐,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤服鹅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后百新,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體企软,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年饭望,在試婚紗的時候發(fā)現(xiàn)自己被綠了仗哨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡铅辞,死狀恐怖厌漂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斟珊,我是刑警寧澤苇倡,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響旨椒,放射性物質(zhì)發(fā)生泄漏晓褪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一综慎、第九天 我趴在偏房一處隱蔽的房頂上張望涣仿。 院中可真熱鬧,春花似錦寥粹、人聲如沸变过。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至岛杀,卻和暖如春阔拳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伏伐。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工介杆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扳还,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓货裹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親精偿。 傳聞我的和親對象是個殘疾皇子弧圆,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容