Error bekerja di level engine: engine yang memutuskan kapan pesan keluar dan program berhenti. Namun banyak kegagalan justru lahir dari aturan bisnis kita sendiri — saldo yang tidak cukup, usia yang belum memenuhi syarat, atau berkas konfigurasi yang tidak valid — dan untuk kegagalan semacam ini PHP menyediakan exception. Exception adalah kegagalan berbentuk object yang kita lempar secara eksplisit, lengkap dengan pesan, kode, dan jejak panggilan yang bisa dibaca program. Pada chapter ini kita belajar melempar exception, membaca informasi di dalamnya, menghadapi exception yang tidak tertangkap, lalu memetakan posisinya dalam hierarki Throwable.
Exception Adalah Object Kegagalan #
Berbeda dari error yang berupa pesan teks, exception adalah object — instance dari class Exception atau class turunannya. Object tersebut dibuat dan dilempar dengan keyword throw, dan begitu dilempar, alur eksekusi normal terputus seketika: baris setelah throw tidak dijalankan, dan PHP mencari blok catch yang sanggup menangkap object tersebut. Mari kita lihat wujudnya:
<?php
function validasiUsia(int $usia) {
if ($usia < 17) {
throw new Exception("Usia minimal 17 tahun untuk mendaftar", 1001);
}
echo "Pendaftaran diterima\n";
}
try {
validasiUsia(15);
} catch (Exception $e) {
echo "Pendaftaran ditolak: " . $e->getMessage() . "\n";
}Output yang Dihasilkan:
Pendaftaran ditolak: Usia minimal 17 tahun untuk mendaftarMari kita bedah contoh di atas baris per baris:
- Baris 4 (
throw new Exception(...)): ketika$usiadi bawah 17, kita membuat objectExceptiondengan dua argumen konstruktor — pesan"Usia minimal 17 tahun untuk mendaftar"dan kode1001— lalu melemparkannya. - Baris 6:
echo "Pendaftaran diterima"tidak dieksekusi karenathrowpada baris 4 sudah memutus alur function. - Baris 10-12: blok
try-catchmenangkap object tersebut; melalui$e->getMessage()kita membaca pesannya. Struktur penangkapan ini dibahas tuntas pada chapter try-catch-finally — fokus kita sekarang adalah sisi object-nya.
Pesan dan kode pada konstruktor sepenuhnya milik kita: pesan menjelaskan apa yang salah untuk manusia, sedangkan kode (angka bebas seperti 1001 atau 4001) biasa dipakai program pemanggil untuk membedakan jenis kegagalan tanpa harus mengurai teks pesan.
Membaca Informasi di Dalam Exception #
Class Exception menyediakan sejumlah getter untuk mengurai isi object. Tabel berikut merangkum yang paling sering dipakai:
| Method | Isi yang dikembalikan |
|---|---|
getMessage() | pesan kegagalan (string) |
getCode() | kode kegagalan (int) yang kita berikan di konstruktor |
getFile() | path berkas tempat throw terjadi |
getLine() | nomor baris tempat throw terjadi |
getPrevious() | exception sebelumnya bila ada perantai (dibahas di chapter try-catch-finally) |
getTraceAsString() | jejak panggilan hingga titik throw, dalam bentuk string |
Berikut demonstrasi tiga getter pertama dalam satu blok yang mudah dicetak:
<?php
try {
throw new Exception("Saldo tidak cukup", 4001);
} catch (Exception $e) {
echo "Pesan : " . $e->getMessage() . "\n";
echo "Kode : " . $e->getCode() . "\n";
echo "Baris : " . $e->getLine() . "\n";
}Output yang Dihasilkan:
Pesan : Saldo tidak cukup
Kode : 4001
Baris : 3Mari kita telaah hasilnya:
- Baris 3:
throwterjadi persis di baris 3, sehinggagetLine()mengembalikan3— lokasi ini sangat membantu saat pesan kegagalan muncul dari function yang dalam. - Baris 5-6:
getMessage()mengembalikan teks"Saldo tidak cukup"persis seperti argumen pertama konstruktor, dangetCode()mengembalikan4001dari argumen keduanya.
Dua getter lainnya, getFile() dan getTraceAsString(), masing-masing mengembalikan path berkas dan jejak panggilan lengkap — nilainya bergantung pada lokasi berkas di sistem Anda, sehingga tidak kita cetak pada contoh agar output tetap ringkas dan deterministik.
Exception yang Tidak Ditangkap #
Apa yang terjadi jika tidak ada blok catch yang menangkap exception yang kita lempar? PHP memperlakukannya sebagai fatal error: pesan Uncaught ditampilkan beserta stack trace — jejak rantai panggilan yang berujung pada throw — lalu proses keluar dengan exit code 255. Perhatikan contoh berikut:
<?php
function tarikSaldo(float $jumlah, float $saldo) {
if ($jumlah > $saldo) {
throw new Exception("Saldo tidak cukup", 4001);
}
return $saldo - $jumlah;
}
echo "Memproses penarikan...\n";
$saldoAkhir = tarikSaldo(500000, 250000);
echo "Saldo akhir: $saldoAkhir\n";Output yang Dihasilkan:
Memproses penarikan...
Fatal error: Uncaught Exception: Saldo tidak cukup in /home/z/my-project/php-course/content/php/error-handling/exceptions/.verify_tmp.php:4
Stack trace:
#0 /home/z/my-project/php-course/content/php/error-handling/exceptions/.verify_tmp.php(10): tarikSaldo(500000.0, 250000.0)
#1 {main}
thrown in /home/z/my-project/php-course/content/php/error-handling/exceptions/.verify_tmp.php on line 4Mari kita bedah bagian-bagian pesan fatal pada output di atas:
- Baris utama (
Fatal error: Uncaught Exception: Saldo tidak cukup in ...:4): kataUncaughtmenegaskan tidak ada penangkap; angka4di akhir adalah baris tempatthrowberada di dalam functiontarikSaldo(). - Baris
#0: frame pertama stack trace menunjukkan pemanggilantarikSaldo(500000.0, 250000.0)dari baris 10 — lengkap dengan nilai argumen yang dikirim, sehingga kita bisa melihat data apa yang memicu kegagalan. - Baris
#1 {main}: frame terakhir adalah skrip utama; setelah frame ini tidak ada lagi pemanggil, lalu PHP mencetakthrown in ... on line 4sebagai penutup lokasi.
Stack trace adalah alat debugging paling berharga dari exception: dari dua baris #0 dan #1 saja kita sudah bisa menelusuri jalur eksekusi dari skrip utama hingga titik kegagalan tanpa membaca ulang seluruh kode.
Hierarki Throwable #
Class Exception tidak berdiri sendiri. Seluruh object yang bisa dilempar dan ditangkap di PHP — baik dari kode aplikasi maupun dari engine — menerapkan interface Throwable, yang bercabang menjadi dua keluarga besar:
Diagram di atas menunjukkan bahwa Error dan Exception adalah dua cabang Throwable yang sejajar. Cabang Error menampung kegagalan level engine yang sejak PHP 7 menjadi object — TypeError, DivisionByZeroError, hingga Call to undefined function — sedangkan cabang Exception menampung kegagalan level aplikasi, dengan subclass bawaan seperti RuntimeException dan InvalidArgumentException yang bisa kita pakai langsung. Kita bisa membuktikan posisi kedua cabang tersebut dengan operator instanceof:
<?php
$e = new Exception("kegagalan logika");
$d = new DivisionByZeroError();
var_dump($e instanceof Throwable);
var_dump($d instanceof Throwable);
var_dump($e instanceof Error);
var_dump($d instanceof Exception);Output yang Dihasilkan:
bool(true)
bool(true)
bool(false)
bool(false)Hasil pada output di atas menegaskan hubungan hierarkinya: object Exception dan object DivisionByZeroError sama-sama Throwable (dua baris bool(true) pertama), namun Exception bukan Error dan DivisionByZeroError bukan Exception (dua baris bool(false) terakhir). Konsekuensi praktisnya penting: catch (Exception $e) tidak akan menangkap Error dari engine, dan sebaliknya — pemahaman yang akan kita manfaatkan di chapter try-catch-finally. Berikut contoh Error cabang engine yang tidak tertangkap:
<?php
$totalNilai = 240;
$jumlahSiswa = 0;
echo "Menghitung rata-rata...\n";
$rataRata = $totalNilai / $jumlahSiswa;
echo "Rata-rata: $rataRata\n";Output yang Dihasilkan:
Menghitung rata-rata...
Fatal error: Uncaught DivisionByZeroError: Division by zero in /home/z/my-project/php-course/content/php/error-handling/exceptions/.verify_tmp.php:6
Stack trace:
#0 {main}
thrown in /home/z/my-project/php-course/content/php/error-handling/exceptions/.verify_tmp.php on line 6Pada contoh di atas, pembagian $totalNilai / $jumlahSiswa di baris 6 dengan pembagi nol memicu DivisionByZeroError — bukan Warning seperti di PHP 7 — sehingga muncul Fatal error: Uncaught DivisionByZeroError: Division by zero dan baris 7 tidak pernah tereksekusi. Perilaku ini konsisten dengan peta kategori fatal error pada chapter sebelumnya: kegagalan engine di PHP 8 cenderung berwujud object Error dari cabang kiri hierarki.
Berkas contoh.php pada bagian bawah halaman merangkum seluruh contoh non-fatal di atas dalam satu program utuh yang dapat Anda unduh dan jalankan langsung.
Ringkasan #
Exception adalah kegagalan berbentuk object: dibuat dengan new Exception(pesan, kode), dilempar dengan throw, dan dibaca melalui getter getMessage(), getCode(), getFile(), serta getLine(). Exception yang tidak tertangkap berakhir sebagai fatal error Uncaught lengkap dengan stack trace yang menelusuri rantai pemanggilan. Seluruh object lempar-impan menaati interface Throwable yang bercabang menjadi Error untuk kegagalan engine dan Exception untuk kegagalan aplikasi. Kini saatnya mempelajari sisi penangkapnya: struktur try-catch-finally pada chapter berikutnya.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.php
Unduh berkas<?php
// contoh.php — rangkuman contoh chapter "Exceptions".
// Jalankan dengan: php contoh.php
function validasiUsia(int $usia) {
if ($usia < 17) {
throw new Exception("Usia minimal 17 tahun untuk mendaftar", 1001);
}
echo "Pendaftaran diterima\n";
}
try {
validasiUsia(15);
} catch (Exception $e) {
echo "Pendaftaran ditolak: " . $e->getMessage() . "\n";
}
echo "---\n";
try {
throw new Exception("Saldo tidak cukup", 4001);
} catch (Exception $e) {
echo "Pesan : " . $e->getMessage() . "\n";
echo "Kode : " . $e->getCode() . "\n";
echo "Baris : " . $e->getLine() . "\n";
}
echo "---\n";
$e = new Exception("kegagalan logika");
$d = new DivisionByZeroError();
var_dump($e instanceof Throwable);
var_dump($d instanceof Throwable);
var_dump($e instanceof Error);
var_dump($d instanceof Exception);