Xatolar — try/catch/finally, Exception, error_reporting

~15 daq · PHP · Fayllar, Sana va Xatolar

Muammodan boshlaylik

Saytingiz ishlab turibdi. Kechasi ma'lumotlar bazasi serveri qayta ishga tushdi. Shu daqiqada saytga kirgan foydalanuvchi ekranda buni ko'rdi:

Fatal error: Uncaught PDOException: SQLSTATE[HY000] [2002] Connection refused
in /var/www/dokon/config/db.php:12
Stack trace: #0 /var/www/dokon/config/db.php(12): PDO->__construct('mysql:host=10.0...', 'dokon_admin', 'Qw3rty!2026')

Foydalanuvchi uchun bu tushunarsiz. Hujumchi uchun esa — sovg'a: server papkalari tuzilmasi, baza manzili, login va parol.

Bu darsda ikki narsani o'rganamiz: xatolarni dastur ichida to'g'ri ushlash va foydalanuvchiga xavfsiz ko'rsatish.


PHP dagi ikki xil muammo

1. Ogohlantirishlar (Warning, Notice, Deprecated) — dastur davom etadi:

<?php

echo $yoq;   // Warning: Undefined variable $yoq
echo "Bu chiqadi";

2. Xatolar va istisnolar (Error, Exception) — ushlanmasa, dastur to'xtaydi:

<?php

echo intdiv(10, 0);   // DivisionByZeroError
echo "Bu chiqmaydi";

PHP 7 dan boshlab deyarli barcha jiddiy xatolar ob'ekt sifatida "tashlanadi" (throw) va ularni ushlash mumkin.


try / catch

<?php

try {
    $natija = intdiv(10, 0);
    echo "Bu qator bajarilmaydi";
} catch (DivisionByZeroError $e) {
    echo "Nolga bo'lib bo'lmaydi";
}

echo "Dastur davom etadi";
  • try — xato chiqishi mumkin bo'lgan kod
  • catch (Tur $e) — shu turdagi xato chiqsa, nima qilish
  • $e — xato ob'ekti, ichida ma'lumot bor
<?php

