Argumen tidak valid diberikan untuk foreach ()

304

Sering terjadi pada saya untuk menangani data yang bisa berupa array atau variabel nol dan untuk memberi makan beberapa foreachdengan data ini.

$values = get_values();

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

Saat Anda memberi makan foreach dengan data yang bukan array, Anda mendapatkan peringatan:

Peringatan: Argumen tidak valid diberikan untuk foreach () di [...]

Dengan asumsi itu tidak mungkin untuk memperbaiki get_values()fungsi untuk selalu mengembalikan array (kompatibilitas ke belakang, tidak tersedia kode sumber, apa pun alasan lain), saya bertanya-tanya yang merupakan cara paling bersih dan paling efisien untuk menghindari peringatan ini:

  • Casting $valueske array
  • Inisialisasi $valueske array
  • Membungkus foreachdenganif
  • Lainnya (harap disarankan)
Roberto Aloi
sumber
Sangat mungkin $valuesbukan array.
Bhargav Nanekalva

Jawaban:

509

Secara pribadi saya menemukan ini sebagai yang paling bersih - tidak yakin apakah ini yang paling efisien, pikiran!

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

Alasan untuk preferensi saya adalah karena tidak mengalokasikan array kosong ketika Anda tidak punya apa-apa untuk memulai.

Andy Shellam
sumber
4
Atau gunakan count () untuk mengetahui apakah array tidak kosong
Kemo
76
@Kemo: count()tidak dapat diandalkan. Jika Anda lulus count()nol, ia mengembalikan 0. Jika Anda memberikan argumen non-null, non-array, mengembalikan 1. Oleh karena itu tidak mungkin digunakan count()untuk menentukan apakah variabel tersebut adalah array ketika variabel bisa berupa array kosong, atau sebuah array yang mengandung 1 item.
Andy Shellam
12
Perhatikan bahwa beberapa objek dapat diubah, dan jawaban ini tidak menjelaskannya.
Brad Koch
32
Seharusnya if (is_array($values) || $values instanceof Traversable).
Bob Stein
3
Sayang sekali dia tidak melanjutkan untuk mengatakan itu bukan pasti yang paling efisien, meskipun: D
Gui Prá
116

Bagaimana dengan yang ini? lebih bersih dan semua dalam satu baris.

foreach ((array) $items as $item) {
 // ...
 }
Ajith R Nair
sumber
7
Ini adalah satu-satunya hal yang berhasil untuk saya. Untuk beberapa alasan, PHP tidak percaya bahwa array multidimensi yang saya buat sebenarnya adalah array array.
Justin
1
Sama di sini, ini adalah perbaikan yang sangat bagus untuk array yang berisi nilai array atau null. Cukup tambahkan tes di loop foreach untuk melanjutkan jika data nol.
Lizardx
Memecahkan masalah saya. Terima kasih!
Hitesh
1
Kode brilian, melewatkan nilai if, else untuk array dan none-array menggunakan $ _POST dengan kotak centang!
Yann Chabot
2
CATATAN: Saat cantik mencari dan menyelesaikan peringatan foreach yang tidak valid, metode ini akan mengembalikan peringatan variabel yang tidak ditentukan jika variabel tidak disetel dengan cara apa pun. Gunakan isset()atau is_array()atau keduanya, sepenuhnya tergantung pada skenario Anda dll.
James
42

Saya biasanya menggunakan konstruksi yang mirip dengan ini:

