Refleksi php dapatkan tipe properti

Di PHP 8, kami menggunakan kelas, properti, dan konstanta kelas, metode, fungsi, parameter untuk mengakses atribut

Di PHP 8, Reflection API mengirimkan metode getAttribute() pada setiap objek Reflection yang cocok

Metode getAttribute() mengembalikan array ilustrasi ReflectionAttribute yang dapat diminta untuk nama atribut, argumen, dan untuk membuat instance dari atribut yang ditandai

Ini terdengar seperti pertanyaan wawancara yang sepele, kami tahu. Anda dapat dengan mudah menemukan metode yang terdokumentasi dengan baik dengan Refleksi. Beberapa guru sejati bahkan memberi kami cara non-Refleksi lainnya untuk melakukan hal yang sama. Namun, saat Anda mulai mengimplementasikannya sendiri, Anda dapat mengalami banyak masalah tak terduga dan aneh. Ini termasuk, tetapi tidak terbatas pada

Anda tidak dapat melakukannya dalam satu baris hanya dengan menggunakan Refleksi

Anda harus bermain-main dengan objek API Refleksi, tetapi jangan berani-berani lupa menelepon setAccessible(true) terlebih dahulu

tangkapan layar asli dari solusi Stackoverflow. Harta pribadi orang tua tidak dihitung ¯\_(ツ)_/¯

Itu benar. jika Anda mencoba mengakses properti pribadi yang diwariskan dari kelas induk, potongan kode Stackoverflow yang disetujui akan gagal

Mengakses privat orang tua gagal di PHP

Saya yakin ada penjelasan akademis yang masuk akal untuk perilaku ini. Jika Anda tahu alasannya, silakan bagikan dengan kami di komentar. Mungkin sesuatu seperti

Properti pribadi kelas induk tidak dapat diakses selama runtime dari dalam kelas anak. Oleh karena itu, mereka juga tidak dapat diakses melalui Reflection API

Kita bisa melihat sedikit perbedaan pada pesan error PHP pada screenshot dari 3v4l. org. alih-alih “Properti p tidak ada” kita mendapatkan “Properti Anak. $p tidak ada”. Ini menyiratkan bahwa properti kelas induk bahkan tidak dilihat

Jika Anda memikirkannya, Anda tidak dapat mengakses properti pribadi orang tua Anda sebagai seorang anak, jadi itu masuk akal

Terlepas dari semua lelucon, bagaimana kita bisa membaca properti pribadi suatu objek di PHP? . php-refleksi. Mari kita lihat sebuah contoh

PHP 7. 4 akhirnya membawa properti yang diketik. Ini adalah fitur yang saya nantikan, dan saya telah menghabiskan waktu berkualitas mengerjakan proyek saya yang sudah ada untuk menambahkan dukungan untuk properti yang diketik

Dengan properti yang diketik, Anda dapat mengatur tipe untuk semua properti kelas. Saat tipe disetel, mesin PHP mencegah siapa pun menyetel tipe yang berbeda ke properti kelas

class Example {
  public string $name;
  public DateTime $birthday;
}

Cuplikan di atas akan memastikan bahwa properti

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_7 akan selalu menjadi objek
class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
8. Sebelum PHP7. 4, pola data ketat semacam ini harus memiliki
class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
9 dan
class Example {
  public string $name;
}

$foo = new Example();
var_dump($foo->name === null);
0 metode untuk menegakkan integritas data

Jenis properti yang didukung

Jenis yang didukung untuk properti kelas
  • Jenis skalar.
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    1,
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    2,
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    3, dan
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    4
  • Jenis senyawa.
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    5,
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    6 dan
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    7
  • Nama kelas atau antarmuka apa pun (seperti
    class Example {
      public $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null); // true
    _8,
    class Example {
      public string $name;
    }
    
    $foo = new Example();
    var_dump($foo->name === null);
    9) dan
    Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
    0
  • Referensi ke objek induk dan objek sendiri.
    Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
    1 dan
    Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
    2
Jenis tidak didukung untuk properti kelas
  • Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
    _3. Memiliki properti
    Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
    _3 tidak masuk akal
  • Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
    _5. Tidak didukung karena perilakunya tidak dapat diprediksi. Lihatlah RFC callables yang konsisten untuk latar belakang lebih lanjut. Ini pada dasarnya menjadi merepotkan ketika callable dapat dideklarasikan dengan sintaks array, mis. g. sebagai
    Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
    _6

