Sejauh ini seluruh property dan method kita memakai kata kunci public — siapa pun boleh membaca dan menimpanya langsung. Pada program kecil hal itu terasa praktis, tetapi bayangkan sebuah class Rekening yang saldonya bisa ditulis bebas dari luar: nilai negatif, selisih yang tidak tercatat, atau transfer yang melewati seluruh aturan bisnis. Visibility adalah mekanisme OOP untuk mengendalikan siapa yang boleh mengakses anggota sebuah class. Pada Chapter ini kita akan mempelajari tiga tingkatnya — public, protected, dan private — beserta pola getter/setter yang menjadikannya berguna.
Tiga Tingkat Visibility #
Kata kunci visibility ditulis sebelum deklarasi property atau method, dan menentukan dari mana anggota tersebut boleh diakses:
| Kata kunci | Dari class sendiri | Dari subclass | Dari luar class |
|---|---|---|---|
public | Bisa | Bisa | Bisa |
protected | Bisa | Bisa | Tidak bisa |
private | Bisa | Tidak bisa | Tidak bisa |
Mari kita lihat ketiganya bekerja dalam satu class. Method info() berada di dalam class, jadi method tersebut bebas membaca ketiga property:
<?php
class Rekening
{
public string $pemilik = "Budi";
protected int $saldo = 50000;
private string $nomor = "REQ-0001";
public function info(): string
{
return "{$this->pemilik} - {$this->nomor} - saldo Rp{$this->saldo}";
}
}
$rekening = new Rekening();
echo $rekening->info() . "\n";
echo $rekening->pemilik . "\n";Output yang Dihasilkan:
Budi - REQ-0001 - saldo Rp50000
BudiBaris 8-11 membuktikan bahwa dari dalam class, ketiga tingkat visibility sama-sama bisa diakses — info() merangkai $pemilik, $nomor, hingga $saldo tanpa hambatan. Baris 16 membaca property public dari luar class dan berhasil. Dua property lainnya tidak kita coba akses dari luar karena barulah batasnya terlihat — itulah topik bagian berikutnya.
private Hanya untuk Dalam Class yang Sama #
Property private hanya bisa diakses oleh kode yang berada secara literal di dalam class yang mendeklarasikannya. Mencoba membacanya dari luar bukan sekadar peringatan, melainkan galat fatal:
<?php
class Rekening
{
private string $nomor = "REQ-0001";
}
$rekening = new Rekening();
try {
echo $rekening->nomor;
} catch (Error $e) {
echo "Galat: {$e->getMessage()}\n";
}Output yang Dihasilkan:
Galat: Cannot access private property Rekening::$nomorBaris 10 mencoba membaca $rekening->nomor dari ruang lingkup luar, dan PHP melempar Error: Cannot access private property. Kami membungkusnya dengan try/catch agar pesan galat bisa ditampilkan rapi alih-alih menghentikan program — teknik penangkapan yang sudah kita kuasai di Section Error Handling
. Perlakuan protected dari luar class juga sama: ditolak. Bedanya baru muncul bila ada subclass, yang akan kita lihat pada bagian khusus di bawah.
Getter dan Setter sebagai Pintu Terkontrol #
Lalu bagaimana kode luar membaca data private? Lewat method public yang kita rancang sendiri — pola yang dikenal sebagai getter (pembaca) dan setter (penulis). Kekuatan pola ini bukan pada kemampuan mengakses, melainkan pada kesempatan memvalidasi setiap perubahan:
<?php
class Produk
{
private int $harga = 0;
public function getHarga(): int
{
return $this->harga;
}
public function setHarga(int $harga): void
{
if ($harga < 0) {
throw new InvalidArgumentException("Harga tidak boleh negatif: {$harga}");
}
$this->harga = $harga;
}
}
$produk = new Produk();
$produk->setHarga(85000);
echo "Harga: Rp" . $produk->getHarga() . "\n";
try {
$produk->setHarga(-1000);
} catch (InvalidArgumentException $e) {
echo "Galat: {$e->getMessage()}\n";
}Output yang Dihasilkan:
Harga: Rp85000
Galat: Harga tidak boleh negatif: -1000Mari kita bedah baris-baris pentingnya:
- Baris 4: property
$hargadibuatprivatesehingga satu-satunya jalan mengubahnya adalah lewatsetHarga(). - Baris 6-9: getter
getHarga()hanya mengembalikan nilai — pintu baca yang sengaja tanpa syarat. - Baris 11-18: setter memeriksa aturan bisnis lebih dulu. Harga negatif ditolak dengan
InvalidArgumentException(class exception bawaan PHP untuk argumen tidak valid), harga sah baru disimpan di baris 17. - Baris 21-23: alur normal berjalan — harga 85000 diterima lalu terbaca kembali.
- Baris 25-29: upaya menyetel -1000 tertangkap dan ditolak dengan pesan yang jelas.
Berkat pagar ini, tidak ada satu baris kode pun di seluruh program yang bisa menciptakan produk dengan harga negatif tanpa melalui pemeriksaan. Sebagai catatan perkembangan bahasa: PHP 8.4 memperkenalkan property hooks yang memungkinkan logika baca-tulis ditempel langsung pada property — kita sempatkan pembahasannya di Section Advanced Object Model; untuk fondasi, getter/setter tetap pola yang perlu dikuasai.
protected untuk Subclass #
Tingkat protected menempati posisi tengah: tertutup dari luar seperti private, namun terbuka untuk subclass — class yang mewarisi class tersebut lewat extends. Kata kunci inheritance akan kita bahas tuntas pada Chapter Inheritance
; untuk sekarang cukup pahami bahwa subclass mewarisi seluruh anggota induknya:
<?php
class Hewan
{
protected string $suara = "belum ada suara";
protected function bunyi(): string
{
return $this->suara;
}
}
class Kucing extends Hewan
{
public function perkenalkan(): string
{
$this->suara = "meong";
return "Kucing berkata: " . $this->bunyi();
}
}
$kucing = new Kucing();
echo $kucing->perkenalkan() . "\n";Output yang Dihasilkan:
Kucing berkata: meongClass Kucing mengakses $this->suara dan memanggil $this->bunyi() di baris 16 dan 18 — keduanya anggota protected milik Hewan, dan akses tersebut sah karena Kucing adalah subclass. Bandingkan dengan kode luar: $kucing->suara dari baris 23 akan ditolak sama kerasnya seperti private. Jadi protected adalah pilihan yang tepat bila Anda merancang class yang memang dimaksudkan untuk diturunkan, sambil tetap menutup akses dari kode lain.
Mengapa Menyembunyikan Data Itu Penting #
Motivasi di balik visibility sering disebut information hiding atau encapsulation: class menyembunyikan detail penyimpanan datanya dan hanya menjanjikan perilaku lewat method. Manfaatnya konkret. Pertama, invarian terjaga — aturan seperti “harga tidak pernah negatif” atau “saldo tidak pernah minus” cukup ditegakkan di satu tempat karena tidak ada jalan lain mengubah data. Kedua, bebas mengubah isi: bila suatu saat $harga perlu disimpan dalam sen alih-alih rupiah, hanya method di dalam class yang perlu disesuaikan; seluruh kode pemakai yang hanya bertumpu pada getHarga() tidak tersentuh.
Sebaliknya, property public yang dipakai di sepuluh tempat berarti setiap perubahan bentuk data berpotensi menyentuh sepuluh tempat itu. Panduan ringkasnya seperti ini:
privateuntuk keadaan dalam (internal state) yang bukan urusan pihak luar — pilihan default yang sehat.protecteduntuk anggota yang sengaja disediakan bagi subclass, misalnya hook kustomisasi.publicuntuk antarmuka yang dijanjikan stabil kepada pemakai class.
Jebakan Magic Method __get dan __set #
Ada satu pintu belakang yang perlu Anda kenali agar tidak tergoda memakainya sebagai pengganti visibility. PHP menyediakan magic method __get() dan __set() yang berjalan otomatis ketika property yang tidak bisa diakses (misalnya private dari luar, atau belum dideklarasikan) dibaca atau ditulis:
<?php
class KantongSihir
{
private array $isi = [];
public function __get(string $nama): mixed
{
return $this->isi[$nama] ?? null;
}
public function __set(string $nama, mixed $nilai): void
{
$this->isi[$nama] = $nilai;
}
}
$kantong = new KantongSihir();
$kantong->merek = "Anka";
$kantong->ukuran = "sedang";
echo $kantong->merek . "\n";
echo $kantong->ukuran . "\n";
var_dump($kantong->warna);Output yang Dihasilkan:
Anka
sedang
NULLProperty $merek dan $ukuran tidak pernah dideklarasikan, sehingga penulisan di baris 18-19 dialihkan ke __set() dan penyimpanannya ditampung array $isi. Pembacaan di baris 21-22 mengalir ke __get(). Kelihatannya praktis — class apa pun bisa menerima property apa saja — tetapi perhatikan baris 23: membaca $warna yang memang tidak pernah ada juga “berhasil” dengan tenang mengembalikan NULL. Salah ketik nama property, lupa mengisi, atau data yang hilang — semuanya larut menjadi null tanpa suara.
Ringkasan #
Visibility mengatur radius akses setiap anggota class: public terbuka untuk semua, protected terbuka untuk class dan subclass-nya, dan private hanya untuk class penyimpannya. Kombinasi property private dengan getter/setter public memindahkan semua perubahan data melalui pintu yang bisa memvalidasi, sehingga invarian class terjaga dan strukturnya bebas berevolusi. Hindari jalan pintas __get/__set untuk data yang bentuknya pasti. Dengan data kini terlindungi, kita siap melangkah ke hubungan antar-class — bagaimana sebuah class mewarisi class lain lewat inheritance pada Chapter berikutnya.