Sejak awal, PHP mengambil jalan tengah yang unik: property object selalu public dan bisa diakses langsung, tanpa perantara method. Begitu kita butuh sedikit logika — memangkas spasi, menormalkan format, menolak nilai kosong — satu-satunya pilihan selama dua puluh tahun adalah menutup property menjadi private lalu menulis pasangan getX()/setX() di setiap class. Bahasa seperti C# dan Kotlin lebih dulu menyelesaikan masalah ini lewat property bawaan; PHP 8.4 akhirnya menyusul dengan property hooks — logika get dan set yang ditulis tepat di deklarasi property. Chapter ini menelusuri evolusinya dari boilerplate lama ke bentuk barunya, lalu menunjukkan variasi yang paling menarik: property virtual yang tidak menyimpan nilai sama sekali. Aturan pewarisan, kombinasi dengan interface, dan detail lanjutan dibahas di chapter property hooks di Section Advanced Object Model
.
Boilerplate Getter dan Setter Lama #
Bentuk klasiknya tampak seperti ini: property private, dua method publik sebagai pintu masuk, dan constructor yang harus memanggil setter agar nilai awal ikut tervalidasi:
<?php
class PenggunaLama
{
private string $nama;
public function __construct(string $nama)
{
$this->setNama($nama);
}
public function getNama(): string
{
return $this->nama;
}
public function setNama(string $nama): void
{
$this->nama = trim($nama);
}
}
$budi = new PenggunaLama(' Budi ');
echo '[', $budi->getNama(), ']', "\n";
$budi->setNama(' Siti ');
echo '[', $budi->getNama(), ']', "\n";Output yang Dihasilkan:
[Budi]
[Siti]Kode ini bekerja — kedua nilai terpangkas spasinya sebelum tersimpan. Harganya adalah tiga deklarasi demi satu property: setter dipaksa hadir hanya untuk memangkas spasi, getter hanya meneruskan nilai mentah, dan constructor harus ingat memanggil $this->setNama() alih-alih menulis $this->nama langsung. Kalikan oleh sepuluh property pada class konfigurasi, dan separuh file menjadi upacara yang tidak menyimpan informasi.
Hook set: Logika Menempel di Property #
Property hooks memindahkan logika tersebut ke tempat yang paling natural — deklarasi property itu sendiri:
<?php
class Pengguna
{
public string $nama {
set (string $nilai) {
$this->nama = trim($nilai);
}
}
}
$budi = new Pengguna();
$budi->nama = ' Budi ';
echo '[', $budi->nama, ']', "\n";
$budi->nama = ' Siti ';
echo '[', $budi->nama, ']', "\n";Output yang Dihasilkan:
[Budi]
[Siti]Mari kita bedah sintaks barunya:
public string $nama { ... }: tanda kurung kurawal setelah deklarasi menandai bahwa property ini punya hook — bukan nilai mentah polos.set (string $nilai): hook yang berjalan pada setiap penulisan; parameter$nilaimenampung nilai yang dimasukkan.$this->nama = trim($nilai);: di dalam hook, penulisan$this->namamengakses backing value — slot penyimpanan asli di balik property. Baris ini menyimpan nilai yang sudah dipangkas tanpa memicu hook lagi (rekursi dilarang dan interpreter menjaganya).
Perhatikan bahwa tidak ada hook get di sini; pembacaan $budi->nama langsung mengambil backing value apa adanya. Hook bersifat opsional per sisi — pasang hanya yang dibutuhkan. Dari luar, property ini tetap terlihat seperti property biasa: $budi->nama = ' Budi '; — tanpa tanda kurung, tanpa method.
Hook get dan set Bersamaan #
Ketika nilai simpan dan nilai tampil memang berbeda, kedua hook dipasang sekaligus. Contoh berikut menyimpan nama dalam huruf kecil dan menampilkannya dengan huruf pertama kapital:
<?php
class Produk
{
public string $nama {
get {
return ucfirst($this->nama);
}
set (string $nilai) {
$this->nama = strtolower($nilai);
}
}
public function __construct(string $nama)
{
$this->nama = $nama;
}
}
$produk = new Produk('kopi tubruk');
echo $produk->nama, "\n";
$produk->nama = 'TEH HIJAU';
echo $produk->nama, "\n";Output yang Dihasilkan:
Kopi tubruk
Teh hijauConstructor menulis 'kopi tubruk', hook set memaksa huruf kecil, dan hook get mengembalikannya menjadi Kopi tubruk. Penulisan kedua membuktikan rantai yang sama bekerja dari luar class: 'TEH HIJAU' disimpan sebagai teh hijau, lalu terbaca Teh hijau. Penting dicatat bahwa hook berlaku untuk semua penulisan, termasuk dari dalam class — constructor tidak bisa menulis versi mentah. Kedua hook juga punya bentuk ringkas satu baris, misalnya get => ucfirst($this->nama);, yang lazim dipakai ketika body hanya satu ekspresi.
Property Virtual yang Dihitung #
Varian paling menarik: property yang hanya punya hook get dan tidak pernah menyimpan nilai. Setiap pembacaan menghitung hasil dari property lain:
<?php
class Pengguna
{
public string $namaDepan = 'Budi';
public string $namaBelakang = 'Santoso';
// Property virtual: dihitung saat dibaca, tidak menyimpan nilai sendiri
public string $namaLengkap {
get => "{$this->namaDepan} {$this->namaBelakang}";
}
}
$pengguna = new Pengguna();
echo $pengguna->namaLengkap, "\n";
$pengguna->namaBelakang = 'Saputra';
echo $pengguna->namaLengkap, "\n";Output yang Dihasilkan:
Budi Santoso
Budi SaputraProperty $namaLengkap tidak punya backing value — hook get menyusunnya dari dua property nyata setiap kali dibaca, sehingga perubahan $namaBelakang langsung tercermin. Konsekuensinya, property virtual bersifat baca-saja; upaya menulis kepadanya ditolak keras:
<?php
class Pengguna
{
public string $namaLengkap {
get => 'Budi Santoso';
}
}
$pengguna = new Pengguna();
$pengguna->namaLengkap = 'Siti Aminah';Output yang Dihasilkan:
Fatal error: Uncaught Error: Property Pengguna::$namaLengkap is read-only in /home/z/my-project/php-course/content/php/modern-php/property-hooks/.verify_tmp.php:10
Stack trace:
#0 {main}
thrown in /home/z/my-project/php-course/content/php/modern-php/property-hooks/.verify_tmp.php on line 10Penugasan $pengguna->namaLengkap = 'Siti Aminah'; melempar Error: Property Pengguna::$namaLengkap is read-only — tidak ada slot penyimpanan yang bisa dituju, dan interpreter menolak menebak. Pola lama yang setara adalah method bernama getNamaLengkap(), dan memang itulah bedanya yang perlu dipegang: property virtual adalah method yang menyamar jadi property, lengkap dengan jaminan read-only gratisnya.
Kapan Masih Perlu Method #
Property hooks bukan pengganti total method akses. Dua aturan praktis memisahkannya. Pertama, gunakan hook saat logikanya milik data itu sendiri — normalisasi format, caching nilai turunan, penolakan nilai kosong. Kedua, tetap gunakan method ketika operasinya lebih dari sekadar baca/tulis: setNama() yang harus memberi tahu pengamat lain, operasi yang butuh beberapa parameter, atau API yang mungkin berubah bentuknya kelak. Hooks juga tidak bisa dikirim sebagai callable semudah method — meski PHP 8.1 punya sintaks first-class callable foo(...) untuk itu.
Sebagai peta perbandingan tiga gaya:
| Kebutuhan | Gaya lama | Gaya property hooks |
|---|---|---|
| Simpan dengan normalisasi | private + setNama() | hook set + backing value |
| Tampilkan dengan format | getNama() | hook get |
| Nilai turunan tanpa penyimpanan | method getNamaLengkap() | property virtual get-saja |
| Pemanggilan dari luar | $obj->getNama(), $obj->setNama($x) | $obj->nama, $obj->nama = $x |
Ringkasan #
Property hooks menutup jurang yang paling sering dieluhkan pengguna PHP berorientasi object: logika baca/tulis kini menempel pada property alih-alih tercecer sebagai pasangan method. Hook set menjaga penyimpanan tetap bersih, hook get mengatur tampilan, property virtual menghitung nilai tanpa slot penyimpanan, dan semuanya diakses dengan sintaks property biasa dari luar. Fitur PHP 8.4 ini bahkan bisa dikombinasikan dengan fitur 8.4 lainnya untuk mengatur siapa yang boleh menulis — pembahasan yang menjadi topik chapter berikutnya: asymmetric visibility.