Kamu melempar exception di PHP dengan throw new Exception('message');. Eksekusi berhenti di baris itu dan melompat ke blok catch terdekat yang menerima tipe exception tersebut, dan di sana $e->getMessage() mengembalikan pesannya. Pakai class yang spesifik, seperti InvalidArgumentException, supaya pemanggil bisa membedakan setiap masalah.
Exception adalah objek. throw meneruskannya ke atas melalui call stack sampai ke pemanggil pertama yang punya catch yang cocok, dan pemanggil itulah yang memutuskan apa yang harus dilakukan; fungsi yang menemukan masalahnya tidak perlu tahu. Cara kerja menangkap exception, finally, dan menangkap beberapa tipe ada di halaman try catch.
Isi sebuah objek exception
Setiap exception membawa pesan, kode integer, file dan baris tempat ia dibuat, serta stack trace. Constructor-nya menerima ($message, $code, $previous), semuanya opsional:
getLine() adalah baris throw new, bukan baris catch. Trace-nya mencantumkan panggilan-panggilan yang mengarah ke sana (#0 /home/index.php(8): findOrder()), yang biasanya menjadi hal pertama yang dibaca saat sebuah error muncul di log. Mengubah exception menjadi string (echo $e; atau (string) $e) mencetak semuanya dalam format standar PHP.
Class exception bawaan
PHP menyertakan sekumpulan class exception di Standard PHP Library (SPL). Melempar class yang menamai masalahnya membuat kodemu lebih mudah dibaca dan memungkinkan pemanggil menangkap secara spesifik. Semua class ini meng-extend Exception:
| Class | Lempar saat |
|---|---|
InvalidArgumentException | bentuk argumennya salah: nama kosong, opsi yang tidak dikenal |
DomainException | nilai berada di luar kumpulan yang masuk akal: harga negatif |
OutOfRangeException | kode meminta indeks yang tidak akan pernah ada (bug di pemanggil) |
OutOfBoundsException | key atau indeks tidak ada di data yang baru diketahui saat runtime |
RangeException | hasil perhitungan berada di luar rentang yang valid saat berjalan |
LengthException | sesuatu terlalu panjang atau terlalu pendek |
RuntimeException | masalah yang hanya bisa dideteksi saat berjalan: disk penuh, timeout |
UnexpectedValueException | sebuah fungsi mengembalikan atau menerima nilai dengan tipe yang tidak terduga |
LogicException | kodenya sendiri yang salah, sebuah bug, bukan input yang buruk |
JsonException | dilempar oleh json_encode()/json_decode() dengan JSON_THROW_ON_ERROR |
InvalidArgumentException, DomainException, LengthException, dan OutOfRangeException meng-extend LogicException; OutOfBoundsException, RangeException, dan UnexpectedValueException meng-extend RuntimeException. Kamu bisa memeriksa induk dari class mana pun sendiri:
Induk TypeError adalah Error, bukan Exception: ia termasuk keluarga kedua, di bawah ini.
Error yang dilempar PHP sendiri
Sejak PHP 7, dan jauh lebih banyak sejak PHP 8, fungsi dan operator bawaan PHP melempar subclass Error untuk masalah yang dulunya hanya warning. Kamu menangkapnya dengan cara yang sama, tetapi catch (Exception $e) tidak akan cocok dengannya:
DivisionByZeroError, TypeError, ValueError, ArgumentCountError, UnhandledMatchError, dan Error biasa semuanya meng-extend Error. Masing-masing lebih sering menandakan bug di kode pemanggil daripada data yang buruk, karena itulah mereka berada di luar pohon Exception. declare(strict_types=1) yang membuat str_repeat(5, 2) menjadi TypeError; tanpa itu, PHP akan mengonversi 5 menjadi "5".
Menulis class exception sendiri
Subclass satu baris sering kali sudah cukup: nama class-nya sendiri membawa makna, dan pemanggil bisa menangkap tepat tipe itu. Saat penangkapnya butuh data (pesanan yang mana, jumlah berapa), tambahkan property lalu teruskan pesannya ke atas dengan parent::__construct():
Pilih induknya dengan cermat: meng-extend RuntimeException (alih-alih Exception biasa) berarti kode yang menangkap RuntimeException juga menangani exception milikmu. Hierarki kecil, satu exception dasar per library atau modul dengan subclass spesifik di bawahnya, memberi pemanggil pilihan antara "tangkap semua dari modul pembayaran" dan "tangkap kasus yang ini saja".
Merangkai exception dengan previous
Saat kamu menangkap exception tingkat rendah lalu melempar exception tingkat yang lebih tinggi, berikan exception aslinya sebagai argumen ketiga. Tidak ada yang hilang: getPrevious() menelusuri kembali sampai ke akar penyebabnya.
Pengguna melihat "customers.csv could not be imported"; log, yang menelusuri rangkaiannya, juga mendapat "row 2 has 2 columns". Kalau exception yang dirangkai tidak pernah ditangkap, fatal error dari PHP mencetak seluruh rangkaiannya: penyebab asli lebih dulu, lalu setiap pembungkusnya di bawah baris Next.
throw sebagai ekspresi
Sejak PHP 8.0, throw adalah ekspresi, jadi bisa diletakkan di mana pun sebuah nilai diharapkan: setelah ??, di dalam ternary, di dalam arrow function. Dengan begitu, "ambil ini atau gagal" cukup satu baris:
Kapan tidak perlu melempar exception
Exception dipakai untuk situasi yang tidak bisa ditangani fungsi saat ini dan tidak diharapkan oleh alur normal. Pencarian yang tidak menemukan apa-apa, field opsional yang kosong, atau pengguna yang belum login adalah hasil yang biasa: kembalikan null, false, atau array kosong dan biarkan pemanggil memeriksanya, lalu simpan exception untuk kasus ketika melanjutkan program justru salah.
Dua kebiasaan lagi yang membuat kode exception tetap mudah dibaca: jangan pernah menangkap exception hanya untuk mengabaikannya (catch (Exception $e) {} yang kosong menyembunyikan bug sungguhan berikutnya), dan lempar class yang spesifik alih-alih Exception polos, supaya pemanggil tidak pernah terpaksa menangkap semuanya hanya untuk menangani satu kasus.
Pertanyaan yang sering diajukan
Bagaimana cara melempar exception di PHP?
Buat objek exception lalu lempar: throw new InvalidArgumentException('Quantity must be positive');. Eksekusi berhenti di baris itu dan melompat ke blok catch terdekat yang cocok; kalau tidak ada, script berakhir dengan fatal error "Uncaught".
Bagaimana cara membuat exception sendiri di PHP?
Extend Exception atau salah satu subclass-nya: class PaymentFailedException extends RuntimeException {}. Satu baris itu sudah cukup untuk melempar dan menangkapnya berdasarkan tipenya sendiri. Tambahkan property dan constructor kalau penangkapnya butuh data tambahan, lalu panggil parent::__construct($message, $code, $previous).
Apa bedanya getMessage dan getCode?
getMessage() mengembalikan teks yang diberikan sebagai argumen pertama constructor, ditujukan untuk manusia dan log. getCode() mengembalikan integer yang diberikan sebagai argumen kedua (0 secara bawaan), berguna untuk pengecekan oleh mesin seperti memetakan error ke kode status HTTP.
Exception mana yang sebaiknya dilempar di PHP?
Pakai class SPL bawaan yang sesuai dengan masalahnya: InvalidArgumentException untuk argumen yang salah, DomainException untuk nilai di luar yang diizinkan, RuntimeException untuk kegagalan yang hanya terlihat saat runtime (disk penuh, timeout), LogicException untuk kesalahan pemrograman. Untuk error yang perlu dibedakan oleh pemanggilmu, buat subclass sendiri.
Apa itu exception chaining di PHP?
Memberikan exception asli sebagai argumen ketiga constructor saat kamu melempar exception baru: throw new ImportException('Import failed', 0, $e);. Exception baru memberi konteks, dan $e->getPrevious() padanya mengembalikan penyebab aslinya, jadi tidak ada detail yang hilang.