php构造函数实例化,php动态实例化对象并向构造函数传递参
数的案例分析
在框架开发,模块化开发等场合,我们可能有⼀种需求,那就是在PHP运⾏时动态实例化对象。
什么是动态实例化对象呢?我们先来看⼀下PHP有⼀种变量函数(可变函数)的概念,例如如下代码:function foo() {
echo 'This is the foo function';
}
$bar = 'foo';
$bar();
运⾏上述代码将会输出“This is the foo function”。具体请参考PHP⼿册:可变函数。当然,如果需要动态调⽤的话,那么就使⽤
call_user_func或call_user_func_array函数。这两个函数的⽤法不是本⽂的重点,不懂的同学请查阅其
它资料。回到本⽂的话题上:什么是动态实例化对象?本⼈认为动态实例化对象,即是:需要实例化的对象是在程序运⾏时(run-time)动态决定(由变量决定)需要实例化什么样的对象,⽽不是直接写死在代码⾥。
通过上述例⼦我们已经知道了如何在运⾏时动态调⽤⼀个函数了,在现在⾯向对向如此流⾏的今天,在⼀些代码中,我们需要去动态去实例化⼀个类,该怎么做呢?
情况⼀:类的构造函数没有参数或参数的个数是确定的
如果类的构造函数没有参数或者我们要实例化的类根本没有构造函数,似乎简单⼀点,可以照上⾯的例⼦改⼀个嘛,嗯,照葫芦画瓢,谁不会:
代码⽰例:(构造函数没有参数)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();
运⾏⼀下,就看到了输出了如下结果: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();
⼀样可以得到类似的结果:This is the method test of class FOO
$this->a=test, $this->b=5
很理想嘛。
情况⼆:类的构造函数的参数个数不确定
这种情况就要⿇烦很多了,但是如果要写得⽐较通⽤的话,就不得不考虑这种情况了。例如,我们有如下两个类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类是没有写构造函数的,或者是可以认为FOO类的构造函数的参数个数为零;⽽BAR类的构造函数却有参数。还好,PHP5已经⾜够强⼤了,引⼊了反射的概念,具体请参考PHP⼿册:反射,虽然⼿册上也没有什么可参考的:)。还好,命名写得不错,从类名和⽅法名上⾯已经可以看出⼤概的端倪,不需要太多的⽂字。
那么好吧,就让我们⽤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,似乎可以了,那么就整理⼀下吧,来个通⽤函数,我们想这么设计,此函数的第⼀个函数是要实例化的类名,从第⼆个参数开始就是要实例化类的构造函数的参数,有⼏个就写⼏个上去,没有就不写。要想实现参数个数可变的函数,我们有两种⽅法:
第⼀种是类似于:function foo($arg1, $arg2 = 123, $arg3 = 'test', $arg4 = null, ....... ) {
//some code;
}
的办法,这种⽅法有两个缺点,第⼀个是你如果需要传100个参数难道就写100个参数吗?第⼆个是你还要在程序⾥判断哪个参数是不是null,或是其它默认值。(题外话:这种写法的参数默认值必须放在最后,你不能将没有默认值的参数插在有默认值的中间或前⾯,否则你调⽤的时候也必须显式地写上有默认值参数的值)
另⼀种实现参数数量可变的⽅法是在函数⾥⽤PHP的内置函数func_get_args(猛击这⾥看⼿册)取得传给函数的参数。类似的函数有
func_get_num和func_get_arg,算了,我懒,你们⾃⼰⼿册看吧。
那么,⽤这个函数似乎要⽅便很多,我们根据想象的函数参数的排列,代码应该是这个样⼦的:function newInstance() {
$arguments = func_get_args();
$className = array_shift($arguments);
$class = new ReflectionClass($className);
return $class->newInstanceArgs($arguments);
}
OK,让我们来看⼀下效果:$foo = newInstance('FOO');
$foo->test();
//输出结果:
//This is the method test of class FOO
$bar = newInstance('BAR', 3, 5);
$bar->test();
//输出结果:
//This is the method test of class BAR
//$this->a=3, $this->b=5
短短四⾏代码,效果相当完美啊。那么,如果应⽤到类⾥⾯,我们可以利⽤这种思想,直接写成魔术⽅法,可以让我们的类更酷哦!
class INSTANCE {
php如何运行代码function call($className, $arguments) {
$class = new ReflectionClass($className);
return $class->newInstanceArgs($arguments);
}
}
$inst = new INSTANCE();
$foo = $inst->foo();
$foo->test();
//输出结果:
//This is the method test of class FOO
$bar = $inst->bar('arg1', 'arg2');
$bar->test();
//输出结果:
//This is the method test of class BAR
//$this->a=3, $this->b=5
咔咔,爽吧。
接下来讨论⼀下不使⽤反射类的情况。例如PHP4中就没有反射,⽽⼀些⽼项⽬就是运⾏在PHP4上⾯的。或者是要保证项⽬对未知环境的兼容性,Whatever,来关⼼⼀下怎么动态传参吧。PHP中动态传参的函数只有⼀个:call_user_func_array(轻击此处查看⼿册)。这是⼀个动态调⽤函数的函数,作⽤是可以将函数的参数以数组的形式传递给要调⽤的函数。好吧,我⾃⼰也被⾃⼰绕晕了,直接来看实例:function foo($a, $b) {
echo '$a=', $a, '
';
echo '$b=', $b;
}
call_user_func_array('foo', array(1, 'string'));
//本例输出结果:
//$a=1
//$b=string
那么,要实现⽤这种⽅法来动态实例化对象并传参,呃……,只有曲线救国了,我们得先写⼀个函数,让这个函数来实例化对象,⽽这个函数的参数就原原本本地传给要实例化对象的类的构造函数就好了。打住!那这个函数得有⼏个参数啊?怎么实现传递不同个数的参数呢?嘿嘿,我⼀声冷笑,你忘了PHP⾥提供⼀个创建匿名函数的函数吗?(⼜开始绕起来了……)create_function(⼿册在此),照着⼿册⾥⾯的例⼦直接画就可以了,我也懒得打字了,直接看下⾯的代码,注释我写清楚点⼤家都明⽩了:function newInst() {
//取得所有参数
$arguments = func_get_args();
//弹出第⼀个参数,这是类名,剩下的都是要传给实例化类的构造函数的参数了
$className = array_shift($arguments);
//给所有的参数键值加个前缀
$keys = array_keys($arguments);
array_walk($keys, create_function('&$value, $key, $prefix', '$value = $prefix . $value;'), '$arg_');
//动态构造实例化类的函数,主要是动态构造参数的个数
$paramStr = implode(', ',$keys);
$newClass=create_function($paramStr, "return new {$className}({$paramStr});");
//实例化对象并返回
return call_user_func_array($newClass, $arguments);
}
好了,⾄于效果是什么,就⿇烦各位看官⾃⼰动动⼿,运⾏⼀下看看,是不是⾃⼰期望的结果。
如果改写成类⾥⾯的魔术⽅法,哼哼,威⼒嘛,你懂的!然后呢,我还是懒了,如果要写成魔术⽅法的话,相信这么easy的事情你很轻松就办到了。就当⼀个作业吧。另,本⽂的代码都是本⼈运⾏过的,但是,写⽂章的时候没有使⽤复制/粘贴功能,所以,你最好是也不要复制粘贴。如果从本⽂中的代码copy下来运⾏出错的话,还烦请各位⾃⼰debug⼀下,编程不就是要⾃⼰写么。
本来这个话题到这⾥就应该可以结束了,但是想到我在想到这个办法之前⽤的⽅法(好绕……),本着重在交流的态度,⼀起放出来。我例两个关键函数吧:extract和eval。只是我个⼈觉得⽤eval函数⽐较⼭寨,能不⽤最好不⽤。于是⼜想出了上⾯的办法,哈哈,编程最重要的是思想,不是吗?好,⼜是⼀道作业题了,⼤家可以试试⽤这两个函数(当然也会⽤到别的函数)来实现带不定数量的参数动态实例化对象的函数。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论