Hướng dẫn php foreach reference or copy - php foreach tham khảo hoặc sao chép

Tôi đã phải dành một vài giờ để tìm ra lý do tại sao A [3] đang thay đổi trên mỗi lần lặp. Đây là lời giải thích mà tôi đã đến.

Có hai loại biến trong PHP: các biến bình thường và biến tham chiếu. Nếu chúng ta gán một tham chiếu của một biến cho một biến khác, biến trở thành biến tham chiếu.

ví dụ trong

$a = array('zero', 'one', 'two', 'three');

nếu chúng ta làm

$v = &$a[0]

Phần tử 0 (

$v = &$a[0]
3) trở thành một biến tham chiếu.
$v = &$a[0]
4 điểm về phía biến đó; Do đó, nếu chúng ta thực hiện bất kỳ thay đổi nào đối với
$v = &$a[0]
4, nó sẽ được phản ánh trong
$v = &$a[0]
3 và ngược lại.

Bây giờ nếu chúng ta làm

$v = &$a[1]

$v = &$a[0]
7 sẽ trở thành một biến tham chiếu và
$v = &$a[0]
3 sẽ trở thành một biến bình thường (vì không ai khác chỉ vào
$v = &$a[0]
3, nó được chuyển đổi thành một biến bình thường. PHP đủ thông minh để biến nó thành một biến bình thường khi không ai khác hướng về phía nó)

Đây là những gì xảy ra trong vòng lặp đầu tiên

foreach ($a as &$v) {

}

Sau lần lặp cuối cùng

$v = &$a[1]
0 là một biến tham chiếu.

$v = &$a[0]
4 đang chỉ ra
$v = &$a[1]
0 bất kỳ thay đổi nào đối với
$v = &$a[0]
4 dẫn đến thay đổi thành
$v = &$a[1]
0

Trong vòng lặp thứ hai,

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}

Trong mỗi lần lặp khi

$v = &$a[0]
4 thay đổi,
$v = &$a[1]
0 thay đổi. (Bởi vì
$v = &$a[0]
4 vẫn chỉ vào
$v = &$a[1]
0). Đây là lý do tại sao
$v = &$a[1]
0 thay đổi trên mỗi lần lặp.

Trong lần lặp trước lần lặp cuối cùng,

$v = &$a[0]
4 được gán giá trị 'hai'. Vì
$v = &$a[0]
4 điểm đến
$v = &$a[1]
0,
$v = &$a[1]
0 hiện có được giá trị 'hai'. Giữ nó trong tâm trí.

Trong lần lặp cuối cùng,

$v = &$a[0]
4 (chỉ vào
$v = &$a[1]
0) hiện có giá trị là 'hai', bởi vì
$v = &$a[1]
0 đã được đặt thành hai trong lần lặp trước đó.
foreach ($a as &$v) {

}
7 được in. Điều này giải thích tại sao 'hai' được lặp lại khi $ v được in trong lần lặp cuối cùng.

Đây là một ví dụ tại sao tham chiếu từng lần trong các vòng lặp foreach là xấu.

$data = array(1,2,3,4);
foreach($data as &$entry) {
    // $entry = ...
}

echo '$data = ';
print_r($data);


/* later in your code, totally unrelated */

$test = array(10,20,30,40);
foreach($test AS $entry) {
    //
}

echo '$data = ';
print_r($data);

/**
 * Expected: array(1,2,3,4)
 * Actual: array(1,2,3,40)
 **/

Điều này là do khi vòng lặp thứ hai thực thi,

foreach ($a as &$v) {

}
8 vẫn là một tài liệu tham khảo. Do đó, với mỗi lần lặp, tham chiếu ban đầu được ghi đè.
Thus, with each iteration the original reference is overwritten.

Bạn có thể sửa nó bằng cách gọi

unset($entry);

sau vòng lặp qua từng tham chiếu. Vì vậy, nếu bạn đã từng sử dụng kiểu mã hóa này, đừng quên bỏ lại biến tham chiếu sau vòng lặp.don't forget to unset the reference variable after the loop.

Tuy nhiên, tôi nghĩ tốt nhất là tránh hoàn toàn cạm bẫy này và chỉ viết

foreach ($a as &$v) {

}
9 vòng lặp phải thao tác với mảng gốc theo cách thông thường:

foreach($data as $index => $entry) {
    // $data[$index] = ...
}

«Quay lại tổng quan về bài viết. 11. Tháng 11 năm 2011

CẬP NHẬT (2017-24-12): Bài viết này mô tả hành vi sao chép của

