2011-11-05 81 views
10

Tôi đã tìm kiếm nhiều trang kết quả của Google cũng như ở đây trên stackoverflow nhưng không thể tìm thấy giải pháp có vẻ phù hợp với tình huống của tôi. Tôi xuất hiện để có nhưng một snag cuối cùng trong chức năng tôi đang cố gắng để xây dựng, trong đó sử dụng call_user_func_array để tự động tạo ra các đối tượng.call_user_func_array truyền đối số cho một hàm tạo

Lỗi nghiêm trọng gây tử vong mà tôi nhận được là Object of class Product could not be converted to string. Khi lỗi xảy ra, trong nhật ký tôi nhận được năm trong số này (một cho mỗi đối số): PHP Warning: Missing argument 1 for Product::__construct(), trước khi lỗi gây tử vong có thể bắt được.

Đây là mã của hàm:

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

Bây giờ, nếu tôi nhận xét ra dòng call_user_func_array và thay thế nó với điều này:

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

Các tải trang như mong muốn, và populates cái bàn tôi đang xây. Vì vậy, tất cả mọi thứ là hoàn toàn chức năng cho đến khi tôi cố gắng thực sự sử dụng mảng $args của mình trong phạm vi call_user_func_array.

Có một số chi tiết tinh tế về việc gọi mảng đó mà tôi đang thiếu không? Tôi đọc hướng dẫn PHP cho call_user_func_array một lần, và sau đó một số, và các ví dụ trên trang đó dường như cho thấy mọi người chỉ xây dựng một mảng và gọi nó cho đối số thứ hai. Tôi có thể làm gì sai?

Trả lời

20

Bạn không thể gọi constructor của $class như thế này:

call_user_func_array (new $class, $args); 

Đó là không valid callback như tham số đầu tiên. Hãy chọn này ngoài:

call_user_func_array (new $class, $args); 

là giống như

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

Như bạn có thể thấy, các nhà xây dựng của $class đã được đã gọi trước khi call_user_func_array đi vào hành động. Vì không có tham số, bạn thấy thông báo lỗi này:

Missing argument 1 for Product::__construct() 

Bên cạnh đó, $obj là đối tượng kiểu. Một cuộc gọi lại hợp lệ phải là một chuỗi hoặc một mảng (hoặc đặc biệt là một đối tượng rất đặc biệt: Closure, nhưng đó là thảo luận ở đây, tôi chỉ đặt tên cho nó để hoàn thành).

Như $obj là một đối tượng và không phải là một callback hợp lệ, vì vậy bạn thấy thông báo lỗi PHP:

Object of class Product could not be converted to string. 

PHP cố gắng để chuyển đổi đối tượng chuỗi, mà nó không cho phép.

Vì vậy, như bạn có thể thấy, bạn không thể dễ dàng tạo một cuộc gọi lại cho một hàm tạo, vì đối tượng chưa tồn tại. Có lẽ đó là lý do tại sao bạn không thể tìm kiếm nó trong sách hướng dẫn một cách dễ dàng.

Constructors cần một số giao dịch đặc biệt ở đây: Nếu bạn cần phải vượt qua đối số biến để một constructor lớp của một đối tượng không-chưa khởi tạo, bạn có thể sử dụng ReflectionClass để làm điều này:

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

Xem ReflectionClass::newInstanceArgs

+0

Cảm ơn bạn đã trả lời, tuy nhiên, tôi không nhận được lỗi 'không gọi lại hợp lệ' và lỗi log dường như chỉ ra rằng 'call_user_func_array' thực hiện thành công việc gọi cấu trúc, vì nó cho tôi một' PHP Warning: Thiếu đối số 1 cho Product :: __ construct() 'cho mỗi trong số năm đối số mà hàm tạo cụ thể mong đợi nhưng vì một số lý do nhận được. Tất nhiên, nó có thể là một cái gì đó tôi chưa hiểu, nhưng đó là kiến ​​thức tôi có cho đến nay. – tuespetre

+0

Bạn lần đầu tiên khởi tạo đối tượng với 'new $ class'. Điều đó có nghĩa là hàm tạo đã được gọi trước khi 'call_user_func_array' get vào hành động. Bạn thấy không? Đầu tiên là 'new' bởi vì bạn sử dụng nó như là một biểu thức trong tham số, sau đó gọi hàm sẽ xảy ra. Nhưng vì mới được thực hiện, hàm tạo của '$ class' đã được gọi (không có đối số). Và sau đó PHP cố gắng chuyển đổi đối tượng thành một chuỗi bởi vì tham số đầu tiên mong đợi một chuỗi hoặc mảng. Vì đối tượng của bạn không hỗ trợ truyền tới chuỗi, bạn nhận được lỗi/cảnh báo. – hakre

+0

Tôi hiểu! Cảm ơn bạn. Tôi sẽ xem xét các cách khác để tự động chuyển các đối số cho một hàm tạo. – tuespetre

2

không thể sử dụng call_user_func_array(), bởi vì (như tên gọi gợi ý) nó gọi chức năng/phương pháp, nhưng không có ý định để tạo các đối tượng, sử dụng ReflectionClass

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

Một (nhiều thiết kế dựa trên) Giải pháp là một phương pháp nhà máy tĩnh

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

và sau đó chỉ cần

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

Trong mắt tôi nó sạch hơn, vì ít ma thuật và kiểm soát nhiều

+0

Bạn có nghĩa là bạn có thể gọi, ví dụ 'Product :: __ construct ($ args)' trong đó '$ args' là một mảng đối số? Tất cả các hàm có thể được gọi bằng cách sử dụng một mảng đối số như vậy không? – tuespetre

+0

Tôi đã nói điều gì đó như thế?!? Tất nhiên bạn có thể instanciate đối tượng với mảng, nhưng sau đó bạn sẽ nhận được mảng đó như là đối số duy nhất trong constructor.Nó không giống như những gì bạn muốn – KingCrunch

+0

Xin lỗi, tôi đã đọc sai bài đăng của bạn. :) – tuespetre

0

tôi sử dụng này cho mô hình nhà máy singleton, bởi vì ReflectionClass brokes cây phụ thuộc, tôi ghét việc sử dụng eval nhưng cách duy nhất của nó để tôi tìm cách đơn giản hóa việc sử dụng mẫu đơn để tiêm mockObjects whith PHPUnit whi tout mở các phương pháp lớp học để tiêm đó, BE CAREFULL KHI DỮ LIỆU NHỮNG GÌ BẠN PASS TO eval FUNCTION !!!!!!!! BẠN PHẢI ĐƯỢC CHỊU R IS RÀNG R IS RÀNG VÀ LỌC!

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


} 

Và để sử dụng:

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

để instanciate đối tượng:

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

nó gọi các nhà xây dựng whith args tôi pased. tôi biết rằng tôi có thể nhận được cùng một kết quả mở rộng phương thức tĩnh getInstance nhưng để làm việc theo nhóm dễ sử dụng hơn theo cách này

Các vấn đề liên quan