Menu

Exception PHP: throw dan Membuat Class Exception Sendiri

Kamu melempar exception di PHP dengan throw new Exception('message');, dan pemanggil membacanya dengan $e->getMessage() di blok catch. Pelajari class exception bawaan, cara menulis exception sendiri, getCode(), getLine(), dan getPrevious(), throw sebagai ekspresi, serta error yang dilempar PHP sendiri seperti ValueError dan TypeError.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

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:

ClassLempar saat
InvalidArgumentExceptionbentuk argumennya salah: nama kosong, opsi yang tidak dikenal
DomainExceptionnilai berada di luar kumpulan yang masuk akal: harga negatif
OutOfRangeExceptionkode meminta indeks yang tidak akan pernah ada (bug di pemanggil)
OutOfBoundsExceptionkey atau indeks tidak ada di data yang baru diketahui saat runtime
RangeExceptionhasil perhitungan berada di luar rentang yang valid saat berjalan
LengthExceptionsesuatu terlalu panjang atau terlalu pendek
RuntimeExceptionmasalah yang hanya bisa dideteksi saat berjalan: disk penuh, timeout
UnexpectedValueExceptionsebuah fungsi mengembalikan atau menerima nilai dengan tipe yang tidak terduga
LogicExceptionkodenya sendiri yang salah, sebuah bug, bukan input yang buruk
JsonExceptiondilempar 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".

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.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai