Cara mendeteksi bidang yang dideklarasikan secara dinamis pada objek dengan codeniffer di PHP

Setelah refactoring, kami memiliki sesuatu seperti ini di salah satu kelas kami:

class FooBar
{
    // $foo was $bla before
    private $foo;

    public function setBlubbOnArrayOnlyOnce($value)
    {
        // $this->bla was forgotten during refactoring. Must be $this->foo
        if(!isset($this->bla['blubb'])) {
             $this->foo['blubb'] = $value;
        }
    }
}

Jadi pada akhirnya $this->foo['blubb'] selalu disetel, tidak hanya sekali. Hal ini terjadi karena metode ajaib PHP. Kami tidak ingin mengakses bidang secara dinamis, jadi saya pikir saya hanya menambahkan aturan codeniffer. Tapi saya tidak menemukannya dan bertanya mengapa.

PHPStorm menunjukkan bidang yang dinyatakan pemberitahuan secara dinamis di sana, tetapi saya ingin ini gagal secara otomatis dengan codeniffer (atau yang serupa) selama siklus penerapan kami.

Adakah yang punya ide tentang ini? Apakah ada aturan yang baik? Haruskah saya menulis sendiri dan bagaimana caranya? Atau apakah menonaktifkannya merupakan praktik yang buruk?

Penafian: Kami menggunakan tes, tetapi terkadang Anda melewatkan sesuatu... Sebaiknya cegah hal ini terlebih dahulu. Selain itu, mohon jangan menimpa metode ajaib. Saya tidak ingin memiliki sifat/abstrak apapun di setiap kelas.


person Kasihasi    schedule 13.08.2015    source sumber
comment
Bisakah Anda mencari variabel yang tidak terdefinisi, karena $this-›bla tidak akan dideklarasikan? Anda mungkin harus memperluas kode di PHPCodeSniffer.   -  person Steven Scott    schedule 13.08.2015
comment
Saya mencoba, tetapi saya mengharapkan cara yang jelas dan mudah untuk melakukannya   -  person Kasihasi    schedule 14.08.2015
comment
Apakah Anda bertanya di Squizlabs (squizlabs.com) atau GitHub mereka (github.com/squizlabs/PHP_CodeSniffer) karena Greg Sherwood cukup responsif terhadap pertanyaan di masa lalu.   -  person Steven Scott    schedule 14.08.2015
comment
Ini mungkin tidak semudah itu, karena, secara umum, $this->bla dapat didefinisikan di kelas induk. Codesniffer bekerja pada tingkat file/token, dan jika Anda mengikuti standar pengkodean PSR, ia tidak akan mengetahui struktur kelas induk (karena berada dalam file terpisah).   -  person weirdan    schedule 08.01.2016
comment
@Weirdan Itu mungkin benar. Namun alangkah baiknya jika properti yang tidak terdefinisi dapat dideteksi di kelas sederhana, bukan memperluas kelas lainnya.   -  person maxhb    schedule 10.01.2016
comment
Mengapa Anda tidak mendeklarasikan private $foo = []; sebagai array di kelas?   -  person Ravi    schedule 12.01.2016
comment
Sudahkah Anda mencoba property_exists? php.net/manual/en/function.property-exists.php   -  person Ravi    schedule 12.01.2016


Jawaban (3)


Ini bukan masalah codeniffer atau phpstorm. Dan Anda tidak ingin memperbaiki masalah ini dengan codeniffer atau IDE. IDE, codeniffer, phpdocumentor, dll. -- ini adalah analisis "statis". Dan untuk analisis dinamis Anda dapat menggunakan mis. unit php.

Jika Anda ingin memeriksa keberadaan properti, Anda harus menggunakan fungsi property_exists().

class X
{
    public function __get($name)
    {
        $this->{$name} = null;
        return $this->{$name};
    }
}

$x = new X();
var_dump(property_exists($x, 'foo')); // false
var_dump($x->foo); // NULL
var_dump(property_exists($x, 'foo')); // true

Atau mungkin Anda bisa menggunakan refleksi untuk properti http://php.net/manual/en/class.reflectionproperty.php

Jika Anda ingin memeriksa "isset" Anda harus mengetahui:

var_dump(isset($x), $x); // false + NULL with notice
$x = null;
var_dump(isset($x), $x); // false + NULL
unset($x);
var_dump(isset($x), $x); // false + NULL without notice

