有没有类似于call_user_func()的函数可以创建一个新的类实例?

21

我该如何创建一个可带入构造函数的指定参数数组的类?类似于:

class a {
    var $args = false;
    function a() {$this->args = func_get_args();}
}

$a = call_user_func_array('new a',array(1,2,3));
print_r($a->args);

理想情况下,这需要在不修改该类的情况下,在PHP4和PHP5中都能正常工作。有什么建议吗?


2
你为什么想要这样做?你试图解决什么问题? - Skilldrick
@Skilldrick:在我的情况下,我正在尝试实现一个简单的依赖注入容器。 - Frank Schwieterman
2个回答

25

ReflectionClass:newInstance()(或newInstanceArgs())可以让你做到这一点。

例如:

class Foo {
  public function __construct() {  
    $p = func_get_args();
    echo 'Foo::__construct(', join(',', $p), ') invoked';
  }
}

$rc = new ReflectionClass('Foo');
$foo = $rc->newInstanceArgs( array(1,2,3,4,5) );

编辑:没有使用ReflectionClass,并且可能兼容PHP4(抱歉,现在手头没有PHP4)

class Foo {
  public function __construct() {  
    $p = func_get_args();
    echo 'Foo::__construct(', join(',', $p), ') invoked';
  }
}

$class = 'Foo';
$rc = new $class(1,2,3,4);

速度比较: 既然这里提到了反射的速度,那我们进行一个小(合成)测试

define('ITERATIONS', 100000);

class Foo {
  protected $something;
  public function __construct() {
    $p = func_get_args();
    $this->something = 'Foo::__construct('.join(',', $p).')';
  }
}

$rcStatic=new ReflectionClass('Foo'); 
$fns = array(
  'direct new'=>function() { $obj = new Foo(1,2,3,4); },
  'indirect new'=>function() { $class='Foo'; $obj = new $class(1,2,3,4); }, 
  'reflection'=>function() { $rc=new ReflectionClass('Foo'); $obj = $rc->newInstanceArgs( array(1,2,3,4) ); },
  'reflection cached'=>function() use ($rcStatic) { $obj = $rcStatic->newInstanceArgs( array(1,2,3,4) ); },
);


sleep(1);
foreach($fns as $name=>$f) {
  $start = microtime(true);
  for($i=0; $i<ITERATIONS; $i++) {
    $f();
  }
  $end = microtime(true);
  echo $name, ': ', $end-$start, "\n";
  sleep(1);
}

这会在我的(不太快的)笔记本上打印出来

direct new: 0.71329689025879
indirect new: 0.75944685935974
reflection: 1.3510940074921
reflection cached: 1.0181720256805

这不是很糟糕,对吧?


反射仅在PHP5中受支持,但这是我所采用的方法。 - Percutio
我建议不要在生产代码中使用反射。它非常慢。 - Lucas Oman
很遗憾,它只支持PHP5,但还是谢谢。我可能会在PHP4中使用回退到eval语句的方法。对于我的用途来说,速度并不是太重要。 - Steve H
在使用eval()函数之前,你真的应该先用php4来测试一下新的$class(1,2,3)这个东西。我非常确定它会工作的。而且工厂模式是一个很棒的基础模式 - 绝对值得花时间去学习它;-) - VolkerK
2
使用 new $class($args) 方法有什么缺点吗? - Shawn

10

可以看一下工厂方法模式,并查看这个例子

维基百科上说:

工厂方法模式是一种面向对象的设计模式。像其他创建型模式一样,它处理创建对象(产品)而不指定将要创建的确切对象类的问题。

如果不想使用专用的工厂来实现这个功能,你仍然可以将Volker的代码包装成一个函数,例如:

/**
 * Creates a new object instance
 *
 * This method creates a new object instance from from the passed $className
 * and $arguments. The second param $arguments is optional.
 *
 * @param  String $className class to instantiate
 * @param  Array  $arguments arguments required by $className's constructor
 * @return Mixed  instance of $className
 */
function createInstance($className, array $arguments = array())
{
    if(class_exists($className)) {
        return call_user_func_array(array(
            new ReflectionClass($className), 'newInstance'), 
            $arguments);
    }
    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接