Apakah mendeklarasikan bidang pada kelas benar-benar berbahaya di PHP?

13

Pertimbangkan kode berikut, di mana setter sengaja rusak karena kesalahan pemrograman biasa yang telah saya buat nyata beberapa kali di masa lalu:

<?php

    class TestClass {

        private $testField;

        function setField($newVal) {
            $testField = $newVal;
            // deliberately broken; should be `$this->testField = $newVal`
        }

        function getField() {
            return $this->testField;
        }

    }

    $testInstance = new TestClass();
    $testInstance->setField("Hello world!");

    // Actually prints nothing; getField() returns null
    echo $testInstance->getField(); 

?>

Fakta bahwa saya menyatakan $testFielddi bagian atas kelas membantu menyembunyikan kesalahan pemrograman itu dari saya. Jika saya tidak mendeklarasikan bidang, maka saya akan mendapatkan sesuatu yang mirip dengan peringatan berikut yang dicetak ke log kesalahan saya setelah memanggil skrip ini, yang berpotensi berguna untuk membantu debugging saya - terutama jika saya membuat kesalahan seperti ini di aplikasi dunia nyata yang besar dan rumit:

Pemberitahuan PHP: Properti tidak terdefinisi: TestClass :: $ testField di /var/www/test.php on line 13

Dengan deklarasi itu, tidak ada peringatan.

Mungkin saya kehilangan sesuatu, tetapi saya menyadari hanya dua alasan untuk mendeklarasikan bidang kelas di PHP: pertama, bahwa deklarasi bertindak sebagai dokumentasi, dan kedua, bahwa tanpa deklarasi, seseorang tidak dapat menggunakan privatedan protectedmengakses pengubah, yang merupakan bisa dibilang bermanfaat. Karena argumen yang terakhir tidak berlaku untuk bidang publik - menugaskan ke bidang yang tidak dideklarasikan dari suatu objek menjadikannya publik - menurut saya setidaknya saya harus mengomentari semua deklarasi bidang publik saya. Komentar akan memberikan nilai dokumentasi yang persis sama, tetapi saya akan mendapat manfaat dari peringatan jika saya mencoba membaca bidang yang tidak diinisialisasi.

Namun, setelah dipikirkan lebih lanjut, tampaknya tidak masuk akal untuk berhenti di situ. Karena dalam pengalaman saya mencoba membaca bidang yang tidak diinisialisasi adalah penyebab kesalahan yang jauh lebih umum daripada mencoba membaca secara tidak tepat atau memodifikasi bidang pribadi atau yang dilindungi (saya sudah melakukan yang sebelumnya beberapa kali dalam karir pemrograman singkat saya, tetapi tidak pernah yang terakhir ), bagi saya sepertinya mengomentari semua deklarasi lapangan - bukan hanya yang umum - akan menjadi praktik terbaik.

Yang membuat saya ragu adalah bahwa saya belum pernah melihat orang lain melakukannya dalam kode mereka. Kenapa tidak? Apakah ada manfaat untuk mendeklarasikan bidang kelas yang tidak saya ketahui? Atau bisakah saya memodifikasi konfigurasi PHP dengan beberapa cara untuk mengubah perilaku deklarasi lapangan sehingga saya bisa menggunakan deklarasi lapangan nyata dan masih mendapat manfaat dari peringatan "Properti tidak terdefinisi"? Atau ada hal lain yang saya lewatkan dalam analisis saya?

Mark Amery
sumber
1
Bisa dibilang bermanfaat ? Manfaat yang Anda nyatakan sangat penting. Saya mungkin akan menolak untuk bekerja pada kode yang tidak memiliki deklarasi properti eksplisit.
Michael
2
Sungguh, cara untuk melindungi diri Anda dari kesalahan ini adalah dengan pemeriksaan kesalahan yang cermat dan lebih baik, melalui pengujian unit.
Michael
1
ada built in pelaporan kesalahan php (tingkat pemberitahuan) yang akan memberi tahu Anda jika Anda menggunakan variabel tanpa terlebih dahulu mendeklarasikannya.
Crayon Violent
3
@MarkAmery Saya akan berpikir bahwa startup yang serba terburu-buru adalah salah satu tempat paling penting untuk mengadopsi pengujian unit yang ketat - karena Anda selalu mengubah kode, Anda cenderung selalu melanggarnya . Itu benar-benar tujuan tepat pengujian ketat dan TDD flat-out. Ya, saya kira menggunakan garis bawah dapat membantu Anda mengingat Anda sedang mengakses barang-barang pribadi, tetapi bagi saya gagasan untuk mengadopsi standar pengkodean khusus untuk melindungi saya dari membuat kesalahan yang seharusnya tidak saya buat adalah meresahkan. Seperti melakukan TRUE === $variableuntuk mencegah diri Anda menetapkan secara tidak sengaja alih-alih membandingkan.
Michael
1
@MarkAmery Perhatikan juga bahwa kata kunci visibilitas diuraikan oleh alat dokumentasi otomatis , sehingga mereka memberikan lebih banyak "nilai dokumentasi" daripada hanya komentar yang akan Anda masukkan secara manual.
Michael

Jawaban:

15