/**
 * 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...
    }
}

Perhatikan bahwa versi khusus ini tidak diuji, diketik langsung ke dalam SO dari memori.

Edit: menambahkan pemeriksaan Traversable

Keris
sumber
3
Jawaban Terbaik. Kecuali saya pikir Anda harus benar-benar memeriksa apakah $var instanceof Traversable. Lihat di sini . Karena misalnya Anda dapat melakukan pendahuluan SimpleXMLElement , tetapi itu bukan turunan dari Iterator atau IteratorAggregate.
Bob Stein
2
Anda mungkin dapat menghapus dua kelas lainnya, @ Kris. Mereka berdua memperluas Traversable sekarang dan tampaknya telah dilahirkan dengan cara seperti itu di 5.0.0. Meskipun aku merasakan sedikit keraguan, apakah contoh selalu diterapkan untuk meluas.
Bob Stein
1
@ BobStein-VisiBone: ya (kecuali mereka adalah antarmuka, bukan kelas) Namun; Saya menempatkan Traversable sebelum itu, baik Iterator maupun IteratorAggregate tidak perlu diverifikasi (dengan cara ini mereka tidak akan memperlambat eksekusi). Saya meninggalkan mereka untuk menyimpan jawaban sedekat mungkin dengan jawaban asli yang saya berikan dan membuatnya tetap jelas / mudah dibaca.
Kris
2
Saya pikir akan adil untuk menambahkan is_object($var)kembali. php.net/manual/en/language.oop5.iterations.php
Mark Fox
1
@ MarkFox: Merasa bebas, namun saya sengaja meninggalkannya; Saya belum pernah melihat penggunaan untuk itu yang tidak lebih baik dilayani dengan menerapkan Iteratoratau IteratorAggregate, tapi itu tentu saja hanya pendapat saya dan karenanya subyektif (saya tidak pernah menggunakan bidang publik).
Kris
15

Tolong jangan bergantung pada casting sebagai solusi , meskipun yang lain menyarankan ini sebagai opsi yang valid untuk mencegah kesalahan, itu mungkin menyebabkan yang lain.

Berhati-hatilah: Jika Anda mengharapkan bentuk array tertentu dikembalikan, ini mungkin gagal. Diperlukan lebih banyak cek untuk itu.

Misalnya casting boolean ke array (array)bool, TIDAK akan menghasilkan array kosong, tetapi array dengan satu elemen yang mengandung nilai boolean sebagai int: [0=>0]atau [0=>1].

Saya menulis tes cepat untuk menyajikan masalah ini . (Berikut ini adalah Tes cadangan jika url tes pertama gagal.)

Termasuk tes untuk: null,false , true, seorang class, sebuah arraydan undefined.


Selalu uji input Anda sebelum menggunakannya di foreach. Saran:

  1. Pengecekan tipe cepat :$array = is_array($var) or is_object($var) ? $var : [] ;
  2. Ketik array petunjuk dalam metode sebelum menggunakan foreach dan menentukan tipe pengembalian
  3. Membungkus foreach dalam jika
  4. Menggunakan try{}catch(){}balok
  5. Merancang kode / pengujian yang tepat sebelum rilis produksi
  6. Untuk menguji array terhadap form yang tepat, Anda dapat menggunakan array_key_existspada kunci tertentu, atau menguji kedalaman array (ketika itu adalah satu!) .
  7. Selalu ekstrak metode pembantu Anda ke namespace global dengan cara mengurangi kode duplikat
AARTT
sumber
8

Coba ini:

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

;)

GigolNet Guigolachvili
sumber
1
Ini tidak akan bekerja dengan baik dengan array asosiatif. Metode is_array secara keseluruhan lebih baik ... dan lebih mudah ...
AO_
4
$values = get_values();

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

Masalahnya selalu nol dan Casting sebenarnya adalah solusi pembersihan.

boctulus
sumber
3

Pertama-tama, setiap variabel harus diinisialisasi. Selalu.
Casting bukanlah suatu pilihan.
jika get_values ​​(); dapat mengembalikan variabel tipe yang berbeda, nilai ini harus diperiksa, tentu saja.

Akal Sehat Anda
sumber
Casting adalah opsi - jika Anda menginisialisasi array menggunakan $array = (array)null;Anda mendapatkan array kosong. Tentu saja itu buang-buang alokasi memori ;-)
Andy Shellam
2
+1: membaca dari sudut pandang sentimental, saya tidak peduli apakah bahasa dapat dilakukan tanpa, variabel HARUS dinyatakan dan hasil yang tidak dapat diandalkan HARUS diperiksa. Diperlukan untuk menjaga pengembang (s) waras dan log kesalahan pendek.
Kris
3

Ekstensi kode @ Kris yang lebih singkat

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

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

terutama untuk menggunakan kode templat di dalam

<?php foreach (secure_iterable($values) as $value): ?>
    ...
<?php endforeach; ?>
HongKilDong
sumber
2
Bukankah maksud Anda return is_iterable($var) ? $var : array($var);?
SQB
3

Jika Anda menggunakan php7 dan Anda hanya ingin menangani kesalahan yang tidak terdefinisi, ini adalah IMHO terbersih

$array = [1,2,3,4];
foreach ( $array ?? [] as $item ) {
  echo $item;
}
Edwin Rodríguez
sumber
2
foreach ($arr ? $arr : [] as $elem) {
    // Does something 
}

Ini tidak memeriksa apakah array, tetapi melewatkan loop jika variabelnya null atau array kosong.

T30
sumber
1

Saya tidak yakin apakah ini masalahnya tetapi masalah ini tampaknya terjadi beberapa kali saat memigrasi situs wordpress atau memigrasi situs dinamis secara umum. Jika demikian, pastikan hosting tempat Anda bermigrasi menggunakan versi PHP yang sama dengan yang digunakan situs lama Anda.

Jika Anda tidak memigrasikan situs Anda dan ini hanya masalah yang muncul coba perbarui ke PHP 5. Ini menangani beberapa masalah ini. Mungkin terlihat seperti solusi konyol tetapi melakukan trik untuk saya.

Erik
sumber
1

Kasus luar biasa untuk pemberitahuan ini terjadi jika Anda mengatur array ke null di dalam foreach loop

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

Bagaimana dengan solusi ini:

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

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

Argumen tidak valid peringatan disediakan untuk foreach()tweet tampilan. pergi ke /wp-content/plugins/display-tweets-php. Kemudian masukkan kode ini pada nomor baris 591, Ini akan berjalan dengan sempurna.

if (is_array($tweets)) {
    foreach ($tweets as $tweet) 
    {
        ...
    }
}
Saad Khanani
sumber
Luar biasa! Itu harus menjadi solusi yang diterima. dalam kasus saya, saya telah menambahkan ini:if (is_array($_POST['auto'])){ // code }
Jodyshop
0

Tampaknya ada juga hubungan dengan lingkungan:

Saya memiliki "argumen tidak valid disediakan foreach ()" kesalahan hanya di lingkungan dev, tetapi tidak di prod (saya bekerja di server, bukan localhost).

Meskipun ada kesalahan, var_dump menunjukkan bahwa array ada di sana (baik aplikasi maupun dev).

Di if (is_array($array))sekitar foreach ($array as $subarray)memecahkan masalah.

Maaf, saya tidak bisa menjelaskan penyebabnya, tetapi karena butuh beberapa saat untuk mencari solusi, saya pikir lebih baik membagikan ini sebagai pengamatan.

araldh
sumber
0

Gunakan fungsi is_array, ketika Anda akan meneruskan array ke foreach loop.

if (is_array($your_variable)) {
  foreach ($your_variable as $item) {
   //your code
}
}
Super Model
sumber
0

Bagaimana dengan mendefinisikan array kosong sebagai fallback jika get_value()kosong?
Saya tidak bisa memikirkan cara terpendek.

$values = get_values() ?: [];

foreach ($values as $value){
  ...
}
Quentin Veron
sumber
0

Saya akan menggunakan kombinasi empty, issetdan is_arraysebagai

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

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

saya akan melakukan hal yang sama seperti Andy tetapi saya akan menggunakan fungsi 'kosong'.

seperti itu:

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
  } 
}
as_bold_as_love
sumber
3
-1, Jika $yourArray = 1;itu akan mencoba untuk beralih, dan Anda akan mendapatkan kesalahan. empty()bukan tes yang cocok.
Brad Koch
@BradKoch benar sekali. is_array () adalah satu-satunya cara yang dapat diandalkan untuk menguji apakah $ yourArray adalah array. Lihat jawaban lain untuk perincian tentang mengapa is_array () tidak cukup - foreach dapat menangani iterator juga.
cgeisel