try {
    $data = json_decode('{noto\'g\'ri', true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo $e->getMessage();   // Syntax error
    echo $e->getFile();      // qaysi faylda
    echo $e->getLine();      // qaysi qatorda
}

Xatolar ierarxiyasi

Throwable
├── Error                      ← PHP ning o'z xatolari (kod xatosi)
│   ├── TypeError
│   ├── ValueError
│   ├── ArgumentCountError
│   └── DivisionByZeroError
└── Exception                  ← kutilgan, qayta ishlanadigan holatlar
    ├── JsonException
    ├── PDOException
    ├── RuntimeException
    └── InvalidArgumentException

catch (Exception $e) — barcha Exception larni ushlaydi, lekin Error larni ushlamaydi. Ikkalasini ham ushlash kerak bo'lsa — Throwable.

Bir nechta tur:

<?php

try {
    // ...
} catch (JsonException | ValueError $e) {
    echo "Ma'lumot noto'g'ri";
} catch (PDOException $e) {
    echo "Bazada muammo";
}

throw — o'zingiz xato tashlash

Funksiya o'z vazifasini bajara olmasa — false qaytarish o'rniga xato tashlash yaxshiroq:

<?php

function pulYechish(int $balans, int $summa): int
{
    if ($summa <= 0) {
        throw new InvalidArgumentException("Summa musbat bo'lishi kerak");
    }
    if ($summa > $balans) {
        throw new RuntimeException("Mablag' yetarli emas");
    }
    return $balans - $summa;
}

try {
    $yangi = pulYechish(50000, 80000);
} catch (RuntimeException $e) {
    echo $e->getMessage();   // Mablag' yetarli emas
}

Nima uchun false qaytarish yomon? Chaqiruvchi uni tekshirishni unutishi mumkin — va false jimgina 0 ga aylanib, hisob-kitobni buzadi. Exception ni esa e'tiborsiz qoldirib bo'lmaydi.

O'z xato turingiz

<?php

class MablagYetarliEmas extends RuntimeException {}

throw new MablagYetarliEmas("Balansda 50 000 so'm bor");

Endi aynan shu holatni boshqalardan ajratib ushlash mumkin. Classlar haqida 28-darsdan boshlab.


finally — har doim bajariladi

<?php

$f = fopen(__DIR__ . '/data.csv', 'r');

try {
    while (($qator = fgetcsv($f)) !== false) {
        qatorniQaytaIshla($qator);   // xato tashlashi mumkin
    }
} finally {
    fclose($f);   // xato bo'lsa ham, bo'lmasa ham fayl yopiladi
}

finally — "nima bo'lishidan qat'i nazar, oxirida buni bajar". Resurslarni (fayl, qulf, ulanish) yopish uchun.


Xatolarni ko'rsatish sozlamalari

Ishlab chiqishda (localhost)

<?php

ini_set('display_errors', '1');
error_reporting(E_ALL);

Barcha xatolar va ogohlantirishlar ekranda. Har bir Warning ni ko'rib, tuzatasiz.

Serverda (production)

php.ini:

display_errors = Off
log_errors = On
error_log = /var/log/php/xatolar.log
error_reporting = E_ALL

Foydalanuvchi hech qanday texnik ma'lumot ko'rmaydi. Hamma xatolar faylga yoziladi — siz u yerdan o'qiysiz.

[!WARNING] Serverda display_errors = On — jiddiy xavfsizlik teshigi. Yuqoridagi misolda baza paroli aynan shu sababli oshkor bo'ldi.


Global xato ushlagich

Hamma joyda try/catch yozish shart emas. Ushlanmay qolgan har qanday xato uchun bitta umumiy ushlagich:

<?php
// bootstrap.php — har bir sahifa boshida require qilinadi

set_exception_handler(function (Throwable $e): void {
    error_log(sprintf(
        "[%s] %s: %s in %s:%d",
        date('Y-m-d H:i:s'),
        $e::class,
        $e->getMessage(),
        $e->getFile(),
        $e->getLine()
    ));

    http_response_code(500);
    echo "Kechirasiz, xatolik yuz berdi. Biz allaqachon tuzatyapmiz.";
});

// Warning va Notice larni ham exception ga aylantirish
set_error_handler(function (int $daraja, string $xabar, string $fayl, int $qator): bool {
    throw new ErrorException($xabar, 0, $daraja, $fayl, $qator);
});

Natija:

  • Foydalanuvchi — do'stona xabar va to'g'ri 500 status
  • Siz — logda to'liq texnik ma'lumot
  • Warning lar endi jimgina o'tib ketmaydi

Laravel va Symfony ichida aynan shunday mexanizm ishlaydi.


Qachon ushlash, qachon ushlamaslik

Ushlang, agar xatoni ma'noli qayta ishlay olsangiz:

  • Foydalanuvchiga tushunarli xabar ko'rsatish ("Bu email allaqachon ro'yxatdan o'tgan")
  • Muqobil yo'l (keshdan olish, qayta urinish)
  • Resursni yopish (finally)

Ushlamang, agar faqat yashirish uchun bo'lsa:

<?php

// ❌ Eng yomon kod
try {
    muhimIsh();
} catch (Throwable $e) {
    // hech narsa
}

Xato yo'qolmadi — u shunchaki ko'rinmay qoldi. Endi sayt noto'g'ri ishlaydi va nima uchunligini hech kim bilmaydi. Ushlay olmasangiz — global ushlagichga qoldiring.


Nima uchun shunday?

Nima uchun PHP da bir vaqtning o'zida eski "Warning" tizimi ham, zamonaviy "Exception" lar ham bor?

PHP dastlab xatolarni ekranga matn chiqarish orqali bildirgan — 1990-yillardagi sahifa skriptlari uchun bu yetarli edi. OOP va katta ilovalar paydo bo'lgach, exception lar qo'shildi (PHP 5). PHP 7 da ko'plab "Fatal error" lar ushlanadigan Error ga aylandi. Eski Warning tizimini butunlay olib tashlash millionlab saytlarni buzardi — shuning uchun ikkalasi yonma-yon yashaydi, set_error_handler esa ularni bitta tizimga birlashtiradi.


Xulosa

Eslab qoling:

  • try { } catch (Tur $e) { } finally { }
  • Exception — kutilgan holatlar, Error — kod xatolari; ikkalasi ham Throwable
  • Muvaffaqiyatsizlikda false emas, throw new ...Exception
  • Ishlab chiqishda: display_errors=1, E_ALL; serverda: display_errors=Off, log_errors=On
  • set_exception_handler — foydalanuvchiga do'stona xabar, logga to'liq ma'lumot
  • Bo'sh catch yozmang

Keyingi dars

28-dars: Class va ob'ekt — xususiyat, metod, $this, konstruktor