Pada dua Chapter sebelumnya, kita mengisi property object satu per satu setelah object diciptakan dengan new. Cara tersebut menuntut setiap pemanggil mengingat property apa saja yang wajib diisi — lupa satu baris, dan object beredar dalam kondisi setengah jadi. PHP menyediakan satu method istimewa untuk menyelesaikan masalah ini: __construct, yang berjalan otomatis tepat setelah object dicetak. Pada Chapter ini kita akan mempelajari cara kerja constructor, urutannya terhadap nilai default property, bentuk singkatnya bernama constructor promotion, sampai cara memanggil constructor milik class induk.

Method Khusus __construct #

Nama __construct adalah nama yang dicari PHP secara otomatis; method ini tidak pernah dipanggil manual. Argumen yang kita tulis setelah nama class akan diteruskan langsung ke constructor. Mari kita ubah class Produk dari Chapter sebelumnya:

php
<?php
class Produk
{
    public string $nama;
    public int $harga;

    public function __construct(string $nama, int $harga)
    {
        $this->nama = $nama;
        $this->harga = $harga;
    }

    public function ringkas(): string
    {
        return "{$this->nama} (Rp{$this->harga})";
    }
}

$kopi = new Produk("Kopi Gayo", 85000);
$gula = new Produk("Gula Aren", 15000);

echo $kopi->ringkas() . "\n";
echo $gula->ringkas() . "\n";

Output yang Dihasilkan:

text
Kopi Gayo (Rp85000)
Gula Aren (Rp15000)

Mari kita telaah baris-baris pentingnya:

  1. Baris 4-5: dua property dideklarasikan tanpa nilai default karena nilainya baru sah bila datang dari pemanggil.
  2. Baris 7-11: constructor menerima $nama dan $harga lalu menyalin keduanya ke property object melalui $this. Perhatikan bahwa nama parameter sengaja dibuat sama dengan nama property — praktik yang lazim, karena $this->nama = $nama; terbaca sebagai “isi property nama milik object dengan parameter nama”.
  3. Baris 19-20: new Produk(...) kini membawa argumen. Pada saat baris ini dieksekusi, PHP mencetak object, menjalankan __construct dengan argumen tersebut, dan hanya setelah itu object dianggap siap.
  4. Baris 22-23: kedua object langsung bisa dipakai penuh tanpa satu baris pengisian manual pun.

Constructor adalah method biasa dalam hal parameter: boleh punya nilai default, tipe, dan jumlah argumen berapa pun. Namun constructor tidak boleh dipanggil ulang secara manual pada object yang sudah jadi — tugasnya memang hanya sekali di awal kehidupan object.

Konstruktor Menjamin State Awal #

Kekuatan constructor bukan sekadar menghemat baris, melainkan menjadikan data wajib sebagai kontrak. Bandingkan dengan cara manual di Chapter Memahami Class dan Object: tidak ada apa pun yang memaksa $laptop->harga diisi, sehingga object tanpa harga bisa lolos entah ke mana. Dengan constructor, pemanggil harus menyertakan nama dan harga saat menciptakan Produk.

PHP turut menegakkan kontrak ini. Jika argumen kurang, PHP melempar ArgumentCountError; jika tipe argumen salah, PHP melempar TypeError — perilaku yang persis sama dengan function biasa yang sudah kita bahas di Section Fungsi dan Section Error Handling . Konsep fail fast seperti ini berharga: data tidak valid tertolak di titik penciptaan, bukan menyebar diam-diam lalu meledak jauh di tengah program.

Prinsip kerjanya sederhana: sebaiknya constructor menolak menciptakan object yang tidak berarti. Class Produk tanpa nama memang tidak berarti, maka namanya wajib. Sebaliknya, data yang memang opsional cukup diberi nilai default — contohnya parameter $harga = 0 yang akan kita lihat sebentar lagi.

Urutan Nilai Default dan Isi Constructor #

Apa yang terjadi bila property punya nilai default sekaligus constructor menimpanya? Nilai default dipasang terlebih dahulu, kemudian badan constructor berjalan. Kita bisa membuktikan urutan ini dari dalam constructor:

php
<?php
class Tugas
{
    public string $status = "baru";
    public string $judul;

    public function __construct(string $judul)
    {
        $this->judul = $judul;
        echo "Nilai status di awal constructor: {$this->status}\n";
        $this->status = "aktif";
    }
}

$tugas = new Tugas("Belajar OOP");
echo "Nilai status setelah constructor: {$tugas->status}\n";

Output yang Dihasilkan:

text
Nilai status di awal constructor: baru
Nilai status setelah constructor: aktif

Baris 4 memberi property $status nilai default "baru". Ketika new Tugas("Belajar OOP") berjalan, PHP memasang nilai default itu lebih dulu, sehingga baris 10 — yang berada di dalam constructor — masih membaca baru. Baris 11 kemudian menimpanya menjadi aktif, dan itulah nilai yang terbaca setelah penciptaan selesai. Urutan tetap sama berapa pun banyaknya property: semua nilai default terpasang, baru badan constructor dieksekusi.

Constructor Promotion sebagai Preview #

