2011-11-05 29 views
10

我已经搜索了很多谷歌搜索结果的页面,以及这里的stackoverflow,但无法找到似乎适合我的情况的解决方案。我似乎只有一个最后的障碍,我试图建立的函数,它使用call_user_func_array来动态创建对象。call_user_func_array将参数传递给构造函数

我收到的可捕捉的致命错误是Object of class Product could not be converted to string。发生错误时,在日志中,我会得到其中的五个(每个参数一个):PHP Warning: Missing argument 1 for Product::__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_user_func_array线以及与此替换:

$array[] = new $class($args[0],$args[1],$args[2],$args[3],$args[4]); 

该页面加载,因为它应该,并填充我正在建造的桌子。所以一切都是绝对有效的,直到我尝试在call_user_func_array内实际使用我的$args阵列。

有没有关于调用我缺少的数组的一些细微的细节?我一次阅读了call_user_func_array的PHP手册,然后看了一些,并且该页面上的示例似乎向人们展示了创建一个数组并将其称为第二个参数。我可能做错了什么?

回答

20

你不能调用$class的构造是这样的:

call_user_func_array (new $class, $args); 

这可不是valid callback作为第一个参数。让我们来挑这个开:

call_user_func_array (new $class, $args); 

相同

$obj = new $class; 
call_user_func_array ($obj, $args); 

正如你所看到的,$class构造已经被称为call_user_func_array进入行动之前。由于它没有参数,您看到此错误消息:

Missing argument 1 for Product::__construct() 

旁边的是,$obj是对象类型。一个有效的回调必须是一个字符串或一个数组(或特殊的非常特殊的对象:Closure,但这里没有讨论,我只是将它命名为完整性)。

由于$obj是一个对象,而不是一个有效的回调,所以你看到了PHP错误消息:

Object of class Product could not be converted to string. 

PHP会尝试将对象转换为字符串,它不允许。

所以,你可以看到,你不能轻易地创建一个构造函数的回调,因为对象还不存在。也许这就是为什么你无法轻松查看手册的原因。

建设者在这里需要一些特殊处理:如果您需要变量参数传递给尚未初始化对象的类的构造函数,你可以使用ReflectionClass做到这一点:

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

ReflectionClass::newInstanceArgs

+0

谢谢你的回答,但是,我没有得到'无效回调'错误,并出现错误日志似乎表明'call_user_func_array'确实调用了构造函数,因为它给了我一个'PHP Warning:对于Product :: __ construct()'缺少参数1“,这是特定构造函数期望的五个参数中的每一个参数,但由于某种原因,接收。当然,这可能是我还不明白的,但那是我迄今所掌握的知识。 – tuespetre

+0

您首先使用'new $ class'实例化对象。这意味着构造函数在'call_user_func_array'开始执行之前已经被调用。你看?首先来'new',因为你将它用作参数中的表达式,然后发生函数调用。但是由于正在执行的新元素,'$ class'的构造函数已经被调用(没有参数)。然后PHP尝试将该对象转换为字符串,因为第一个参数需要一个字符串或数组。因为你的对象不支持强制转换为字符串,你会得到错误/警告。 – hakre

+0

我明白了!谢谢。我将研究将参数动态传递给构造函数的其他方法。 – tuespetre

2

使用call_user_func_array(),因为(正如其名字暗示的)它调用函数/方法,但不打算创建对象不可能的,使用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); 

在我眼里它是清洁,因为更少的魔术和更多的控制

+0

你的意思是你可以调用,例如'Product :: __ construct($ args)',其中'$ args'是一个参数数组?所有的函数都可以使用这样的参数数组来调用吗? – tuespetre

+0

我在哪儿说过这样的话?!?当然,你可以用数组实例化对象,但是你会在构造函数中接受该数组作为单个参数。它看起来不像那个你想要的 – KingCrunch

+0

对不起,我误解了你的帖子。 :) – tuespetre

0

我使用这个单身工厂模式,因为ReflectionClass中断了依赖树,我讨厌使用eval,但它的唯一方法是我发现简化单例模式的使用来注入mockObjects,PHPUnit whi打开类注入的方法,请仔细考虑数据通过什么来评估函数!!!!!!!!你必须确保它被清洗和过滤!

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];  
} 


} 

而且使用的是:

class MyClass extends Singleton{ 
    protected function __construct(MyDependInjection $injection){ 
     //here i use the args like a normal class but the method IS PROTECTED!!! 
    } 
} 

来实例化对象:

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

它调用构造蒙山我pased的ARGS。我知道我可以得到相同的结果扩展静态方法getInstance,但团队工作更容易使用这种方式

相关问题