Anda harus selalu mendeklarasikan properti kelas Anda sebelumnya. Meskipun PHP adalah bahasa yang dinamis dan dengan senang hati akan berjalan seiring dengan Anda membuat properti Anda saat runtime, ada beberapa kelemahan pada jalur ini.

  • Kompiler dapat mengoptimalkan properti yang dideklarasikan. Saat Anda mendeklarasikan properti secara dinamis, ini adalah hit kinerja karena kompiler harus membuat tabel hash dinamis untuk menyimpan properti dinamis Anda.
  • Saya tidak 100% pada yang satu ini tetapi saya percaya bahwa bytecode optimizers seperti APC tidak akan berguna karena mereka tidak akan memiliki gambaran lengkap dari kelas Anda. (Dan pengoptimal seperti APC adalah suatu keharusan )
  • Membuat kode Anda 1000x lebih sulit dibaca. Orang akan membencimu.
  • Membuatnya 1000x lebih mungkin bahwa Anda akan membuat kesalahan. (Apakah getId atau getID? Tunggu, Anda menggunakan keduanya. Gagal.)
  • Tidak ada pelengkapan otomatis IDE atau pengetikan tipe.
  • Generator dokumentasi tidak akan melihat properti Anda.

Masalah yang Anda uraikan sebenarnya masalah kecil jika dibandingkan dengan masalah tidak mendeklarasikan properti Anda dalam definisi kelas Anda. Ini solusi yang bagus.

Biasakan mendeklarasikan default untuk properti Anda.

private $testField = null;
private $testField = '';
private $testField = 0;
private $testField = []; //array()
private $testField = false;

Kecuali untuk properti yang akan menyimpan objek, ini akan mencakup sebagian besar tipe dasar yang akan Anda simpan. Properti yang akan menyimpan objek dapat diatur di konstruktor Anda.

Aturan yang baik untuk desain kelas adalah bahwa setelah objek Anda telah dibuat dan konstruktor Anda telah berjalan, Anda seharusnya tidak memiliki properti di luar sana "tidak terdefinisi".

Jarrod Nettles
sumber
Menanggapi 5 kerugian Anda: 1 (Kinerja): Apakah Anda memiliki sumber untuk ini? 2 (Keterbacaan): Tidak yakin saya mengerti; proposal itu untuk mengomentari deklarasi, bukan hanya menghapusnya, jadi keterbacaan apa yang hilang? Penyorotan sintaksis yang sedikit kurang ramah dan potensi kesulitan membedakan dari komentar tetangga, saya kira? 3: Yang ini saya tidak mengerti sama sekali; bisakah kamu mengklarifikasi? 4 (IDE): Cukup adil - Saya tidak punya pengalaman coding PHP dengan IDE dan tidak tahu tentang isyarat tipe Eclipse. 5 (generator generator): Cukup adil - tidak pernah menggunakan salah satunya.
Mark Amery
Saya tidak melihat kalimat Anda tentang berkomentar. Terlepas dari itu, poin-poin di atas masih berlaku - bagaimana Anda tahu mana yang aktif atau berkomentar secara sah ??
Jarrod Nettles
Menanggapi solusi yang Anda usulkan: inilah tepatnya yang ingin saya hindari - penetapan standar bahkan ketika tidak ada artinya dan nilai default tidak boleh digunakan. Masalah dengan default ini (dan dengan deklarasi tanpa penugasan, yang hanya menetapkan nol) adalah bahwa jika, karena kesalahan pemrograman, saya tidak memberikan nilai pada sesuatu di konstruktor ketika saya seharusnya, maka daripada PHP memberikan peringatan ketika saya mencoba menggunakan nilai itu nanti - membantu saya menemukan kesalahan saya - semuanya akan berfungsi dengan baik meskipun kelasnya rusak.
Mark Amery
2
@MarkAmery Kesalahan pemrograman Anda, pada dasarnya sama dengan kesalahan ketik. Kita semua sudah memilikinya - tetapi Anda mencoba meledakkan bom untuk membunuh lalat di sini.
Jarrod Nettles
Kamu mungkin benar. Saya menghabiskan beberapa jam melacak bug hari ini yang ternyata disebabkan sepenuhnya oleh kesalahan ketik seperti itu, dan merasa frustrasi setelahnya oleh kesadaran yang tiba-tiba bahwa jika saya tidak menggunakan deklarasi lapangan, saya akan mendapat pemberitahuan dan segera diketahui di mana kesalahan saya (yang saya selalu berpikir akan terjadi dalam keadaan ini; saya tidak menyadari deklarasi menginisialisasi bidang ke nol). Aksesibilitas langsung dari pengalaman itu mungkin condong pada penilaian saya tentang seberapa besar kemungkinannya terulang kembali, atau seberapa berharganya mempertahankannya.
Mark Amery
2

Saya bekerja pada beberapa kode yang menggunakan properti objek yang dibuat secara dinamis. Saya pikir menggunakan properti objek yang dibuat secara dinamis cukup keren (benar, menurut saya). Namun, program saya butuh 7 detik untuk berjalan. Saya menghapus properti objek dinamis dan menggantinya properti objek yang dinyatakan sebagai bagian dari setiap kelas (publik dalam kasus ini). Waktu CPU berubah dari 7 detik menjadi 0,177 detik. Itu cukup besar.

Mungkin saja saya melakukan sesuatu yang salah dengan cara saya menggunakan properti objek dinamis. Mungkin juga konfigurasi saya rusak. Tentu saja, saya harus mengatakan bahwa saya memiliki konfigurasi PHP vanilla yang sangat sederhana di komputer saya.

pengguna328304
sumber