foreach ($a as &$v) {

}
9 trong PHP 5. Một mô tả tốt hơn về hành vi foreach tổng thể và lý do đằng sau nó, bao gồm cả Php 5 và Php 7, có sẵn trong câu trả lời StackOverflow này. (2017-24-12): This article describes the copying behavior of
foreach ($a as &$v) {

}
9 in PHP 5. A better description of the overall foreach behavior and the reasons behind it, covering both PHP 5 and PHP 7, is available in this StackOverflow answer.

Bài đăng này đòi hỏi một số kiến ​​thức về hoạt động nội bộ của PHP, cụ thể là

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
1S,
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
2 và hành vi sao chép trên các tác phẩm. Nếu những điều khoản đó không có ý nghĩa với bạn, bạn sẽ cần phải đọc chúng trong Oder để hiểu bài đăng này. Tôi muốn giới thiệu một bài viết của Sara Golemon.

PHP từ

foreach ($a as &$v) {

}
9 là một cấu trúc ngôn ngữ rất gọn gàng và điểm. Vẫn còn một số người không thích sử dụng nó, vì họ nghĩ rằng nó chậm. Một lý do thường được đặt tên là
foreach ($a as &$v) {

}
9 sao chép mảng nó lặp lại. Do đó, một số người khuyên bạn nên viết:

$keys = array_keys($array);
$size = count($array);
for ($i = 0; $i < $size; $i++) {
    $key   = $keys[$i];
    $value = $array[$key];

    // ...
}

Thay vì trực quan và đơn giản hơn nhiều:

foreach ($array as $key => $value) {
    // ...
}

Có hai vấn đề này:

  1. Microoptimization là xấu xa. Thông thường nó chỉ lãng phí thời gian của bạn và không cung cấp cho bất kỳ cải thiện hiệu suất có thể đo lường được.
  2. Hành vi sao chép của
    foreach ($a as &$v) {
    
    }
    
    9 có phần phức tạp hơn hầu hết mọi người nghĩ. Thường thì biến thể của Tối ưu hóa trên mạng có thể chậm hơn so với bản gốc.

Khi nào bản sao foreach?

Việc

foreach ($a as &$v) {

}
9 có sao chép mảng hay không và bao nhiêu phụ thuộc vào ba điều: liệu mảng lặp có được tham chiếu hay không,
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
2 của nó cao như thế nào và liệu lần lặp có được thực hiện hay không.

Không được tham chiếu, refCount == 1

Trong mã dưới đây

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 không được tham chiếu và có số tiền rebount là
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
9. Trong trường hợp này
foreach ($a as &$v) {

}
9 sẽ không sao chép mảng (bằng chứng) - trái với niềm tin phổ biến rằng
foreach ($a as &$v) {

}
9 luôn sao chép mảng lặp nếu nó được tham chiếu.not copy the array (proof) - contrary to the popular belief that
foreach ($a as &$v) {

}
9 always copies the iterated array if it isn’t referenced.

$v = &$a[0]
0

Lý do rất đơn giản: tại sao nó nên? Điều duy nhất mà

foreach ($a as &$v) {

}
9 sửa đổi về
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 là con trỏ mảng nội bộ. Đây là hành vi mong đợi và do đó không cần phải ngăn chặn.

Không được tham chiếu, refcount> 1

Mã sau đây trông rất giống với mã trước. Sự khác biệt duy nhất là mảng hiện được thông qua như một đối số. Điều này có vẻ như là một sự khác biệt không đáng kể, nhưng nó thay đổi hành vi của

foreach ($a as &$v) {

}
9: Bây giờ nó sẽ sao chép cấu trúc mảng, nhưng không phải là các giá trị (bằng chứng; nếu bạn muốn thấy rằng đây thực sự chỉ là cấu trúc được sao chép so sánh điều này và tập lệnh đó . Chỉ thứ nhất sao chép cấu trúc, bản sao thứ hai).will copy the array structure, but not the values (proof; if you want to see that this is really only the structure being copied compare this and that script. The first only copies the structure, the second copies both).

$v = &$a[0]
1

Điều này có vẻ kỳ lạ lúc đầu: tại sao nó sẽ sao chép khi mảng được chuyển qua một đối số, nhưng không phải nếu nó được xác định trong hàm? Lý do là mảng

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
1 hiện được chia sẻ giữa nhiều biến: biến
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 bên ngoài hàm và biến
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 bên trong nó. Nếu
foreach ($a as &$v) {

}
9 sẽ lặp lại mảng mà không sao chép cấu trúc của nó, nó sẽ không chỉ thay đổi con trỏ mảng của biến
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 trong hàm, mà còn cả con trỏ của biến
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 bên ngoài hàm. Do đó,
foreach ($a as &$v) {

}
9 cần sao chép cấu trúc mảng (tức là bảng băm). Mặt khác, các giá trị vẫn có thể chia sẻ zvals và do đó don don cần được sao chép.

