Intersection types pada Chapter sebelumnya menuntut sebuah nilai memenuhi banyak interface sekaligus; sekarang kita melompat ke ujung spektrum yang berlawanan — tipe yang hanya menerima satu nilai spesifik. Pada bahasa seperti TypeScript, kita bisa menulis tipe 'asc' | 'desc' yang hanya menerima dua string tersebut, dan tipe semacam itu disebut literal types. PHP 8.2 menghadirkan sebagian dari kemampuan itu: true, false, dan null kini boleh berdiri sendiri sebagai tipe, sementara literal string dan integer tetap menjadi urusan PHPDoc beserta alat analisis statis. Pada Chapter ini kita memetakan dengan jujur apa yang bisa ditegakkan oleh bahasa dan apa yang diserahkan kepada alat.
true, false, dan null Berdiri Sendiri #
Sebelum PHP 8.2, true dan false hanyalah nilai dari tipe bool, bukan tipe tersendiri. Kini keduanya — bersama null — sah ditulis sebagai type declaration. Deklarasi : true dibaca sebagai “fungsi ini hanya boleh mengembalikan nilai true”, pola yang populer untuk fungsi yang berhasil kembali dengan true atau gagal dengan melempar error:
<?php
function simpanKonfigurasi(array $data): true
{
// implementasi nyata menulis ke berkas atau database;
// bila gagal, fungsi ini melempar error — bukan mengembalikan false
return true;
}
function cariDiCache(string $kunci): false
{
// cache memori belum diaktifkan: pencarian selalu gagal
return false;
}
function penanganSenyap(): null
{
// callback ini dipanggil hanya untuk efek sampingnya
return null;
}
var_dump(simpanKonfigurasi(["tema" => "gelap"]));
var_dump(cariDiCache("tema"));
var_dump(penanganSenyap());Output yang Dihasilkan:
bool(true)
bool(false)
NULLMari kita bedah ketiga fungsi di atas. simpanKonfigurasi() berjanji mengembalikan true — satu-satunya nilai yang diizinkan — sehingga pemanggil tidak perlu lagi memeriksa hasilnya dengan === false. cariDiCache() adalah kebalikannya: stub yang jujur karena cache memang belum aktif, sehingga setiap pencarian pasti gagal. penanganSenyap() menutup daftar dengan null berdiri sendiri, cocok untuk callback yang dipanggil semata demi efek sampingnya.
Dalam praktik, false dan null lebih sering berada di dalam union — ingat string|false pada Chapter Union Types
dan ?string yang setara dengan string|null pada Chapter Nullable Types
. Bentuk berdiri sendiri tetap berguna ketika satu nilai memang satu-satunya kemungkinan, dan deklarasi seperti ini juga berlaku untuk parameter:
<?php
function setModeMinimal(true $aktif): string
{
return $aktif ? "mode minimal aktif" : "tidak terjangkau";
}
var_dump(setModeMinimal(true));Output yang Dihasilkan:
string(18) "mode minimal aktif"Parameter true $aktif hanya menerima nilai true — bukan false, bukan 1, bukan nilai lain apa pun. Cabang "tidak terjangkau" pada kondisi pun tidak akan pernah dijalankan; ia hanya ditulis agar kondisi terbaca lengkap.
Penegakan oleh Runtime #
PHP tidak sekadar membiarkan tipe bernilai tunggal ini sebagai hiasan; runtime menegakkannya sungguh-sungguh. Simpan berikut sebagai kunci.php lalu jalankan:
<?php
function simpanKonfigurasi(array $data): true
{
return "tersimpan";
}
var_dump(simpanKonfigurasi(["tema" => "gelap"]));Output yang Dihasilkan:
Fatal error: Uncaught TypeError: simpanKonfigurasi(): Return value must be of type true, string returned in /home/z/my-project/php-course/content/php/tipe-data-dan-type-system/literal-types/.verify_tmp.php:4
Stack trace:
#0 /home/z/my-project/php-course/content/php/tipe-data-dan-type-system/literal-types/.verify_tmp.php(7): simpanKonfigurasi(Array)
#1 {main}
thrown in /home/z/my-project/php-course/content/php/tipe-data-dan-type-system/literal-types/.verify_tmp.php on line 4Pesan TypeError di atas menegaskan kontraknya: nilai kembali harus bertipe true, tetapi yang diserahkan adalah string. Perhatikan bedanya dengan pelanggaran parameter — pesannya menyebut Return value must be of type ... dan menunjuk baris pernyataan return yang melanggar, bukan baris pemanggilan. Pelanggaran pada sisi parameter pun ditolak dengan gaya yang sama: memanggil setModeMinimal(false) menghasilkan pesan Argument #1 ($aktif) must be of type true, false given.
PHP Tidak Punya Literal String dan Integer #
Setelah melihat true dan false bekerja, pertanyaan alami berikutnya: apakah kita bisa menulis 'asc'|'desc' langsung sebagai tipe? Jawabannya tidak — dan PHP menolaknya sejak tahap penerjemahan. Simpan berikut sebagai urut.php:
<?php
function urutkanNama(array $nama, 'asc'|'desc' $arah): array
{
return $nama;
}Output yang Dihasilkan:
Parse error: syntax error, unexpected single-quoted string "asc", expecting variable in /home/z/my-project/php-course/content/php/tipe-data-dan-type-system/literal-types/.verify_tmp.php on line 2Parser PHP hanya menerima nama tipe pada type declaration — int, string, bool, nama class, true, false, null — dan menolak nilai mentah seperti 'asc'. Dengan kata lain, PHP pada level bahasa tidak memiliki literal types untuk string dan integer. Fitur sebaliknya justru berlaku untuk boolean: karena true dan false sudah menjadi bagian dari bool, menggabungkan keduanya dianggap berlebihan dan ditolak keras sejak penerjemahan:
<?php
function proses(bool|true $nilai): void
{
}Output yang Dihasilkan:
Fatal error: Duplicate type true is redundant in /home/z/my-project/php-course/content/php/tipe-data-dan-type-system/literal-types/.verify_tmp.php on line 2Aturan lengkapnya lugas: bool|true ditolak karena Duplicate type true is redundant, dan true|false ditolak dengan pesan Type contains both true and false, bool must be used instead — cukup tulis bool saja. Dengan dua penolakan tersebut, peta kemampuan bahasa menjadi jelas: nilai tunggal true/false/null ditegakkan, sedangkan literal string dan integer tidak tersedia pada level bahasa.
Literal Types lewat PHPDoc #
Karena bahasa tidak menyediakannya, komunitas PHP menempuh jalur lain: mendeklarasikan literal types di dalam PHPDoc — blok komentar /** ... */ di atas fungsi — lalu membiarkan alat analisis statis seperti PHPStan atau Psalm yang menegakkannya:
<?php
/**
* Mengurutkan daftar nama pengguna.
*
* @param 'asc'|'desc' $arah Arah pengurutan: naik atau turun.
*/
function urutkanNama(array $nama, string $arah): array
{
if (!in_array($arah, ["asc", "desc"], true)) {
throw new InvalidArgumentException(
"\$arah harus 'asc' atau 'desc', didapat: '$arah'"
);
}
$urut = $nama;
sort($urut);
return $arah === "desc" ? array_reverse($urut) : $urut;
}
var_dump(urutkanNama(["Budi", "Ani", "Siti"], "asc"));
var_dump(urutkanNama(["Budi", "Ani", "Siti"], "desc"));Output yang Dihasilkan:
array(3) {
[0]=>
string(3) "Ani"
[1]=>
string(4) "Budi"
[2]=>
string(4) "Siti"
}
array(3) {
[0]=>
string(4) "Siti"
[1]=>
string(4) "Budi"
[2]=>
string(3) "Ani"
}Mari kita bedah dua lapis pada contoh di atas. Lapis pertama adalah @param 'asc'|'desc' $arah: anotasi ini tidak dibaca oleh PHP, tetapi dibaca oleh PHPStan dan Psalm — pemanggilan urutkanNama($nama, "bawah") akan ditandai error sebelum program berjalan. Lapis kedua adalah penjaga runtime in_array() dengan mode ketat: baris ini menolak nilai liar ketika data datang dari luar kendali kita, misalnya dari query string. Pernyataan throw menghentikan jalur tersebut dengan error — mekanisme lengkapnya dibahas pada Section Error Handling.
Ada pula jawaban level bahasa yang lebih modern untuk nilai terbatas: enum sejak PHP 8.1. Sebuah backed enum dengan kasus Asc = 'asc' dan Desc = 'desc' memberi tipe bernama yang diperiksa runtime, jauh lebih kaya daripada union string. Kita akan bertemu dengannya pada Chapter Enums
; sampai saat itu, PHPDoc plus penjaga runtime adalah pola yang dipakai mayoritas proyek.
Ringkasan #
PHP 8.2 mengizinkan true, false, dan null berdiri sendiri sebagai tipe untuk nilai kembali maupun parameter, dan runtime menegakkannya dengan TypeError — lengkap dengan larangan redundan seperti bool|true. Literal string dan integer tidak dikenali level bahasa, sehingga 'asc'|'desc' ditulis lewat PHPDoc dan ditegakkan PHPStan/Psalm, ditopang penjaga runtime untuk data dari luar. Dengan berbagai bentuk tipe kini terpetakan, ada satu pertanyaan yang belum terjawab: bagaimana mendeklarasikan fungsi yang memang tidak menyerahkan nilai — atau tidak pernah kembali sama sekali? Itulah tugas void dan never pada Chapter berikutnya.