Polah tulis $this->x = $x berulang-ulang begitu umum sampai PHP 8.0 menyediakan jalan pintas resmi: tambahkan kata kunci public pada parameter constructor, dan PHP otomatis mendeklarasikan property sekaligus mengisinya. Perhatikan perbandingannya:

php
<?php
class Produk
{
    public function __construct(
        public string $nama,
        public int $harga = 0,
    ) {
    }

    public function ringkas(): string
    {
        return "{$this->nama} (Rp{$this->harga})";
    }
}

$kopi = new Produk("Kopi Gayo", 85000);
$teh = new Produk("Teh Melati");

echo $kopi->ringkas() . "\n";
echo $teh->ringkas() . "\n";

Output yang Dihasilkan:

text
Kopi Gayo (Rp85000)
Teh Melati (Rp0)

Tubuh constructor pada baris 7 kini kosong karena semua pekerjaan dilakukan oleh deklarasi parameter di baris 4-6: public string $nama berarti “deklarasikan property $nama bertipe string, lalu isi dengan parameter ini”. Promoted parameter tetap parameter biasa — boleh punya nilai default seperti $harga = 0 di baris 6, dan pemanggil tetap mengirim argumen seperti biasa di baris 16-17. Versi panjang di bagian pertama Chapter ini dan versi pendek ini menghasilkan class yang perilakunya identik.

Constructor yang Ringan dan Dependency #

Constructor idealnya hanya menata data, bukan bekerja berat. Mengapa? Karena constructor berjalan pada momen penciptaan — bila isinya mengirim permintaan jaringan atau membaca berkas besar, sekadar membuat object pun menjadi lambat dan sulit diuji. Pola yang sehat: hitung atau ambil sesuatu di luar, lalu serahkan hasilnya ke constructor sebagai dependency:

php
<?php
class Logger
{
    public function catat(string $pesan): void
    {
        echo "[LOG] $pesan\n";
    }
}

class PemesananService
{
    public Logger $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }

    public function buat(string $nomor): void
    {
        $this->logger->catat("Pemesanan $nomor dibuat");
        echo "Pemesanan $nomor siap dipakai\n";
    }
}

$service = new PemesananService(new Logger());
$service->buat("PSN-001");

Output yang Dihasilkan:

text
[LOG] Pemesanan PSN-001 dibuat
Pemesanan PSN-001 siap dipakai

Class PemesananService pada baris 10-24 tidak membuat loggernya sendiri, melainkan menerimanya lewat constructor (baris 14-17) lalu menyimpannya di property $logger. Baris 26 memperlihatkan cara pakainya: object Logger diciptakan di pemanggil, lalu disuntikkan ke dalam PemesananService. Pola menerima kolaborator dari luar ini adalah akar dari konsep dependency injection yang akan sering kita jumpai pada framework — untuk sekarang, cukup pahami bahwa constructor adalah tempat menyerahkan alat kerja, bukan tempat mengoperasikannya.

Memanggil Constructor Induk #

Saat sebuah class mewarisi class lain dengan extends, class anak perlu menginisialisasi data milik induknya juga. PHP tidak menjalankan constructor induk secara otomatis; kita memanggilnya sendiri dengan parent::__construct():

php
<?php
class Produk
{
    public function __construct(public string $nama)
    {
        echo "Constructor Produk berjalan untuk '{$this->nama}'\n";
    }
}

class Buku extends Produk
{
    public function __construct(string $nama, public int $jumlahHalaman)
    {
        parent::__construct($nama);
        echo "Constructor Buku berjalan\n";
    }

    public function info(): string
    {
        return "{$this->nama} - {$this->jumlahHalaman} halaman";
    }
}

$buku = new Buku("Belajar PHP Modern", 320);
echo $buku->info() . "\n";

Output yang Dihasilkan:

text
Constructor Produk berjalan untuk 'Belajar PHP Modern'
Constructor Buku berjalan
Belajar PHP Modern - 320 halaman

Baris 10 mendeklarasikan Buku sebagai anak dari Produk — mekanisme inheritance akan kita bahas tuntas pada Chapter Inheritance , jadi anggaplah sejenak bahwa Buku mewarisi seluruh anggota Produk. Constructor Buku menerima dua data, namun hanya jumlahHalaman yang bisa ia kelola sendiri lewat promotion; nama diserahkan ke induk lewat parent::__construct($nama) di baris 14. Urutan output membuktikan urutan jalannya: constructor Produk lebih dulu, constructor Buku menyusul, lalu object lengkap dipakai.

Ringkasan #

Constructor __construct berjalan otomatis saat penciptaan object, menjadikan data wajib sebagai kontrak yang ditegakkan PHP lewat ArgumentCountError dan TypeError. Nilai default property terpasang lebih dulu, diikuti badan constructor; promoted parameter menyusun deklarasi dan pengisian property dalam satu baris. Constructor terbaik adalah yang ringan: menerima dependency dari luar, menugaskannya ke property, dan menolak data tidak valid sejak awal. Seluruh pembicaraan kita sebenarnya mengarah ke satu prinsip — object yang valid sejak lahir — dan prinsip itulah yang kita bedah secara khusus pada Chapter berikutnya.

Referensi #