Tham chiếu

Trường hợp tiếp theo rất giống với trường hợp trước. Sự khác biệt duy nhất là mảng được truyền qua tham chiếu. Trong trường hợp này, mảng một lần nữa sẽ không được sao chép (bằng chứng).not be copied (proof).

$v = &$a[0]
2

Trong trường hợp này, lý do tương tự được áp dụng như với trường hợp trước: bên ngoài

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 và bên trong
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 chia sẻ
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
1S. Sự khác biệt là bây giờ chúng là tài liệu tham khảo (
unset($entry);
5). Do đó, trong trường hợp này, người ta dự đoán rằng bất kỳ thay đổi nào đối với mảng bên trong cũng sẽ được thực hiện cho mảng bên ngoài. Vì vậy, nếu con trỏ mảng của mảng bên trong được thay đổi, con trỏ mảng của mảng ngoài cũng sẽ thay đổi. Đó là lý do tại sao
foreach ($a as &$v) {

}
9 không cần phải sao chép.

Lặp lại bằng tài liệu tham khảo

Các ví dụ trên là tất cả lặp đi lặp lại theo giá trị. Đối với phép lặp bằng cách tham chiếu cùng các quy tắc được áp dụng, nhưng tham chiếu giá trị bổ sung thay đổi hành vi sao chép của các giá trị mảng (hành vi về sao chép cấu trúc vẫn ở lại).

Trường hợp không được tham chiếu, refcount == 1 không thay đổi. Bằng cách lặp tham chiếu có nghĩa là chúng tôi muốn thay đổi mảng gốc nếu có bất kỳ thay đổi nào đối với

unset($entry);
7, do đó, mảng được sao chép (bằng chứng).

Vụ án được tham chiếu, cũng có thể giữ nguyên, như trong trường hợp này, một thay đổi thành

unset($entry);
7 sẽ thay đổi tất cả các biến tham chiếu mảng lặp (bằng chứng).

Chỉ có các trường hợp không được tham chiếu, giới thiệu> 1 trường hợp thay đổi, vì bây giờ cả cấu trúc mảng và giá trị của nó được sao chép được sao chép. Cấu trúc mảng vì nếu không thì con trỏ mảng của biến

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 bên ngoài hàm sẽ thay đổi và các giá trị vì thay đổi thành
unset($entry);
7 cũng sẽ thay đổi các giá trị
foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
8 bên ngoài (bằng chứng).

Bản tóm tắt

Để tóm tắt:

  • foreach ($a as &$v) {
    
    }
    
    9 sẽ sao chép cấu trúc mảng nếu và chỉ khi mảng lặp không được tham chiếu và có
    foreach($data as $index => $entry) {
        // $data[$index] = ...
    }
    3
  • foreach ($a as &$v) {
    
    }
    
    9 sẽ sao chép thêm các giá trị mảng nếu và chỉ khi điểm trước đó được áp dụng và lần lặp được thực hiện bằng cách tham chiếu

Có phải foreach tạo một bản sao?

Bản tóm tắt.Tóm lại: Foreach sẽ sao chép cấu trúc mảng nếu và chỉ khi mảng lặp không được tham chiếu và có một bản tóm tắt> 1. Foreach sẽ sao chép thêm các giá trị mảng nếu và chỉ khi điểm trước được áp dụng và lần lặp được thực hiện bằng tham chiếu.foreach will copy the array structure if and only if the iterated array is not referenced and has a refcount > 1. foreach will additionally copy the array values if and only if the previous point applies and the iteration is done by reference.

Foreach có nhanh hơn so với PHP không?

Tóm lại: Foreach nhanh hơn ForEach với tài liệu tham khảo.foreach is faster than foreach with reference.

Foreach () trong PHP là gì?

Foreach là gì trong PHP?Phương thức foreach () được sử dụng để lặp qua các phần tử trong một mảng được lập chỉ mục hoặc kết hợp.Nó cũng có thể được sử dụng để lặp lại trên các đối tượng.Điều này cho phép bạn chạy các khối mã cho mỗi phần tử.used to loop through the elements in an indexed or associative array. It can also be used to iterate over objects. This allows you to run blocks of code for each element.

Liệu foreach mảng đột biến?

foreach () không làm biến đổi mảng mà nó được gọi, nhưng hàm được cung cấp như CallbackFn có thể., but the function provided as callbackFn can.