Class OOP klasik di PHP menulis data yang sama tiga kali: sekali sebagai deklarasi property, sekali sebagai parameter constructor, dan sekali sebagai assignment di badan constructor. Untuk satu property, pengulangan ini masih bisa ditoleransi; untuk class dengan delapan property, kita menulis dua puluh empat potongan kode yang sebagian besar hanya menyalin nama dan tipe yang sama. PHP 8.0 memangkas ritual tersebut lewat constructor property promotion: satu baris parameter constructor yang sekaligus mendeklarasikan property dan mengisinya. Fitur ini menjadi salah satu fitur modern PHP yang paling cepat diadopsi karena diterapkan begitu saja pada pola yang sudah kita tulis selama bertahun-tahun.
Boilerplate Constructor Sebelum PHP 8.0 #
Mari kita lihat dulu bentuk lama yang ingin digantikan. Class produk berikut menyimpan tiga data: nama, harga, dan stok.
<?php
class ProdukLama
{
public string $nama;
public float $harga;
public int $stok;
public function __construct(string $nama, float $harga, int $stok)
{
$this->nama = $nama;
$this->harga = $harga;
$this->stok = $stok;
}
}
$produk = new ProdukLama("Kopi Gayo", 65000.0, 12);
echo "{$produk->nama}: Rp{$produk->harga} (stok {$produk->stok})\n";Output yang Dihasilkan:
Kopi Gayo: Rp65000 (stok 12)Perhatikan pengulangan yang terjadi:
- Baris 5-7: tiga property dideklarasikan lengkap dengan tipe datanya.
- Baris 9: constructor menerima tiga parameter dengan nama dan tipe yang sama persis.
- Baris 11-13: badan constructor hanya menyalin setiap parameter ke property senamanya.
Total sembilan baris hanya untuk menghubungkan data yang sama. Setiap property baru menambah tiga baris di tiga lokasi berbeda, dan salah urutan assignment seperti $this->stok = $harga tidak akan pernah ditangkap pemeriksa sintaks.
Sintaks Constructor Property Promotion #
Sekarang versi PHP 8.0 ke atas. Letakkan modifier visibility di depan parameter constructor, dan PHP otomatis melakukan ketiga pekerjaan tersebut:
<?php
class Produk
{
public function __construct(
public string $nama,
public float $harga,
public int $stok,
) {}
}
$produk = new Produk("Kopi Gayo", 65000.0, 12);
echo "{$produk->nama}: Rp{$produk->harga} (stok {$produk->stok})\n";Output yang Dihasilkan:
Kopi Gayo: Rp65000 (stok 12)Anatomi satu parameter terpromosi dapat dibedah menjadi tiga bagian:
<?php
class Produk
{
public function __construct(
public string $nama,
protected float $harga,
private int $stok,
) {}
}| Bagian | Peran |
|---|---|
public / protected / private | Visibility property sekaligus penanda bahwa parameter ini dipromosikan |
string | Tipe property, sekaligus tipe parameter constructor |
$nama | Nama property, sekaligus nama parameter constructor |
Ketika konstruktor berjalan, PHP otomatis mengeksekusi $this->nama = $nama untuk setiap parameter terpromosi — persis seperti badan constructor versi lama. Modifier visibility adalah pemicu promotion: parameter tanpa modifier tetap menjadi parameter biasa yang tidak pernah berubah menjadi property. Karena itu, kelas di atas boleh dipakai modul mana pun untuk $nama, sementara $harga hanya boleh diakses turunannya dan $stok hanya dari dalam class Produk sendiri.
Mencampur Parameter Terpromosi dan Parameter Biasa #
Promotion bukan pilihan serba atau serba tidak — kedua gaya parameter boleh dicampur dalam satu constructor. Pola ini berguna ketika sebagian data layak menjadi property sedangkan sebagian lagi hanya input sementara untuk logika konstruktor:
<?php
class Pesanan
{
public string $status;
public function __construct(
public string $nomor,
public readonly DateTimeImmutable $dibuatPada,
string $statusAwal = "menunggu",
) {
$this->status = $statusAwal;
}
}
$pesanan = new Pesanan("INV-2026-001", new DateTimeImmutable("2026-01-15 08:00:00"));
echo $pesanan->nomor . "\n";
echo $pesanan->dibuatPada->format("Y-m-d H:i:s") . "\n";
echo $pesanan->status . "\n";Output yang Dihasilkan:
INV-2026-001
2026-01-15 08:00:00
menungguMari kita bedah campuran di atas:
- Baris 5: property
$statusdideklarasikan manual karena parameternya memang tidak ingin dipromosikan. - Baris 8 (
public string $nomor): parameter terpromosi biasa — menjadi propertypublicyang bisa ditulis ulang. - Baris 9 (
public readonly DateTimeImmutable $dibuatPada): promotion dipadukanreadonly(fitur PHP 8.1), sehingga timestamp pembuatan terkunci setelah konstruktor selesai. PHP juga menerimareadonlytanpa visibility pada parameter terpromosi — hasilnya sama denganpublic readonly— tetapi menulispublic readonlylebih eksplisit dan lebih lazim dijumpai. - Baris 10 (
string $statusAwal = "menunggu"): parameter biasa tanpa modifier, lengkap dengan nilai default; nilai ini tidak otomatis mengisi property apa pun. - Baris 12: badan constructor tetap dieksekusi setelah semua assignment otomatis —
$this->status = $statusAwaladalah jembatan manual dari parameter biasa ke property yang dideklarasikan manual.
Ada satu batasan tipe yang perlu diingat: callable tidak boleh menjadi tipe property, sehingga parameter terpromosi bertipe callable langsung ditolak PHP:
<?php
class ContohCallable
{
public function __construct(public callable $proses) {}
}Output yang Dihasilkan:
Fatal error: Property ContohCallable::$proses cannot have type callable in /home/z/my-project/php-course/content/php/advanced-object-model/constructor-property-promotion/.verify_tmp.php on line 5Pesan Property ContohCallable::$proses cannot have type callable menegaskan aturan tersebut. Jika Anda perlu menyimpan callback, gunakan tipe Closure atau interface lain yang mendeskripsikan kontrak dengan lebih jelas.
Promotion Bertemu readonly Class #
Bab Readonly Classes
memperlihatkan bahwa DTO modern PHP cukup ditulis dalam satu blok deklarasi. Itu hanya mungkin karena dua fitur ini bekerja sama: readonly class mengunci, promotion mendeklarasikan dan mengisi:
<?php
readonly class DataPengguna
{
public function __construct(
public string $nama,
public string $email,
public array $peran,
) {}
}
$pengguna = new DataPengguna("Budi", "[email protected]", ["member"]);
echo $pengguna->nama . " <" . $pengguna->email . ">\n";
echo implode(", ", $pengguna->peran) . "\n";Output yang Dihasilkan:
Budi <[email protected]>
memberTujuh baris deklarasi menghasilkan DTO dengan tiga property terkunci yang terisi otomatis — bandingkan dengan versi lama yang membutuhkan deklarasi property, daftar parameter, dan blok assignment terpisah.
Ringkasan #
Constructor property promotion menggabungkan deklarasi property, parameter constructor, dan assignment menjadi satu baris bertanda visibility. Parameter terpromosi dan parameter biasa bisa dicampur sesuai kebutuhan, nilai default parameter ikut menjadi nilai awal property, dan callable menjadi satu-satunya tipe yang dilarang. Dipadukan dengan readonly class, fitur ini menghasilkan DTO yang ringkas sekaligus terkunci. Hingga titik ini property kita masih polos: ditulis begitu saja, tersimpan begitu saja. PHP 8.4 memungkinkan logika get dan set menempel langsung pada property melalui property hooks, topik chapter berikutnya.