Haruskah saya menggunakan assert dalam kode PHP saya?

87

Seorang rekan kerja telah menambahkan perintah assert beberapa kali dalam perpustakaan kami di tempat di mana saya akan menggunakan pernyataan if dan memberikan pengecualian. (Saya belum pernah mendengar tentang assert sebelumnya.) Berikut adalah contoh bagaimana dia menggunakannya:

assert('isset($this->records); /* Records must be set before this is called. */');

Saya akan melakukan:

if (!isset($this->records)) {
    throw new Exception('Records must be set before this is called');
}

Dari membaca dokumen PHP tentang assert , sepertinya Anda disarankan untuk memastikan assert aktif dan menambahkan penangan sebelum menggunakan assert. Saya tidak dapat menemukan tempat di mana dia melakukan ini.

Jadi, pertanyaan saya adalah, apakah menggunakan assert adalah ide bagus yang diberikan di atas dan haruskah saya menggunakannya lebih sering daripada jika dan pengecualian?

Catatan lain, kami berencana untuk menggunakan pustaka ini pada berbagai proyek dan server, termasuk proyek yang bahkan mungkin bukan bagian kami (pustaka bersifat open source). Apakah ada perbedaan dalam menggunakan assert?

Darryl Hein
sumber
Apakah benar-benar 'isset(baris kode dengan assert)? Bukan hanya isset(tanpa kutipan tunggal, ')?
Peter Mortensen

Jawaban:

79

Aturan praktis yang dapat diterapkan di sebagian besar bahasa (semua yang samar-samar saya ketahui) adalah bahwa an assertdigunakan untuk menyatakan bahwa suatu kondisi selalu benar sedangkan an ifsesuai jika dapat dibayangkan terkadang akan gagal.

Dalam hal ini, saya akan mengatakan itu asserttepat (berdasarkan pemahaman saya yang lemah tentang situasi) karena recordsharus selalu diatur sebelum metode yang diberikan dipanggil. Jadi, kegagalan untuk menetapkan rekor akan menjadi bug dalam program daripada kondisi runtime. Di sini, assertmembantu memastikan (dengan pengujian yang memadai) bahwa tidak ada kemungkinan jalur eksekusi program yang dapat menyebabkan kode yang dijaga dengan assertdipanggil tanpa recordsdisetel.

Keuntungan menggunakan assertsebagai lawan ifadalah yang assertumumnya dapat dimatikan dalam kode produksi sehingga mengurangi overhead. Jenis situasi yang paling baik ditangani ifdapat terjadi selama runtime dalam sistem produksi sehingga tidak ada yang hilang dengan tidak dapat mematikannya.

aaronasterling
sumber
4
Untuk menambah ini, Anda mungkin tidak ingin menonaktifkan pernyataan dalam kode produksi Anda, karena mereka membantu memastikan bahwa kondisi "ini tidak boleh terjadi" tetap seperti itu. Mungkin lebih baik membiarkan aplikasi Anda berhenti dari sebuah assert daripada membiarkan pengguna Anda terus mengikuti jalur eksekusi yang seharusnya tidak ada.
derekerdmann
2
@derekerdmann: Benar. Untuk beberapa pernyataan, mungkin cukup dengan mencatatnya (dalam produksi) atau mencetak peringatan (dalam lingkungan pengembangan). Tapi karena seringkali menegaskan juga melindungi kode keamanan yang relevan, Anda mungkin juga mengaktifkan assert_options(ASSERT_BAIL). Ini lebih cepat daripada solusi manual if / throw.
mario
4
@ Derekerdmann Saya tidak setuju tentang ini (dalam konteks menggunakan assert () di php). Ini adalah celah kerentanan yang besar, karena assert () memperlakukan semua argumen string sebagai kode PHP, sehingga (secara teoritis) dimungkinkan untuk memasukkan dan menjalankan kode arbitrer. IMHO, pernyataan harus dimatikan pada produksi
Vitaliy Lebedev
2
@VitaliyLebedev jika Anda tidak ingin rentan terhadap injeksi jangan berikan string untuk menegaskan.
Damon Snyder
9
Terlambat ke pesta tetapi PHP.net menyatakan: "Pernyataan harus digunakan sebagai fitur debugging saja."
Koen.
25

Pikirkan pernyataan sebagai "komentar kuat". Dari pada komentar seperti:

// Note to developers: the parameter "a" should always be a number!!!

menggunakan:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');

Artinya persis sama dan ditujukan untuk audiens yang sama persis, tetapi komentar pertama mudah dilupakan atau diabaikan (tidak peduli berapa banyak tanda seru), sedangkan "komentar kuat" tidak hanya tersedia untuk dibaca dan dipahami manusia, itu juga terus-menerus diuji mesin selama pengembangan, dan tidak akan diabaikan jika Anda menyiapkan penanganan assert yang baik dalam kode dan kebiasaan kerja.

Dilihat dengan cara ini, pernyataan adalah konsep yang sama sekali berbeda dari if (error) ... dan pengecualian, dan keduanya dapat hidup berdampingan.