Di sinilah sebagian besar penarikan rambut mungkin terjadi. Dengan PHP7. 4 properti yang diketik, properti kelas memiliki status yang tidak diinisialisasi. Ini berarti bahwa properti belum diinisialisasi. Ini tidak sama dengan

Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
_8

Jika tidak ada tipe yang dideklarasikan, properti memiliki

Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
8 sebagai nilai yang tidak diinisialisasi

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_

Saat sebuah tipe dideklarasikan, semua properti akan memiliki status yang tidak diinisialisasi. Tidak diperbolehkan mengakses properti kelas sebelum menetapkan nilai eksplisit

class Example {
  public string $name;
}

$foo = new Example();
var_dump($foo->name === null);
_

Dalam cuplikan ini, properti

class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid
_0 tidak diinisialisasi. Ini tidak sama dengan
Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
_8, dan cuplikan di atas akan menimbulkan kesalahan

Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...

Periksa status yang tidak diinisialisasi

Anda dapat memeriksa apakah properti kelas tidak diinisialisasi menggunakan

class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid
2. Karena nilai ini tidak sama dengan
Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
8, Anda tidak dapat menggunakan
class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid
4 untuk memeriksa apakah properti tidak diinisialisasi

Setel ulang properti ke status tidak diinisialisasi

Untuk mengatur ulang properti kembali ke keadaan tidak diinisialisasi, gunakan

class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid
5. Setelah tidak disetel, mencoba mengakses properti tanpa menetapkan nilainya akan menimbulkan kesalahan
class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid
6 yang sama

Jenis yang dapat dibatalkan

Mirip dengan PHP7. 1's nullable type, tipe properti juga dapat ditandai nullable. Untuk menandai properti bisa menjadi nol, awali jenisnya dengan tanda tanya, mis. g.

class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid
_7

class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid

Bahkan jika sebuah properti ditandai nullable, nilainya yang tidak diinisialisasi tidak akan menjadi

Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
8. Misalnya, cuplikan di bawah ini masih akan menimbulkan kesalahan

class Example {
  public ?string $name;
}

$foo = new Example();
var_dump($foo->name);
// Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization

Meskipun ini tampak rumit untuk dikerjakan, ini memberikan fitur brilian yang dapat Anda yakini bahwa properti kelas akan selalu bertipe seperti itu. Jika nilainya tidak diinisialisasi, PHP akan menyerah dan memunculkan kesalahan alih-alih mengembalikan

Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
8, seperti untuk properti yang tidak diketik

Jenis yang ketat

Properti kelas juga mendukung deklarasi tipe ketat dengan

class Example {
  public ?string $name;
}

$foo = new Example();
var_dump($foo->name);
// Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization
0 di bagian atas blok file PHP. Tanpa tipe yang ketat, PHP akan memasukkan nilai ke tipe properti

class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"

Perhatikan bagaimana kami menyetel

class Example {
  public ?string $name;
}

$foo = new Example();
var_dump($foo->name);
// Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization
_1 ke properti
class Example {
  public string $name;
}

$foo = new Example();
var_dump($foo->name === null);
2, dan panggilan
class Example {
  public ?string $name;
}

$foo = new Example();
var_dump($foo->name);
// Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization
3 mengembalikan
class Example {
  public ?string $name;
}

$foo = new Example();
var_dump($foo->name);
// Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization
4 sebagai
class Example {
  public string $name;
}

$foo = new Example();
var_dump($foo->name === null);
2. Saat menetapkan nilai, mesin memberikan nilai ke tipe yang dideklarasikan

Untuk meminimalkan masalah dengan tipe juggling dan untuk memanfaatkan sepenuhnya properti yang diketik, saya sarankan Anda menguji kelas Anda dengan

class Example {
  public ?string $name;
}

$foo = new Example();
var_dump($foo->name);
// Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization
0. Sangat mudah untuk mengabaikan ketika PHP sedang membantu ketika dilemparkan ke tipe untuk Anda, tetapi ini bisa menjadi akar dari beberapa bug di hilir. Lebih mudah untuk men-debug kesalahan yang muncul segera daripada bug yang hanya terjadi pada Jumat malam pukul 6. 28PM, hanya saat DST aktif

Properti statis dan referensi

Properti statis juga dapat memiliki tipe yang dideklarasikan. Ini mungkin tampak seperti detail yang jelas, tetapi proposal sebelumnya untuk properti yang diketik tidak menyertakan properti statis. Di PHP7. 4, Anda juga dapat mendeklarasikan tipe untuk properti statis

