Mastering Errors & Exceptions in PHP
Memahami Dasar Masalah
Error adalah masalah serius yang biasanya terjadi di tingkat sistem atau lingkungan runtime PHP.
Exception adalah kondisi abnormal yang terjadi saat kode dieksekusi (Runtime).
| Fitur | Error | Exception |
|---|---|---|
| Sumber | Environment/System | Application Logic |
| Handling | Sulit/Fatal | Bisa di-catch |
| Impact | Stop Script (Fatal) | Flow Control |
Sejak PHP 7, Error dan Exception mengimplementasikan interface yang sama: Throwable.
interface Throwable {
public function getMessage();
public function getCode();
public function getFile();
public function getLine();
public function getTrace();
// ...
}
Sebelum Exception populer, kita sering menggunakan if-else atau die().
$file = fopen("data.txt", "r");
if (!$file) {
die("Gagal membuka file!");
}
// Lanjut proses...
Masalah: Kode logika bercampur dengan error handling.
Mekanisme Penanganan
Bungkus kode yang berpotensi error di dalam blok try.
try {
// Kode yang mungkin melempar Exception
$data = getDataFromApi();
} catch (Exception $e) {
// Kode ini jalan jika terjadi Exception
echo "Terjadi kesalahan: " . $e->getMessage();
}
Variabel $e di blok catch adalah object dari class Exception.
try {
// ...
} catch (Exception $e) {
echo $e->getMessage(); // Pesan error
echo $e->getCode(); // Kode error
echo $e->getFile(); // File penyebab
echo $e->getLine(); // Baris error
}
Kita bisa menangkap jenis Exception yang berbeda secara spesifik.
try {
// ...
} catch (DatabaseException $e) {
echo "Masalah Database";
} catch (NetworkException $e) {
echo "Masalah Koneksi";
} catch (Exception $e) {
echo "Masalah Umum Lainnya";
}
PENTING: Tangkap Exception dari yang paling SPESIFIK ke yang paling UMUM.
Jika Exception (parent) ditaruh di atas, child exception tidak akan pernah tertangkap.
Gunakan pipa | untuk menangkap beberapa tipe sekaligus.
try {
// ...
} catch (MissingFileException | PermissionException $e) {
logError($e->getMessage());
echo "Gagal akses file.";
}
Blok finally akan SELALU dieksekusi, baik terjadi exception maupun tidak.
Cocok untuk cleanup resource (tutup koneksi DB, tutup file).
$fh = fopen("test.txt", "w");
try {
fwrite($fh, "Data");
// Simulasi error
throw new Exception("Oops");
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
} finally {
echo "Menutup file...";
fclose($fh); // Pasti dijalankan
}
try.try sukses.catch DILEWATI.finally.try.try STOP.catch yang cocok.finally.Melempar Masalah
Gunakan keyword throw untuk memicu Exception secara manual.
function bagi($a, $b) {
if ($b == 0) {
throw new Exception("Tidak bisa bagi dengan nol!");
}
return $a / $b;
}
PHP menyediakan banyak class Exception bawaan (SPL).
InvalidArgumentExceptionOutOfBoundsExceptionLogicExceptionRuntimeException
function setAge($age) {
if ($age < 0) {
throw new InvalidArgumentException("Umur harus positif");
}
$this->age = $age;
}
Kita bisa menangkap exception, melakukan sesuatu (misal logging), lalu melemparnya kembali.
try {
doSomething();
} catch (Exception $e) {
logger($e->getMessage()); // Log dulu
throw $e; // Lempar lagi ke atas
}
Jika Exception tidak ditangkap di fungsi saat ini, dia akan "naik" ke fungsi pemanggil (Caller), terus sampai ke Global Scope.
Jika Exception sampai ke Global Scope dan tidak ada yang menangkap, PHP akan menampilkan Fatal Error: Uncaught Exception.
Ini buruk untuk User Experience.
Gunakan set_exception_handler untuk menangkap semua exception yang lolos.
set_exception_handler(function ($e) {
echo "Maaf, terjadi kesalahan sistem.";
// Log error ke file
});
Sekarang throw bisa digunakan di tempat yang hanya menerima expression.
$value = $input['user'] ?? throw new Exception("User wajib ada");
$result = $condition ? 'Valid' : throw new InvalidArgumentException();
Membuat Exception Sendiri
UserNotFoundException).
Cukup buat class baru yang extends Exception.
class SaldoTidakCukupException extends Exception {
// Bisa kosong, mewarisi semua fitur parent
}
function tarikTunai($jumlah, $saldo) {
if ($jumlah > $saldo) {
throw new SaldoTidakCukupException("Saldo kurang!");
}
return $saldo - $jumlah;
}
try {
tarikTunai(100000, 5000);
} catch (SaldoTidakCukupException $e) {
echo "Transaksi Gagal: " . $e->getMessage();
// Tampilkan saran topup
} catch (Exception $e) {
echo "Error Sistem Lainnya";
}
class ValidationException extends Exception {
private $errors = [];
public function __construct($errors) {
parent::__construct("Validation Failed");
$this->errors = $errors;
}
public function getErrors() {
return $this->errors;
}
}
try {
validateInput($_POST);
} catch (ValidationException $e) {
$fieldErrors = $e->getErrors();
print_r($fieldErrors); // Tampilkan detail error per field
}
Kelompokkan Exception berdasarkan domain bisnis aplikasi Anda.
App\Exceptions\Auth\InvalidCredentialsExceptionApp\Exceptions\Payment\PaymentFailedExceptionApp\Exceptions\Upload\FileTooLargeExceptionAnda bisa membuat Interface untuk menandai Exception tertentu.
interface PublicExceptionInterface {}
class UserMessageException extends Exception implements PublicExceptionInterface {}
// Di Global Handler:
if ($e instanceof PublicExceptionInterface) {
echo $e->getMessage(); // Aman ditampilkan ke user
}
Selalu akhiri nama class dengan kata Exception.
Good: UserNotFoundException
Bad: UserNotFound, UserError
Tips & Trik Profesional
Exception itu "mahal" (berat secara performa). Jangan gunakan untuk logika biasa.
// BAD
try {
$user = findUser();
} catch (NotFoundException $e) {
createUser();
}
// GOOD
$user = findUser();
if (!$user) {
createUser();
}
Bungkus Low-Level Exception menjadi High-Level Exception agar lebih bermakna.
try {
$db->connect();
} catch (PDOException $e) {
// Sembunyikan detail SQL, lempar error aplikasi
throw new DatabaseConnectionException("Gagal koneksi DB", 0, $e);
}
Parameter ke-3 di constructor Exception adalah $previous. Gunakan untuk menyimpan rantai
exception (seperti contoh wrapping di atas).
Ini membantu debugging untuk melacak akar masalah.
Selalu log exception yang tidak terduga (500 Internal Server Error).
Jangan log exception yang memang diharapkan (404 Not Found, 422 Validation Error), kecuali untuk audit trail.
JANGAN PERNAH menampilkan Stack Trace ke User di Production!
Stack trace berisi path file, nama database, password, dll. Tampilkan pesan generik "Terjadi Kesalahan" ke user, tapi log detailnya di server.
Kita bisa menggunakan match untuk handling error code.
try {
// ...
} catch (ApiException $e) {
$message = match ($e->getCode()) {
404 => 'Data tidak ditemukan',
500 => 'Server sedang sibuk',
default => 'Error tidak diketahui',
};
echo $message;
}
try untuk kode berisiko.catch untuk recovery atau logging.finally untuk cleanup.Happy Coding!