2015-04-22 17 views
5

Tôi có một đối tượng triển khai Iterator và chứa 2 mảng: "mục nhập" và "trang". Bất cứ khi nào tôi lặp qua đối tượng này, tôi muốn sửa đổi mảng mục nhưng tôi nhận được lỗi An iterator cannot be used with foreach by reference mà tôi thấy bắt đầu trong PHP 5.2.PHP Trình lặp không thể được sử dụng với foreach bằng cách tham chiếu

Câu hỏi của tôi là, làm cách nào để sử dụng lớp Iterator để thay đổi giá trị của đối tượng được lặp trong khi sử dụng foreach trên đó?

Mã của tôi:

//$flavors = instance of this class: 
class PaginatedResultSet implements \Iterator { 
    private $position = 0; 

    public $entries = array(); 
    public $pages = array(); 

    //...Iterator methods... 
} 

//looping 
//throws error here 
foreach ($flavors as &$flavor) { 
    $flavor = $flavor->stdClassForApi(); 
} 

Lý do cho điều này là đôi khi $flavors sẽ không là một một thể hiện của lớp học của tôi và thay vào đó sẽ chỉ là một mảng đơn giản. Tôi muốn có thể sửa đổi mảng này một cách dễ dàng bất kể loại đó là gì.

+0

tại sao không chỉ kiểm tra xem đó là một o lớp r một mảng đầu tiên và sau đó xử lý nó một cách thích hợp? – MuppetGrinder

+0

@MuppetGrinder Bởi vì tôi sử dụng những phương pháp này ở nhiều nơi và tôi không muốn phải kiểm tra mỗi lần, thay vì chỉ ở 1 nơi. – shiznatix

Trả lời

5

Tôi chỉ cố gắng tạo ra một iterator mà sử dụng:

public function &current() { 
    $element = &$this->array[$this->position]; 
    return $element; 
} 

Nhưng điều đó vẫn không làm việc.

Điều tốt nhất tôi có thể giới thiệu là bạn thực hiện \ArrayAccess, mà sẽ cho phép bạn làm điều này:

foreach ($flavors as $key => $flavor) { 
    $flavors[$key] = $flavor->stdClassForApi(); 
} 

Sử dụng máy phát điện:

Đang cập nhật dựa trên Marks bình luận về máy phát điện, ý chí sau cho phép bạn lặp qua các kết quả mà không cần triển khai \Iterator hoặc \ArrayAccess.

class PaginatedResultSet { 
    public $entries = array(); 

    public function &iterate() 
    { 
     foreach ($this->entries as &$v) { 
      yield $v; 
     } 
    } 
} 

$flavors = new PaginatedResultSet(/* args */); 

foreach ($flavors->iterate() as &$flavor) { 
    $flavor = $flavor->stdClassForApi(); 
} 

Đây là tính năng có sẵn trong PHP 5.5.

+0

Cũng có thể truy cập các phần tử của Máy phát điện bằng cách tham khảo, do đó có thể là một cách tiếp cận khác –

+0

Tôi đã kết thúc với việc triển khai cả \ ArrayAccess và \ Iterator và sau đó sử dụng $ key => $ val style thay vì tham chiếu . Điều này làm việc hoàn hảo. – shiznatix

+0

@MarkBaker Tôi vẫn cần phải xem xét cách làm việc đó, tôi hơi bối rối về ý nghĩa của chúng khi chúng nói trạng thái của hàm không thay đổi theo từng cuộc gọi. – Flosculus

0

Mở rộng khi giải pháp Flosculus, nếu bạn không muốn tham chiếu khóa mỗi khi bạn sử dụng biến lặp, bạn có thể gán tham chiếu cho biến đó thành biến mới trong dòng đầu tiên của foreach.

foreach ($flavors as $key => $f) { 
    $flavor = &$flavors[$key]; 
    $flavor = $flavor->stdClassForApi(); 
} 

Đây là chức năng giống với cách sử dụng phím trên các đối tượng cơ sở, nhưng giúp giữ mã gọn gàng, và tên biến ngắn ... Nếu bạn đang vào mà loại điều.

0

Nếu bạn thực hiện các chức năng lặp trong calss của bạn, tôi sẽ đề nghị thêm một phương pháp khác để lớp "setCurrent()":

//$flavors = instance of this class: 
class PaginatedResultSet implements \Iterator { 
    private $position = 0; 

    public $entries = array(); 
    public $pages = array(); 

    /* --- Iterator methods block --- */ 
    private $current; 

    public function setCurrent($value){ 
     $this->current = $value; 
    } 

    public function current(){ 
     return $this->current; 
    } 
    //...Other Iterator methods... 
} 

Sau đó, bạn chỉ có thể sử dụng chức năng này bên trong vòng lặp foreach:

foreach ($flavors as $flavor) { 
    $newFlavor = makeNewFlavorFromOldOne($flavor) 
    $flavors -> setCurrent($newFlavor); 
} 

Nếu bạn cần chức năng này trong các lớp khác, bạn cũng có thể định nghĩa một iterator mới và mở rộng giao diện iterator để chứa setCurrent()

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