Selanjutnya, Anda dapat mengembalikan referensi ke properti yang diketik, dan jenisnya akan tetap dihormati

class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 'Apple';
$bar =& $foo->name;
$bar = []; // Not allowed

Ini akan menimbulkan kesalahan

Fatal error: Uncaught TypeError: Cannot assign .. to reference held by property Example::$name of type .. in ...

Nilai default dalam konstruktor dan deklarasi properti

Untuk alasan historis, PHP memungkinkan Anda menetapkan nilai default untuk argumen fungsi dalam deklarasinya meskipun jenisnya tidak dapat dibatalkan

class Example {
  public function __construct(string $name = null) {
      var_dump($name);
  }
}

$foo = new Example();
// NULL

Di konstruktor, kami secara eksplisit menandai bahwa

class Example {
  public ?string $name;
}

$foo = new Example();
$foo->name = 'Ayesh'; // Valid
$foo->name = null; // Valid
0 argumen tidak dapat dibatalkan, namun PHP menerima
Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
8 sebagai nilai default. Perilaku ini hanya berlaku untuk
Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
8 nilai default. Meskipun secara semantik tidak valid, perilaku ini diperbolehkan karena alasan historis dan implementasi

Dengan properti yang diketik, ini tidak diperbolehkan

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_0

Ini akan segera menimbulkan kesalahan

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_1

Jenis Varians

PHP 7. 4 hadir dengan varian tipe pengembalian, yang berarti kelas anak dapat mengembalikan instance yang lebih spesifik. Ini belum didukung untuk jenis properti. Misalnya

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_2

Kode di atas tidak akan berfungsi. Meskipun

class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
0 adalah subset dari kelas
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
1, mengubah deklarasi tipe
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
2 tidak diperbolehkan. Anda masih dapat menetapkan instance
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
1 ke
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
2. Ini disebut
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
_5, dan sekarang didukung untuk jenis pengembalian
Mencoba hal di atas akan menimbulkan kesalahan seperti berikut

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_3

Kode berikut ini masih valid

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_4

Perhatikan bagaimana deklarasi properti di

class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
_6 dan
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
2 adalah
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
1, tetapi kami menetapkan instance
class Example {
  public string $name;
}

$foo = new Example();
$foo->name = 420;
var_dump($foo->name);
// string(3) "420"
0 untuk itu

Untuk meringkas

  • Anda tidak dapat mengganti kelas anak untuk properti
  • Anda tidak dapat menambahkan tipe ke kelas anak jika induknya tidak menerapkan tipe
  • Anda tidak dapat menandai tipe non-nullable sebagai nullable di kelas anak
  • Anda tidak dapat menandai tipe nullable sebagai non-nullable di kelas anak

Untuk memvisualisasikan ini, lihat rantai pewarisan berikut. Tidak satu pun dari hal berikut yang diizinkan

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_5

Di atas akan membuang kesalahan berikut (tidak bersamaan)

class Example {
  public $name;
}

$foo = new Example();
var_dump($foo->name === null); // true
_6

Kompatibilitas mundur

Mendeklarasikan jenis properti bersifat opsional, dan semua kode Anda yang ada akan berfungsi. Jika Anda berencana untuk memutakhirkan basis kode yang ada ke properti yang diketik, awasi status yang tidak diinisialisasi, dan pewarisan di mana aturan diterapkan dengan agak ketat. Selanjutnya, tipe properti tidak membawa perilaku warisan yang mengizinkan

Fatal error: Uncaught Error: Typed property Example::$name must not be accessed before initialization in ...
8 nilai dalam argumen fungsi/metodenya, dan ini bisa mengejutkan

Pikiran Akhir

Jenis properti adalah fitur yang secara pribadi membuat saya bersemangat, dan sekarang akhirnya ada di sini, saya telah menghabiskan beberapa waktu menambahkan jenis properti ke proyek pribadi saya yang sudah ada. PHPStorm 2019. 3 hadir dengan dukungan untuk semua PHP 7. 4 fitur, dan ada perbaikan cepat untuk menambahkan jenis properti jika dapat dikumpulkan dari komentar docblock properti atau dari konstruktor
Bahkan dalam proyek yang memiliki cakupan pengujian 100%, saya masih menemukan beberapa bug yang selalu ada, tetapi jenis properti membuatnya menonjol

Proyek open source mungkin membutuhkan waktu untuk membutuhkan PHP 7. 4 sebagai versi minimum, tetapi itu tidak akan menghalangi Anda untuk menggunakannya dalam proyek pribadi Anda