Đối số không hợp lệ được cung cấp cho foreach ()


304

Tôi thường xử lý dữ liệu có thể là một mảng hoặc một biến null và để cung cấp một số foreachdữ liệu với các dữ liệu này.

$values = get_values();

foreach ($values as $value){
  ...
}

Khi bạn cung cấp một foreach với dữ liệu không phải là một mảng, bạn sẽ nhận được một cảnh báo:

Cảnh báo: Đối số không hợp lệ được cung cấp cho foreach () trong [...]

Giả sử không thể cấu trúc lại get_values()hàm để luôn trả về một mảng (khả năng tương thích ngược, không có mã nguồn, bất kỳ lý do nào khác), tôi tự hỏi đâu là cách sạch nhất và hiệu quả nhất để tránh những cảnh báo này:

  • Đúc $valuesthành mảng
  • Đang khởi tạo $valuesmảng
  • Bao bọc foreachvới mộtif
  • Khác (xin đề nghị)

Rất có thể đó $valueskhông phải là một mảng.
Bhargav Nanekalva

Câu trả lời:


509

Cá nhân tôi thấy điều này là sạch sẽ nhất - không chắc nó có hiệu quả nhất không, tâm trí!

if (is_array($values) || is_object($values))
{
    foreach ($values as $value)
    {
        ...
    }
}

Lý do cho sở thích của tôi là nó không phân bổ một mảng trống khi bạn không có gì để bắt đầu.


4
Hoặc sử dụng Count () để tìm hiểu xem mảng có trống không
Kemo

76
@Kemo: count()không đáng tin cậy. Nếu bạn truyền count()null, nó trả về 0. Nếu bạn truyền cho nó một đối số không phải là null, nó sẽ trả về 1. Do đó, không thể sử dụng count()để xác định xem biến đó có phải là một mảng không khi biến đó có thể là một mảng trống hay một mảng chứa 1 mục.
Andy Shellam

12
Lưu ý rằng một số đối tượng có thể lặp lại và câu trả lời này không giải thích cho những đối tượng đó.
Brad Koch

32
Nên if (is_array($values) || $values instanceof Traversable).
Bob Stein

3
Thật xấu hổ khi anh không tiếp tục nói rằng nó không chắc chắn là hiệu quả nhất, mặc dù: D
Gui Prá

116

Cái này thì sao? sạch hơn rất nhiều và tất cả trong một dòng duy nhất.

foreach ((array) $items as $item) {
 // ...
 }

7
Đây là điều duy nhất làm việc cho tôi. Vì một số lý do, PHP đã không tin rằng mảng đa chiều mà tôi xây dựng thực sự là một mảng các mảng.
Justin

1
Tương tự ở đây, đây là một sửa chữa rất tốt cho một mảng chứa các mảng hoặc giá trị null. Chỉ cần thêm một bài kiểm tra trong vòng lặp foreach để tiếp tục nếu dữ liệu là null.
Lizardx

Giải quyết vấn đề của tôi. Cảm ơn!
Hitesh

1
Mã rực rỡ, bỏ qua if, khác cho mảng và giá trị không mảng bằng cách sử dụng $ _POST với hộp kiểm!
Yann Chabot

2
LƯU Ý: Mặc dù tìm kiếm đẹp và giải quyết cảnh báo foreach không hợp lệ, phương thức này sẽ trả về một cảnh báo biến không xác định nếu biến không được đặt theo bất kỳ cách nào. Sử dụng isset()hoặc is_array()hoặc cả hai, hoàn toàn tùy thuộc vào kịch bản của bạn, v.v.
James

42

Tôi thường sử dụng một cấu trúc tương tự như thế này:

/**
 * Determine if a variable is iterable. i.e. can be used to loop over.
 *
 * @return bool
 */
function is_iterable($var)
{
    return $var !== null 
        && (is_array($var) 
            || $var instanceof Traversable 
            || $var instanceof Iterator 
            || $var instanceof IteratorAggregate
            );
}

$values = get_values();

if (is_iterable($values))
{
    foreach ($values as $value)
    {
        // do stuff...
    }
}

Lưu ý rằng phiên bản cụ thể này không được kiểm tra, nó được nhập trực tiếp vào SO từ bộ nhớ.

Chỉnh sửa: thêm kiểm tra Traversable


3
Câu trả lời tốt nhất. Ngoại trừ tôi nghĩ bạn nên thực sự kiểm tra nếu $var instanceof Traversable. Xem tại đây . Bởi vì, ví dụ, bạn có thể đưa ra một SimpleXMLE bổ sung , nhưng nó không phải là một thể hiện của Iterator hoặc IteratorAggregate.
Bob Stein

