Error & Exception
Hierarki Throwable (Error vs Exception), try/catch/finally, exception kustom, multi-catch, dan penanganan error yang benar.
Mengapa penanganan error penting?
Di dunia nyata, hal buruk pasti terjadi: file hilang, database mati, input ngawur, koneksi putus. Program yang baik tidak "meledak" di depan pengguna, melainkan menangani kegagalan dengan anggun dan mencatatnya untuk developer.
Throwable: Error vs Exception
Throwable
├── Error → kesalahan fatal bahasa (PHP 7+)
│ ├── TypeError, ValueError, ArithmeticError → DivisionByZeroError
│ └── ArgumentCountError, UnhandledMatchError, ...
└── Exception → kegagalan yang dapat diantisipasi
├── RuntimeException (gagal saat berjalan: file, jaringan, DB)
├── LogicException (kesalahan logika/programmer)
│ └── InvalidArgumentException, DomainException, ...
└── JsonException, PDOException, ...
Sejak PHP 7 kebanyakan "fatal error" menjadi objek Error yang bisa ditangkap. Di PHP 8, banyak warning lama naik pangkat menjadi Error/TypeError/ValueError sehingga bug tidak diam-diam.
try / catch / finally
function bagi(int $a, int $b): float
{
if ($b === 0) {
throw new InvalidArgumentException('Pembagi tidak boleh nol');
}
return $a / $b;
}
try {
echo bagi(10, 0);
} catch (InvalidArgumentException $e) {
echo 'Input salah: ' . $e->getMessage();
} catch (RuntimeException | LogicException $e) { // multi-catch
echo 'Masalah lain';
} finally {
echo "\nSelalu dijalankan (bersih-bersih)";
}
- Urutkan
catchdari yang paling spesifik ke umum. finallyselalu jalan — tempat menutup file/koneksi.- Sejak PHP 8 variabel boleh dihilangkan:
catch (RuntimeException).
Exception kustom
class StokTidakCukupException extends RuntimeException
{
public function __construct(
public readonly string $produk,
public readonly int $diminta,
public readonly int $tersedia,
) {
parent::__construct("Stok {$produk} tidak cukup: diminta {$diminta}, tersedia {$tersedia}");
}
}
throw new StokTidakCukupException('Mouse', 5, 2);
Exception kustom membuat penangan bisa memilah penyebab dengan tepat, dan membawa data tambahan (produk, jumlah) alih-alih mengurai teks pesan.
Exception berantai (previous)
try {
$pdo->query($sql);
} catch (PDOException $e) {
throw new RuntimeException('Gagal menyimpan pesanan', 0, $e); // $e disimpan sebagai previous
}
$e->getPrevious(), getFile(), getLine(), dan getTraceAsString() membantu menelusuri akar masalah.
Aturan emas
- Jangan telan exception diam-diam (
catch {}kosong). Minimal catat log. - Jangan bocorkan detail teknis ke pengguna di produksi (
display_errors=Off,log_errors=On). Tampilkan pesan ramah, simpan detailnya di log. - Tangkap di tempat yang bisa berbuat sesuatu (coba lagi, pesan ke pengguna); sisanya biarkan naik ke penangan global (
set_exception_handler/ error handler framework). - Gunakan exception untuk keadaan luar biasa, bukan alur normal.
Mengubah warning menjadi exception
set_error_handler(function (int $no, string $msg, string $file, int $line) {
throw new ErrorException($msg, 0, $no, $file, $line);
});
Framework modern melakukan ini untukmu agar semua masalah punya alur penanganan yang seragam.