PHP创建新对象时传递参数,使用call_user_func_array调用对象

23
我可以动态创建一个PHP对象,参数是可选的。
例如,不用这样做:
$test = new Obj($param);

我想做类似这样的事情(创建一个虚构的新对象):

$test = create_new_obj('Obj', $param);

PHP中是否有这样的函数?类似于call_user_func_array,但是用于对象。


为了更加明确,类名是可变的(它会改变),而$param取决于加载的类。有时候没有参数。基本上是根据情况动态加载各种类。 - Patrick
我认为下面的答案仍然符合你所描述的要求(变量类名,可选构造参数)。如果构造函数不需要该参数,那么你可以传递一个空的 $param。另一方面,如果你说创建的对象的类型实际上取决于 $param,那么你应该考虑使用工厂模式:http://en.wikipedia.org/wiki/Factory_method_pattern。在这种情况下,PHP无法处理你的构造决策...你需要编写逻辑来决定构建什么。 - zombat
6个回答

28

从 PHP 5.6 开始,你现在可以使用新的参数展开运算符(...)一行代码实现这个功能。

以下是一个简单的示例。

$className='Foo';
$args=['arg1','arg2','arg3'];

$newClassInstance=new $className(...$args);

更多信息请参见PHP变长参数列表


$db = (new ReflectionClass('Foo'))->newInstanceArgs($args); - hanshenrik

18

由于某些构造函数可能需要可变数量的参数,因此应使用以下方法来适应它。

$r = new ReflectionClass($strClassName);
$myInstance = $r->newInstanceArgs($arrayOfConstructorArgs);
例如,如果您的Car构造函数需要3个参数。
$carObj = new Car($color, $engine, $wheels);

然后

$strClassName = 'Car';
$arrayOfConstructorArgs = array($color, $engine, $wheels);
$r = new ReflectionClass($strClassName);
$carObj = $r->newInstanceArgs($arrayOfConstructorArgs);

http://php.net/manual/zh/class.reflectionclass.php
http://php.net/manual/zh/reflectionclass.newinstanceargs.php


反射是昂贵的。你最好使用 func_get_args() 来完成这个任务。 - zombat
4
在我的测试中,使用 PHP 反射并不比使用 func_get_args() 更慢。另外,您无需更改类的代码。 - VolkerK

7
在这种情况下,我会使用工厂方法。 它们可以在抽象类中轻松定义:
class Foobar {

    public function __construct($foo, $bar) 
    {
        // do something
    }

    static public function factory($foo, $bar) 
    {
        return new self($foo, $bar);
    }
}

使用这个函数可以调用call_user_func_array()

$my_foobar_obj = call_user_func_array('Foobar::factory', array($foo, $bar));

6
只要你知道类名,就可以动态创建对象:
$objName = 'myClass';
$test = new $objName($param);

如果您的构建逻辑需要,您可以轻松定义一个__construct()函数来接受默认参数

[编辑说明]: 这是一个称为可变变量的概念,在手册中有一些介绍new命令的示例。


4

这是您需要的干净版本:

class ClassName {
    public static function init(){       
        return (new ReflectionClass(get_called_class()))->newInstanceArgs(func_get_args());        
    }

    public static function initArray($array=[]){       
        return (new ReflectionClass(get_called_class()))->newInstanceArgs($array);        
    }

    public function __construct($arg1, $arg2, $arg3){
        ///construction code
    } 
}

使用new创建新对象实例的常规且不美观的方法

$obj = new ClassName('arg1', 'arg2', 'arg3');
echo $obj->method1()->method2();

使用init而不是new进行静态调用
echo ClassName::init('arg1', 'arg2', 'arg3')->method1()->method2();

使用initArray而不是new进行静态调用
echo ClassName::initArray(['arg1', 'arg2', 'arg3'])->method1()->method2();

0

基于 @chris 的回答(https://dev59.com/N3E85IYBdhLWcg3w6oB0#2550465),这里是反射类的使用方法:

abstract class A{
    // the constructor writes out the given parameters
    public function __construct(){
        var_dump(func_get_args());
    }

    public function copy(){
        // find our current class' name, __CLASS__ would return A
        $thisClass = get_class($this);
        $tmp = new ReflectionClass($thisClass);
        // pass all the parameters recieved to the new object
        $copy = $tmp->newInstanceArgs(func_get_args());

        return $copy;
    }
}

class B extends A{}

// create a new B object, with no parameters
$b = new B();

// create another b, but with other parameters
$c = $b->copy('the parameter of the copied B');

如果你想在一个祖先类中创建一个对象复制函数,但不确定子类将来是否需要参数,那么这就非常有用。


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