2
Bạn có thể xóa hai lớp còn lại, @Kris. Cả hai đều mở rộng Traversable bây giờ và dường như đã được sinh ra theo cách đó trong 5.0.0. Mặc dù tôi cảm thấy một nghi ngờ nhỏ về việc liệu instanceof luôn được áp dụng để mở rộng.
Bob Stein

1
@ BobStein-VisiBone: có (ngoại trừ chúng là giao diện, không phải lớp) Tuy nhiên; Tôi đặt Traversable trước những cái đó, cả Iterator và IteratorAggregate đều không cần xác minh (theo cách này chúng sẽ không làm chậm quá trình thực thi). Tôi để chúng lại để giữ câu trả lời gần nhất có thể với câu trả lời ban đầu tôi đã đưa ra và để nó rõ ràng / dễ đọc.
Kris

2
Tôi nghĩ sẽ công bằng khi thêm is_object($var)lại. php.net/manual/en/lingu.oop5.iterations.php
Mark Fox

1
@MarkFox: Hãy thoải mái, tuy nhiên tôi cố tình bỏ nó đi; Tôi chưa bao giờ thấy việc sử dụng nó không được phục vụ tốt hơn bằng cách thực hiện Iteratorhoặc IteratorAggregate, nhưng đó tất nhiên chỉ là ý kiến ​​của tôi và do đó chủ quan (tôi không bao giờ sử dụng các lĩnh vực công cộng).
Kris

15

Xin đừng phụ thuộc vào việc truyền dưới dạng một giải pháp , mặc dù những người khác đang đề xuất đây là một tùy chọn hợp lệ để ngăn ngừa lỗi, nó có thể gây ra một lỗi khác.

Lưu ý: Nếu bạn mong đợi một dạng mảng cụ thể được trả về, điều này có thể làm bạn thất vọng. Cần thêm kiểm tra cho điều đó.

Ví dụ, truyền boolean vào một mảng (array)bool, sẽ KHÔNG dẫn đến một mảng trống, mà là một mảng có một phần tử chứa giá trị boolean là int: [0=>0]hoặc [0=>1].

Tôi đã viết một bài kiểm tra nhanh để trình bày vấn đề này . (Đây là Kiểm tra sao lưu trong trường hợp url kiểm tra đầu tiên không thành công.)

Bao gồm các bài kiểm tra cho: null, false, true, một class, một arrayundefined.


Luôn luôn kiểm tra đầu vào của bạn trước khi sử dụng nó trong foreach. Gợi ý:

  1. Kiểm tra loại nhanh :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Nhập mảng gợi ý trong các phương thức trước khi sử dụng foreach và chỉ định kiểu trả về
  3. Gói foreach trong nếu
  4. Sử dụng try{}catch(){}khối
  5. Thiết kế mã / kiểm tra thích hợp trước khi phát hành sản xuất
  6. Để kiểm tra một mảng dựa trên hình thức phù hợp, bạn có thể sử dụng array_key_existstrên một khóa cụ thể hoặc kiểm tra độ sâu của một mảng (khi đó là một mảng!) .
  7. Luôn trích xuất các phương thức trợ giúp của bạn vào không gian tên toàn cầu để giảm mã trùng lặp

8

Thử cái này:

//Force array
$dataArr = is_array($dataArr) ? $dataArr : array($dataArr);
foreach ($dataArr as $val) {
  echo $val;
}

;)


1
Điều này sẽ không hoạt động tốt với các mảng kết hợp .. Phương pháp is_array nhìn chung tốt hơn ... và dễ dàng hơn ...
AO_

4
$values = get_values();

foreach ((array) $values as $value){
  ...
}

Vấn đề luôn luôn là null và Casting thực tế là giải pháp làm sạch.


3

Trước hết, mọi biến phải được khởi tạo. Luôn luôn.
Đúc không phải là một lựa chọn.
nếu get_values ​​(); có thể trả về biến loại khác nhau, tất nhiên giá trị này phải được kiểm tra.


Truyền là một tùy chọn - nếu bạn khởi tạo một mảng bằng cách sử dụng, $array = (array)null;bạn nhận được một mảng trống. Tất nhiên, đó là sự lãng phí phân bổ bộ nhớ ;-)
Andy Shellam

2
+1: đọc từ quan điểm tình cảm, tôi không quan tâm nếu ngôn ngữ có thể làm mà không có, các biến PHẢI được khai báo và kết quả không đáng tin cậy PHẢI được kiểm tra. Cần phải giữ (các) nhà phát triển lành mạnh và các bản ghi lỗi ngắn.
Kris

3

Phần mở rộng ngắn gọn hơn của mã @ Kris

function secure_iterable($var)
{
    return is_iterable($var) ? $var : array();
}

foreach (secure_iterable($values) as $value)
{
     //do stuff...
}