Ya, Anda harus mengomentari kode Anda, dan ya, Anda harus menggunakan "komentar kuat" (menegaskan) bila memungkinkan.

DaveWalley
sumber
bagaimana jika dalam pengembangan saat pengujian Anda selalu meneruskan kondisi baik ke assert tetapi dalam produksi jika assert dimatikan - beberapa pengguna melewati kondisi lain yang tidak Anda pikirkan saat pengujian? Atau jika tidak, Anda harus terus menegaskan, tetapi bukankah itu sama dengan menulis cek Anda sendiri?
Dariux
Kemudian program Anda akan gagal. Perbaiki dengan benar menggunakan pernyataan if dan fitur penanganan error bahasa dan lingkungan pengembangan Anda. menegaskan dapat mengungkap masalah, ada cara yang lebih baik untuk memperbaiki masalah.
DaveWalley
Perhatikan bahwa mulai dari PHP 7.2 meneruskan string ke assert untuk evaluasi sudah tidak digunakan lagi. Sedih, karena kelihatannya cukup praktis.
Jannie Theunissen
16

Itu sepenuhnya tergantung pada strategi pengembangan Anda. Sebagian besar pengembang tidak menyadari assert()dan menggunakan pengujian unit hilir. Tetapi skema pengujian proaktif dan built-in terkadang bisa menguntungkan.

assert berguna, karena dapat diaktifkan dan dinonaktifkan. Itu tidak menguras kinerja jika tidak ada penangan pernyataan seperti itu yang ditentukan. Kolega Anda tidak memilikinya, dan Anda harus merancang beberapa kode yang memungkinkannya sementara dalam lingkungan pengembangan (jika E_NOTICE / E_WARNING aktif, jadi harus menjadi penangan pernyataan). Saya menggunakannya sesekali di mana kode saya tidak dapat menerima jenis variabel campuran - saya biasanya tidak terlibat dalam pengetikan ketat dalam PHP yang diketik lemah, tetapi ada kasus penggunaan acak:

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));

Yang misalnya akan mengkompensasi kurangnya penentu tipe string $a, array $b. PHP5.4 akan mendukung mereka, tetapi tidak dicentang.

mario
sumber
Apa artinya "php 5.4 akan memilikinya tetapi tidak centang"?
Kzqai
1
PHP 5.4 memiliki, mendukung dan memeriksa menegaskan.
DaveWalley
7

Assert bukanlah pengganti untuk kontrol aliran normal seperti ifatau pengecualian, karena ini hanya dimaksudkan untuk digunakan untuk debugging selama pengembangan.

Tandai Snidovich
sumber
6

Catatan penting tentang assert di PHP lebih awal dari 7. Tidak seperti bahasa lain dengan konstruksi assert, PHP tidak membuang pernyataan assert sepenuhnya - ia memperlakukannya sebagai fungsi (lakukan debug_backtrace () dalam fungsi yang dipanggil dengan assertion). Mematikan asserts tampaknya hanya mengubah fungsi menjadi tidak melakukan apa pun di mesin. Perhatikan bahwa PHP 7 dapat dibuat untuk meniru perilaku ini dengan menyetel zend.assertions ke 0 alih-alih nilai yang lebih normal dari 1 (aktif) atau -1 (nonaktif).

Masalah yang muncul dalam assert itu akan mengambil argumen apa pun - tetapi jika argumennya bukan string, maka assert mendapatkan hasil ekspresi apakah assert aktif atau nonaktif. Anda dapat memverifikasi ini dengan blok kode berikut.

<?php
  function foo($a) { 
    echo $a . "\n"; 
    return TRUE;
  }
  assert_options(ASSERT_ACTIVE, FALSE);

  assert( foo('You will see me.'));
  assert('foo(\'You will not see me.\')');

  assert_options(ASSERT_ACTIVE, TRUE);

  assert( foo('Now you will see'));
  assert('foo(\'both of us.\')');

Mengingat maksud dari assert ini adalah bug, dan sudah lama sejak itu ada dalam bahasa sejak assert diperkenalkan kembali di PHP 4.

String yang diteruskan ke assert dievaluasi, dengan semua implikasi performa dan bahaya yang menyertainya, tetapi ini adalah satu-satunya cara untuk mendapatkan pernyataan assert agar berfungsi sebagaimana mestinya di PHP (Perilaku ini tidak digunakan lagi di PHP 7.2).

EDIT: Diubah di atas untuk mencatat perubahan di PHP 7 dan 7.2

Michael Morris
sumber
1
Di PHP 7 ada / akan ada zend.assertionspengaturan ini untuk mematikan sepenuhnya assert().
Kontrollfreak
Itu adalah berita bagus - tetapi berdasarkan dokumentasi di sana, sepertinya patch ke PHPUnit adalah urutan, menambahkan penangan callback assert untuk menampilkan AssertionException ketika pernyataan gagal di bawah PHP 5.x. Dengan cara ini Pengujian unit dapat menggunakan anotasi @expectedException AssertionException terlepas dari apakah pengujian tersebut dijalankan pada PHP 5.x atau 7.
Michael Morris
3

