PHP lahir sebagai bahasa yang sangat dinamis: variabel bisa berganti tipe kapan saja dan fungsi menerima apa saja. Seiring waktu, PHP tumbuh menjadi bahasa yang bertahap bertipe (gradually typed): type declaration kini bisa hadir di parameter, return, property, hingga class constant, dan mesin PHP menegakkannya sungguh-sungguh saat runtime. Section ini membahas type system PHP dari dua sisi — deklarasi yang ditegakkan interpreter, dan static analysis yang membuktikan kebenaran kode sebelum kode dijalankan. Chapter pembuka ini memetakan medannya: di mana tipe boleh ditulis, apa yang dijaminnya, bagaimana mode lemah dan ketat bekerja, serta kategori tipe apa saja yang tersedia.

Empat Lokasi Type Declaration #

Type declaration pada dasarnya bisa menempel pada empat lokasi. Sebelum PHP 7, tipe hanya bisa berupa class, interface, atau array; kini hampir semua jenis tipe tersedia di semua lokasi. Tabel berikut merangkum kapan tiap lokasi mulai menerima deklarasi:

LokasiMulai tersediaContoh
Parameter fungsi/methodPHP 5.0 (class/array), 7.0 (scalar)function kali(int $a): int
Return valuePHP 7.0function kali(int $a): int
Property classPHP 7.4public string $nama;
Class constantPHP 8.3const float KURS_USD = 16500.0;

Contoh berikut memakai keempat lokasi sekaligus dalam satu class kecil:

php
<?php
class KursProduk
{
    const float KURS_USD = 16500.0; // typed class constant (PHP 8.3)

    public function __construct(
        private string $nama, // typed property (PHP 7.4)
    ) {}

    public function konversi(int $jumlah): string // parameter + return type
    {
        $rupiah = $jumlah * self::KURS_USD;
        return $this->nama . ": Rp" . number_format($rupiah, 0, ",", ".");
    }
}

$kurs = new KursProduk("Harga kopi");
echo $kurs->konversi(25) . "\n";

Output yang Dihasilkan:

text
Harga kopi: Rp412.500

Mari kita bedah lokasi-lokasi tipe pada contoh tersebut:

  1. Baris 4 — const float KURS_USD adalah typed class constant (fitur PHP 8.3): nilai konstanta dipaksa berjenis float sejak deklarasi, dan editor dapat mengandalkan tipe tersebut.
  2. Baris 7 — private string $nama adalah typed property (PHP 7.4) yang sekaligus dipromosikan lewat constructor: penugasan dengan nilai bukan string akan memicu TypeError.
  3. Baris 10 — method konversi() mendeklarasikan dua tipe sekaligus: parameter $jumlah wajib int, dan nilai kembalian wajib string.
  4. Baris 12-13 — hasil perkalian int * float berupa float, lalu number_format() mengembalikannya sebagai string sesuai kontrak return type.

Apa yang Dijamin saat Runtime #

Type declaration dijamin pada batas-batas tertentu: nilai dicek saat masuk (parameter), saat keluar (return), dan saat disimpan (property maupun konstanta). Pelanggaran memicu TypeError yang menghentikan eksekusi — kecuali koersi masih mungkin dilakukan dalam mode lemah yang akan kita bahas sebentar lagi. Namun perlu dipahami batas jaminannya: tipe menjamin bentuk, bukan makna. Deklarasi array tidak mengatakan apa isi array tersebut; int tidak menjamin nilainya positif; string tidak menjamin teksnya valid.

Mode Lemah dan Mode Ketat #

Perilaku pemeriksaan tipe untuk nilai skalar ditentukan per-berkas oleh deklarasi declare(strict_types=1). Tanpa deklarasi tersebut — mode lemah — PHP mengonversi (coercion) nilai skalar yang cukup cocok ke tipe yang diminta: string numerik menjadi int, int menjadi string, dan seterusnya. Dengan deklarasi tersebut — mode ketat — konversi tidak pernah terjadi; tipe harus sudah persis. Kedua mode ini telah dibahas tuntas pada chapter Strict Types dalam Fungsi ; di sini kita segarkan intuisinya karena seluruh section ini memakai mode ketat sebagai asumsi kerja.

php
<?php
// Mode lemah (default): string numerik dikonversi otomatis menjadi int
function kaliDua(int $angka): int
{
    return $angka * 2;
}

echo kaliDua("5") . "\n";

Output yang Dihasilkan:

text
10

String "5" lolos masuk karena mode lemah mengonversinya menjadi int 5 sebelum fungsi berjalan. Kenyamanan ini ada harganya: kesalahan tipe di sisi pemanggil bisa lolos sebagai konversi diam-diam. Bandingkan dengan mode ketat berikut:

php
<?php
declare(strict_types=1);

