Exception зохицуулах
Програм ажиллаж байх үед олон зүйл буруу болж болно — файл олдохгүй, өгөгдлийн сантай холбогдохгүй, буруу утга ирэх зэрэг. Ийм гэнэтийн нөхцөл байдлыг exception гэж нэрлэдэг. PHP-д exception-ийг try/catch блокоор зохицуулдаг: "туршиж үзэх" (try) ба "алдааг барих" (catch). Exception нь алдааны мэдэгдлийг (error message) хэвлэн зогсохоос илүү сайн шийдэл — алдааг ухаалгаар зохицуулж, хэрэглэгчид ойлгомжтой мэдэгдэл өгдөг.
try / catch — үндсэн бүтэц
try блок дотор exception шидэж болох код байрна. Exception шидэгдвэл catch блок ажиллана. Exception шидэгдэхгүй бол catch алгасагдана:
<?php
function хуваах(float $тоо, float $хуваагч): float {
if ($хуваагч === 0.0) {
throw new InvalidArgumentException("Тэгд хуваах боломжгүй.");
}
return $тоо / $хуваагч;
}
try {
echo хуваах(10, 2) . "\n"; // 5 — ажиллана
echo хуваах(10, 0) . "\n"; // Exception шидэнэ
echo "Энэ мөр хэзээ ч ажиллахгүй\n"; // алгасагдана
} catch (InvalidArgumentException $e) {
echo "Алдаа: " . $e->getMessage() . "\n"; // Алдаа: Тэгд хуваах боломжгүй.
echo "Мөр дугаар: " . $e->getLine() . "\n";
echo "Файл: " . $e->getFile() . "\n";
}
echo "Програм үргэлжилж байна.\n"; // catch-ийн дараа үргэлжилнэ
?>
$e->getMessage() — алдааны мэдэгдэл, $e->getLine() — exception шидэгдсэн мөр, $e->getFile() — файлын нэрийг буцаадаг.
Олон catch — төрлөөр ялгах
Нэг try блокт хэд хэдэн catch бичиж, exception-ийн төрлөөр ялгаж зохицуулж болно. PHP нь дарааллаар шалгаж, тохирсон эхнийхийг ажиллуулдаг:
<?php
function файл_унших(string $зам): string {
if (!file_exists($зам)) {
throw new RuntimeException("Файл олдсонгүй: {$зам}");
}
if (!is_readable($зам)) {
throw new RuntimeException("Файлыг унших эрхгүй: {$зам}");
}
$агуулга = file_get_contents($зам);
if ($агуулга === false) {
throw new RuntimeException("Файл уншихад алдаа гарлаа.");
}
return $агуулга;
}
function тоо_задлах(string $утга): int {
if (!is_numeric($утга)) {
throw new InvalidArgumentException("'{$утга}' тоо биш байна.");
}
return (int)$утга;
}
try {
$агуулга = файл_унших("тохиргоо.txt");
$тоо = тоо_задлах("abc"); // Энд exception шидэнэ
} catch (InvalidArgumentException $e) {
// Буруу утгын алдаа
echo "Оруулт буруу: " . $e->getMessage() . "\n";
} catch (RuntimeException $e) {
// Файлын алдаа
echo "Системийн алдаа: " . $e->getMessage() . "\n";
} catch (Exception $e) {
// Бусад бүх exception — хамгийн сүүлд бичнэ
echo "Тодорхойгүй алдаа: " . $e->getMessage() . "\n";
}
?>
Илүү тодорхой exception төрлүүдийг эхэнд, ерөнхий Exception-г хамгийн сүүлд бичнэ — эсрэгээр бол ерөнхий нь бүгдийг барьчихна.
finally — заавал ажилладаг блок
finally блок нь exception шидэгдсэн эсэхээс үл хамааран үргэлж ажилладаг. Нөөц чөлөөлөх, лог бичих, холболт хаах зэрэгт тохиромжтой:
<?php
function өгөгдөл_боловсруулах(array $өгөгдөл): void {
$файл = fopen('гаралт.txt', 'w');
try {
foreach ($өгөгдөл as $утга) {
if (!is_string($утга)) {
throw new InvalidArgumentException("Зөвхөн мөр өгөгдөл хүлээнэ.");
}
fwrite($файл, $утга . "\n");
}
echo "Амжилттай боловсруулав.\n";
} catch (InvalidArgumentException $e) {
echo "Алдаа: " . $e->getMessage() . "\n";
} finally {
// Exception шидэгдсэн ч, шидэгдээгүй ч энд хүрнэ
fclose($файл);
echo "Файл хаагдлаа.\n";
}
}
өгөгдөл_боловсруулах(["Болд", "Сарнай"]);
// Амжилттай боловсруулав.
// Файл хаагдлаа.
өгөгдөл_боловсруулах(["Болд", 42, "Сарнай"]);
// Алдаа: Зөвхөн мөр өгөгдөл хүлээнэ.
// Файл хаагдлаа. ← finally заавал ажилладаг
?>
Өөрийн Exception класс
Exception классаас өвлөн тусгай exception класс бүтээж болно. Энэ нь алдааны төрлийг илүү тодорхой болгож, тусгай мэдээлэл дамжуулах боломж олгодог:
<?php
// Суурь апп exception
class АппАлдаа extends RuntimeException {}
// Тодорхой алдааны төрлүүд
class ЗөвшөөрөлгүйАлдаа extends АппАлдаа {
public function __construct(string $үйлдэл) {
parent::__construct("'{$үйлдэл}' үйлдлийг гүйцэтгэх эрхгүй байна.", 403);
}
}
class ОлдохгүйАлдаа extends АппАлдаа {
public function __construct(string $нөөц, int|string $id) {
parent::__construct("'{$нөөц}' (id: {$id}) олдсонгүй.", 404);
}
}
class БаталгаажуулалтАлдаа extends АппАлдаа {
private array $алдаанууд;
public function __construct(array $алдаанууд) {
parent::__construct("Оруулт баталгаажаагүй.", 422);
$this->алдаанууд = $алдаанууд;
}
public function алдаанууд(): array {
return $this->алдаанууд;
}
}
// Ашиглах жишээ
function хэрэглэгч_авах(int $id): array {
$хэрэглэгчид = [1 => ['нэр' => 'Болд'], 2 => ['нэр' => 'Сарнай']];
if (!isset($хэрэглэгчид[$id])) {
throw new ОлдохгүйАлдаа('Хэрэглэгч', $id);
}
return $хэрэглэгчид[$id];
}
function шинэ_бүртгэл(array $өгөгдөл): void {
$алдаанууд = [];
if (empty($өгөгдөл['нэр'])) $алдаанууд[] = "Нэр заавал шаардлагатай.";
if (empty($өгөгдөл['имэйл'])) $алдаанууд[] = "И-мэйл заавал шаардлагатай.";
if (!empty($алдаанууд)) {
throw new БаталгаажуулалтАлдаа($алдаанууд);
}
}
try {
$хэрэглэгч = хэрэглэгч_авах(99);
} catch (ОлдохгүйАлдаа $e) {
echo $e->getMessage() . " (код: " . $e->getCode() . ")\n";
// 'Хэрэглэгч' (id: 99) олдсонгүй. (код: 404)
}
try {
шинэ_бүртгэл(['нэр' => '']);
} catch (БаталгаажуулалтАлдаа $e) {
echo $e->getMessage() . "\n";
foreach ($e->алдаанууд() as $алдаа) {
echo " - {$алдаа}\n";
}
}
?>
set_exception_handler — барьж авагдаагүй exception
Аль ч catch-д барьж авагдаагүй exception-уудыг нэг газарт зохицуулах боломжтой:
<?php
set_exception_handler(function (Throwable $e): void {
// Лог бичнэ
error_log($e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine());
// Хэрэглэгчид ойлгомжтой мэдэгдэл
echo "Системд алдаа гарлаа. Дахин оролдоно уу.";
});
// Барьж авагдаагүй exception
throw new RuntimeException("Тест алдаа");
// Хэрэглэгч: "Системд алдаа гарлаа. Дахин оролдоно уу."
// Лог: тодорхой алдааны мэдээлэл
?>
Дараагийн хичээлд:
PHP Data Objects (PDO) ашиглан өгөгдлийн сантай холбогдох аргыг судална. PDO гэж юу болохыг, SQLite болон MySQL-тэй хэрхэн холбогддогийг, prepared statement яагаад ашиглах ёстойг жишээгээр тайлбарлана.