Php 调用\u user\u func\u数组,将参数传递给构造函数

Php 调用\u user\u func\u数组,将参数传递给构造函数,php,mysql,oop,constructor,Php,Mysql,Oop,Constructor,我在stackoverflow上搜索了很多谷歌搜索结果页面,但找不到适合我的解决方案。我试图构建的函数中似乎只有最后一个障碍,它使用call\u user\u func\u数组来动态创建对象 我得到的可捕获致命错误是类Product的对象无法转换为字符串。当错误发生时,在日志中我会得到其中五个(每个参数一个):PHP警告:产品缺少参数1::\uu construct(),,在可捕获的致命错误之前 这是函数的代码: public static function SelectAll($class,

我在stackoverflow上搜索了很多谷歌搜索结果页面,但找不到适合我的解决方案。我试图构建的函数中似乎只有最后一个障碍,它使用call\u user\u func\u数组来动态创建对象

我得到的可捕获致命错误是类Product的
对象无法转换为字符串。当错误发生时,在日志中我会得到其中五个(每个参数一个):
PHP警告:产品缺少参数1::\uu construct(),
,在可捕获的致命错误之前

这是函数的代码:

public static function SelectAll($class, $table, $sort_field, $sort_order = "ASC")
{  
/* First, the function performs a MySQL query using the provided arguments. */

$query = "SELECT * FROM " .$table. " ORDER BY " .$sort_field. " " .$sort_order;
$result = mysql_query($query);

/* Next, the function dynamically gathers the appropriate number and names of properties. */

$num_fields = mysql_num_fields($result);
for($i=0; $i < ($num_fields); $i++)
{
  $fetch = mysql_fetch_field($result, $i);
  $properties[$i] = $fetch->name;
}

/* Finally, the function produces and returns an array of constructed objects.*/

while($row = mysql_fetch_assoc($result))
{
  for($i=0; $i < ($num_fields); $i++)
  {
    $args[$i] = $row[$properties[$i]];
  }
  $array[] = call_user_func_array (new $class, $args);
}

return $array;
}
页面按其应该的方式加载,并填充我正在构建的表。因此,在我尝试在
call\u user\u func\u array
中实际使用我的
$args
数组之前,一切都是绝对正常的


调用该数组是否有一些我遗漏的微妙细节?我读过一次关于call_user_func_array的PHP手册,然后读了一些,该页面上的示例似乎向人们展示了如何构建一个数组,并在第二个参数中调用它。我可能做错了什么?

您不能像这样调用
$class
的构造函数:

call_user_func_array (new $class, $args);
这不是第一个参数。让我们把它分开:

call_user_func_array (new $class, $args);

$obj = new $class;
call_user_func_array ($obj, $args);
如您所见,
$class
的构造函数在
call\u user\u func\u array
生效之前已经被调用。由于没有参数,您会看到以下错误消息:

Missing argument 1 for Product::__construct()
Object of class Product could not be converted to string.
其次,
$obj
是object类型。有效的回调必须是字符串或数组(或者是非常特殊的对象:,但这不在讨论范围之内,我只是为了完整性而命名它)

由于
$obj
是一个对象,不是有效的回调,因此您会看到PHP错误消息:

Missing argument 1 for Product::__construct()
Object of class Product could not be converted to string.
PHP尝试将对象转换为字符串,这是不允许的

因此,正如您所看到的,您无法轻松地为构造函数创建回调,因为对象还不存在。也许这就是为什么你不能很容易地在手册中找到它

构造函数在这里需要一些特殊处理:如果需要将变量参数传递给尚未初始化对象的类构造函数,可以使用:

  $ref = new ReflectionClass($class);
  $new = $ref->newInstanceArgs($args);

请参见不可能使用
调用用户函数数组()
,因为(顾名思义)它调用函数/方法,但不用于创建对象,请使用
ReflectionClass

$refClass = new ReflectionClass($class);
$object = $refClass->newInstanceArgs($args);
另一个(更基于设计的)解决方案是静态工厂方法

class MyClass () {
  public static function create ($args) {
    return new self($args[0],$args[1],$args[2],$args[3],$args[4]);
  }
}
然后就

$object = $class::create($args);

在我看来,它更干净,因为更少的魔法和更多的控制

我将其用于单例工厂模式,因为ReflectionClass破坏了依赖树,我讨厌使用eval,但这是我发现的唯一简化使用singleton模式来注入mockObjects的方法,其中PHPUnit没有打开该注入的类方法,小心传递给eval函数的数据!!!!!!!!您必须确保已清洁和过滤

abstract class Singleton{
   private static $instance=array();//collection of singleton objects instances
   protected function __construct(){}//to allow call to extended constructor only from dependence tree
   private function __clone(){}//to disallow duplicate
   private function __wakeup(){}//comment this if you want to mock the object whith php unit jejeje

   //AND HERE WE GO!!!
   public static function getInstance(){        
    $a=get_called_class();
    if(!array_key_exists($a, self::$instance)){ 
        if(func_num_args()){
          /**HERE IS THE CODE **//
            $args=func_get_args();
            $str='self::$instance[$a]=new $a(';
            for($i=0;$i<count($args);$i++){
                $str.=(($i)?",":"").'$args['.$i.']';
            }
            eval($str.");");//DANGER, BE CAREFULLY...we only use this code to inject MockObjects in testing...to another use you will use a normal method to configure the SingletonObject
          /*--------------------------*/
        }else{
            self::$instance[$a]=new $a();
        }

    }
    return self::$instance[$a];     
}


}
要实例化对象,请执行以下操作:

$myVar= MyClass::getInstance($objetFromClassMyDependInjection);

它调用带有参数的构造函数。我知道扩展静态方法getInstance可以得到相同的结果,但通过这种方式进行团队合作更容易使用

谢谢你的回答,但是,我没有收到“无效回调”错误,错误日志似乎表明
调用用户函数数组
确实成功调用了该构造,因为它给了我一个
PHP警告:Product::\uu construct()
缺少参数1,对于特定构造函数期望的五个参数中的每一个,但由于某种原因没有收到。当然,这可能是我还不了解的东西,但这是我目前掌握的知识。您首先使用
new$class
实例化对象。这意味着构造函数在调用用户函数数组之前就已经被调用了。看到了吗?首先是
new
,因为您将其用作参数中的表达式,然后会发生函数调用。但是由于执行了new,已经调用了
$class
的构造函数(没有参数)。然后PHP尝试将对象转换为字符串,因为第一个参数需要字符串或数组。因为您的对象不支持对字符串进行强制转换,所以您会收到错误/警告。我明白了!非常感谢。我将研究动态地将参数传递给构造函数的其他方法。@tuespetre:请参阅答案中的代码示例(最后的两行代码),这是您可以做到的方法。我现在理解了ReflectionClass的这一点。非常感谢你的回答。我现在可以做我需要的!例如,您的意思是可以调用
Product::u-construct($args)
,其中
$args
是一个参数数组?所有函数都可以用这样的参数数组调用吗?我在哪里说过这样的话?!?当然,您可以使用数组实例化对象,但随后将在构造函数中以单个参数的形式接收该数组。看起来这不是你想要的。对不起,我看错了你的帖子ReflectionClass方法更干净,因为您的方法不会缩放到超过4个参数。不过,您的方法在执行时间上会更快。@demonkoryu有人可能会说,使用反射本身就是一种黑客行为,但您当然是对的,每次签名更改时都需要特殊的方法。然而,这种特殊的方法是如此普遍,以至于它们甚至有一个名字:工厂:)另一方面,答案是从2011年开始的,现在有了variadics,如果你认为这样做是一个好主意,那么创建一个通用工厂“东西”是微不足道的。