// Mode ketat: tidak ada koersi string → int
function kaliDua(int $angka): int
{
    return $angka * 2;
}

try {
    echo kaliDua("5") . "\n";
} catch (TypeError $e) {
    $pesan = explode(", called", $e->getMessage())[0];
    echo "TypeError: " . $pesan . "\n";
}

Output yang Dihasilkan:

text
TypeError: kaliDua(): Argument #1 ($angka) must be of type int, string given

Pemanggilan dengan string kini ditolak keras: TypeError dilempar dan ditangkap blok try-catch. Pesan lengkap dari getMessage() menyertakan berkas serta nomor baris pemanggilan, sehingga explode(", called", ...) pada contoh memotong bagian itu agar keluaran mudah dibaca — inti pesannya tetap utuh: argumen pertama harus int, yang dikirim string.

Peta Kategori Tipe yang Tersedia #

Vokabular tipe PHP bertambah besar dalam enam tahun terakhir. Tabel berikut memetakannya sekaligus menunjuk lokasi pembahasan mendalam:

KategoriPenulisanSejakBahasan mendalam
Tipe tunggalint, float, string, bool, array, object, callable, iterable, mixed—Tipe Data dan Type System
Nullable?string atau string|null7.1Nullable Types
Unionint|float8.0Union Types
IntersectionCountable&Iterator8.1Intersection Types
DNF(Countable&Iterator)|null8.2Union Types
Literaltrue, false, null berdiri sendiri8.2Literal Types
Khususvoid, never, self, static, parent, nama class/enum—Never dan Void , Return Types

Kategori tipe tunggal adalah fondasi yang dipakai sehari-hari; nullable dan union menutup kasus “boleh kosong” dan “salah satu dari beberapa”; intersection memilih kasus “harus memenuhi semuanya”; DNF menggabungkan keduanya dalam satu ekspresi; sedangkan tipe literal menyempitkan bool menjadi nilai yang sudah pasti. Perilaku koersi antar tipe — bagaimana "5" bisa menjadi 5 — dibahas pada Type Juggling , dan galeri khusus union, intersection, serta nullable versi section ini ada di chapter Union, Intersection, dan Nullable Types .

Tipe sebagai Dokumentasi dan Kontrak Tooling #

Manfaat type declaration tidak berhenti di pemeriksaan runtime. Deklarasi tipe adalah dokumentasi yang tidak pernah kedaluwarsa: pembaca kode tahu persis nilai apa yang masuk dan keluar tanpa harus membaca isi fungsi. Editor dan IDE memakainya untuk autocomplete serta peringatan dini, sedangkan alat static analysis seperti PHPStan dan Psalm memakainya sebagai fakta untuk membuktikan ketiadaan kesalahan tipe di seluruh titik panggilan — sebelum program dijalankan. Ketika informasi yang dibutuhkan melebihi kemampuan deklarasi native, anotasi PHPDoc melengkapi:

php
<?php
/**
 * @param array<string, int> $stok peta nama produk => sisa stok
 */
function totalSisa(array $stok): int
{
    return array_sum($stok);
}

echo totalSisa(["Kopi Gayo" => 2, "Teh Melati" => 3]) . "\n";

Output yang Dihasilkan:

text
5

Perhatikan pembagian peran pada contoh tersebut:

  1. Tipe native — array $stok hanya mampu menyatakan “argumen harus berupa array”; runtime berhenti di situ.
  2. Anotasi PHPDoc — @param array<string, int> menegaskan isi array: kunci bertipe string (nama produk) dan nilai bertipe int (sisa stok).
  3. Siapa yang membaca apa — interpreter mengabaikan PHPDoc, tetapi manusia, editor, PHPStan, dan Psalm memakainya untuk memverifikasi panggilan seperti totalSisa(["Teh" => "dua belas"]) sebagai kesalahan sebelum runtime.

Bahasa anotasi ini dibedah tuntas di chapter PHPDoc Types , dan praktik analisisnya di Menggunakan PHPStan serta Menggunakan Psalm .

Referensi #

Ringkasan #

Type declaration kini hadir di empat lokasi — parameter, return, property, dan class constant — dan dijamin pada batas-batas runtime, sepanjang kita paham bahwa yang dijaga adalah bentuk, bukan makna. Mode lemah dan ketat menentukan seberapa longgar koersi skalar terjadi, dengan declare(strict_types=1) sebagai pengalih per-berkas. Kategori tipe yang tersedia membentang dari tipe tunggal sampai DNF, dan seluruhnya berfungsi ganda: ditegakkan interpreter sekaligus dibaca static analysis sebagai kontrak. Dengan peta ini di tangan, kita masuk ke lokasi yang paling sering Anda tulis: parameter types, dilihat dari sudut pandang sistem tipe — tempat kontrak mulai berinteraksi dengan inheritance.