đặc biệt là sử dụng mã mẫu bên trong

<?php foreach (secure_iterable($values) as $value): ?>
    ...
<?php endforeach; ?>

2
Ý bạn là return is_iterable($var) ? $var : array($var);sao?
SQB

3

Nếu bạn đang sử dụng php7 và bạn chỉ muốn xử lý các lỗi không xác định thì đây là IMHO sạch nhất

$array = [1,2,3,4];
foreach ( $array ?? [] as $item ) {
  echo $item;
}

2
foreach ($arr ? $arr : [] as $elem) {
    // Does something 
}

Điều này không kiểm tra nếu nó là một mảng, nhưng bỏ qua vòng lặp nếu biến là null hoặc một mảng trống.


1

Tôi không chắc chắn nếu đây là trường hợp nhưng vấn đề này dường như xảy ra một số lần khi di chuyển các trang web wordpress hoặc di chuyển các trang web động nói chung. Nếu đây là trường hợp, hãy chắc chắn rằng máy chủ lưu trữ mà bạn đang di chuyển để sử dụng cùng phiên bản PHP mà trang web cũ của bạn sử dụng.

Nếu bạn không di chuyển trang web của mình và đây chỉ là một vấn đề đã xuất hiện, hãy thử cập nhật lên PHP 5. Điều này sẽ giải quyết một số vấn đề này. Có vẻ như là một giải pháp ngớ ngẩn nhưng đã lừa tôi.


1

Trường hợp đặc biệt cho thông báo này xảy ra nếu bạn đặt mảng thành null bên trong vòng lặp foreach

if (is_array($values))
{
    foreach ($values as $value)
    {
        $values = null;//WARNING!!!
    }
}

1

Làm thế nào về giải pháp này:

$type = gettype($your_iteratable);
$types = array(
    'array',
    'object'
);

if (in_array($type, $types)) {
    // foreach code comes here
}

1

Cảnh báo đối số không hợp lệ được cung cấp cho foreach()tweet hiển thị. đi đến /wp-content/plugins/display-tweets-php. Sau đó chèn mã này vào dòng số 591, Nó sẽ chạy hoàn hảo.

if (is_array($tweets)) {
    foreach ($tweets as $tweet) 
    {
        ...
    }
}

Tuyệt vời! Đó nên là giải pháp được chấp nhận. trong trường hợp của tôi, tôi đã thêm cái này:if (is_array($_POST['auto'])){ // code }
Jodyshop

0

Dường như cũng có một mối quan hệ với môi trường:

Tôi chỉ gặp lỗi "đối số không hợp lệ được cung cấp foreach ()" trong môi trường dev, nhưng không phải trong prod (Tôi đang làm việc trên máy chủ, không phải localhost).

Mặc dù lỗi, var_dump chỉ ra rằng mảng đã ở đó (trong cả hai trường hợp ứng dụng và dev).

Các if (is_array($array))xung quanh foreach ($array as $subarray)giải quyết vấn đề.

Xin lỗi, tôi không thể giải thích nguyên nhân, nhưng vì phải mất một thời gian tôi mới tìm ra giải pháp tôi nghĩ nên chia sẻ điều này tốt hơn như một quan sát.


0

Sử dụng hàm is_array, khi bạn sẽ chuyển mảng sang vòng lặp foreach.

if (is_array($your_variable)) {
  foreach ($your_variable as $item) {
   //your code
}
}

0

Điều gì về việc xác định một mảng trống là dự phòng nếu get_value()trống?
Tôi không thể nghĩ ra một cách ngắn nhất.

$values = get_values() ?: [];

foreach ($values as $value){
  ...
}

0

Tôi sẽ sử dụng một sự kết hợp của empty, issetis_arraynhư

$array = ['dog', 'cat', 'lion'];

if (!empty($array) && isset($array) && is_array($array) {
    //loop
    foreach ($array as $values) {
        echo $values; 
    }
}

-3

Tôi sẽ làm điều tương tự như Andy nhưng tôi sẽ sử dụng chức năng 'trống'.

như vậy

if(empty($yourArray))
{echo"<p>There's nothing in the array.....</p>";}
else
{
foreach ($yourArray as $current_array_item)
  {
    //do something with the current array item here
  } 
}

3
-1, Nếu $yourArray = 1;nó sẽ cố gắng lặp lại, và bạn sẽ nhận được lỗi. empty()không phải là một bài kiểm tra phù hợp.
Brad Koch

@BradKoch hoàn toàn đúng. is_array () là cách kiểm tra đáng tin cậy duy nhất cho dù $ yourArray có phải là một mảng hay không. Xem các câu trả lời khác để biết chi tiết về lý do tại sao is_array () không đủ - foreach cũng có thể xử lý các trình vòng lặp.
cgeisel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.