本博客的所有原創(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ù)吧:extract和eval渤刃。只是我個人覺得用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/