Assert sebaiknya hanya digunakan dalam pengembangan karena berguna untuk debugging. Jadi jika Anda mau, Anda dapat menggunakannya untuk mengembangkan situs web Anda, tetapi Anda harus menggunakan pengecualian untuk situs web langsung.

Kyle
sumber
7
Tapi seseorang masih akan memiliki pernyataan dalam kode. Mereka tidak akan aktif dalam lingkungan produksi.
aaronasterling
1
Saya akan menyimpannya dalam produksi dan menyesuaikan penanganan kesalahan saya.
Daniel W.
3

Tidak, rekan kerja Anda tidak boleh menggunakannya sebagai penangan kesalahan tujuan umum. Menurut manual:

Pernyataan harus digunakan sebagai fitur debugging saja. Anda dapat menggunakannya untuk pemeriksaan kewarasan yang menguji kondisi yang harus selalu BENAR dan yang menunjukkan beberapa kesalahan pemrograman jika tidak atau untuk memeriksa keberadaan fitur tertentu seperti fungsi ekstensi atau batas dan fitur sistem tertentu.

Pernyataan tidak boleh digunakan untuk operasi waktu proses normal seperti pemeriksaan parameter input. Sebagai aturan praktis, kode Anda harus selalu dapat berfungsi dengan benar jika pemeriksaan pernyataan tidak diaktifkan.

Jika Anda terbiasa dengan rangkaian pengujian otomatis, kata kerja "assert" biasanya digunakan untuk memverifikasi keluaran dari beberapa metode atau fungsi. Sebagai contoh:

function add($a, $b) {
    return $a + $b;
}

assert(add(2,2) == 5, 'Two and two is four, dummy!');
assert(is_numeric(add(2,2)), 'Output of this function to only return numeric values.');

Rekan kerja Anda tidak boleh menggunakannya sebagai pengendali kesalahan tujuan umum dan dalam kasus ini sebagai pemeriksaan masukan. Sepertinya bidang rekaman tidak dapat disetel oleh beberapa pengguna perpustakaan Anda.

Dean Atau
sumber
3

Rekan kerja Anda benar-benar mencoba menerapkan design by contract (DbC) dari bahasa Eiffel dan berdasarkan buku: Object Oriented Software Construction, 2nd Edition.

Penegasan, seperti yang dia gunakan, akan menjadi {P}-bagian dari Hoare Logic atau Hoare Triple: {P} C {Q}, di mana {P} adalah pernyataan prasyarat (ion) s dan {Q} adalah pernyataan pasca-kondisi (ion) s.

Saya akan mengambil catatan kritis dari saran yang diberikan tentang fitur assert di PHP yang memiliki bug. Anda tidak ingin menggunakan kode buggy. Apa yang Anda inginkan adalah pembuat PHP untuk memperbaiki bug di assert. Sampai mereka melakukannya, Anda dapat menggunakan assert, tetapi gunakan dengan memperhatikan status buggy saat ini.

Selain itu, jika fitur assert buggy, saya sarankan Anda tidak menggunakannya dalam kode produksi. Namun demikian, saya menganjurkan agar Anda menggunakannya dalam pengembangan dan pengujian kode jika sesuai.

Terakhir — jika Anda melakukan studi desain demi kontrak, Anda akan menemukan bahwa ada konsekuensi penggunaan pernyataan Boolean dalam kaitannya dengan pewarisan klasik berorientasi objek — yaitu — Anda tidak boleh melemahkan prasyarat, atau melemahkan pascakondisi. Melakukannya bisa berbahaya bagi objek turunan polimorfik Anda yang berinteraksi satu sama lain. Sampai Anda memahami apa artinya — saya akan membiarkannya!

Selain itu — saya sangat menyarankan agar pembuat PHP melakukan studi komprehensif tentang desain berdasarkan kontrak dan mencoba memasukkannya ke dalam PHP ASAP! Maka kita semua bisa mendapatkan keuntungan dari memiliki kompiler / interpreter yang sadar DbC, yang akan menangani masalah yang disebutkan dalam jawaban (di atas):

  1. Kompiler design-by-contract-aware yang diimplementasikan dengan benar (mudah-mudahan) akan bebas bug (tidak seperti pernyataan PHP saat ini).
  2. Kompiler desain-demi-kontrak-sadar yang diimplementasikan dengan benar akan menangani nuansa manajemen logika pernyataan polimorfik untuk Anda alih-alih memeras otak Anda atas masalah ini!

CATATAN: Bahkan penggunaan if-statement sebagai pengganti assert (precondition) akan mengalami konsekuensi yang mengerikan jika digunakan untuk memperkuat prasyarat atau melemahkan post-condition. Untuk memahami apa artinya, Anda perlu mempelajari desain demi kontrak untuk mengetahuinya! :-)

Selamat belajar dan belajar.

Larry
sumber