Saat Anda memilih untuk kasus pemeriksaan ini, Anda dapat menggunakan isset()

Namun Anda harus selalu memeriksa terlebih dahulu keberadaan properti tersebut. Kalau tidak, Anda dapat memiliki perilaku kode Anda yang tidak terdefinisi.

person Deep    schedule 10.01.2016
comment
Ini bukan tentang mendeteksi apakah $this->bar[''anyStringValue didefinisikan (bahkan mungkin tidak mungkin dilakukan dalam analisis statis), ini tentang mendeteksi bahwa $this-›bar tidak ditentukan (lagi). - person maxhb; 10.01.2016
comment
@maxhb Jawaban saya termasuk tentang ini. - person Deep; 10.01.2016

Setelah pemfaktoran ulang

Sebaiknya hal ini dicegah sejak awal.

Anda hanya dapat mengetahui kesalahan pemfaktoran ulang semacam ini dengan menjalankan pengujian setelah setiap langkah pemfaktoran ulang. Kesalahan ini juga akan muncul, karena foo['blubb'] disetel ke nilai tertentu dan ini akan menyebabkan efek yang tidak diinginkan pada pengujian lain - tidak hanya pada pengujian logika penyetel.

Kami menggunakan tes, tetapi terkadang Anda melewatkan banyak hal...

Ya, sering kali cakupannya tidak cukup tinggi. Itu sebabnya memiliki cakupan pengujian yang baik adalah titik awal untuk semua pemfaktoran ulang.

Kedua baris ini tidak berwarna hijau dalam laporan cakupan Anda:

   if(!isset($this->bla['blubb'])) {
       $this->foo['blubb'] = $value;

Selain itu, mohon jangan menimpa metode ajaib. Saya tidak ingin memiliki sifat/abstrak apapun di setiap kelas.

Anda telah mengecualikannya, tapi itu salah satu cara untuk menangkap properti: dengan menggunakan fungsi ajaib __set() (untuk vars yang tidak dapat diakses) atau property_exists() atau penggunaan kelas Reflection* untuk menemukan.


Sekarang, sudah terlambat, Anda ingin alat lain menangkap kesalahan tersebut, oke:

Alat ini perlu mengurai file PHP dan induknya (karena cakupan variabel) dan menemukan $this->bla tanpa deklarasi variabel public|private|protected sebelumnya (properti kelas). Ini tidak akan menunjukkan jenis kesalahan yang sebenarnya, hanya saja bla itu diakses tanpa deklarasi.

Ini mungkin untuk diterapkan sebagai aturan CodeSniffer.

Anda juga dapat memberikan http://phpmd.org/ atau https://scrutinizer-ci.com/ coba. Dan, jika Anda menggunakan PHP7: https://github.com/etsy/phan

tl;tr

Sulit untuk menentukan kesalahan sebenarnya dan konteksnya tanpa menjalankan, mengevaluasi, dan menganalisis kode yang mendasarinya. Coba pikirkan tentang nama variabel dinamis dan Anda tahu alasannya: Anda bahkan tidak mengetahui nama properti dengan melihat kode sumbernya, karena properti tersebut dibuat secara dinamis selama aliran program. Penganalisis statis tidak akan menangkapnya.

Penganalisis dinamis harus melacak semua hal, di sini $this-> mengakses dan akan mempertimbangkan konteksnya: !isset(x). Evaluasi konteks dapat menemukan banyak kesalahan pengkodean yang umum. Pada akhirnya Anda dapat membuat laporan: mengatakan bahwa $this-›bla diakses hanya 1 kali dan itu menunjukkan bahwa

  • properti yang dideklarasikan secara dinamis telah diperkenalkan, namun tidak pernah digunakan kembali, dengan saran agar Anda menghapusnya atau mendeklarasikannya sebagai properti kelas
  • ATAU dengan evaluasi konteks tambahan: bahwa dan ketika variabel ini diakses dari dalam isset() - kunci properti yang tidak dideklarasikan yang tidak ada diakses, tanpa set() sebelumnya, dll.
person Jens A. Koch    schedule 13.01.2016

Sekarang di tahun 2017, Anda sedang mencari alat PHPStan. Saya menghubungkan intro singkat yang saya tulis untuk pengguna pertama kali.

Itu melakukan apa yang Anda butuhkan!

person Tomas Votruba    schedule 21.05.2017