مقاله

سلسله‌مراتب exception سفارشی، render در handler، logging و تفاوت Error و Exception در PHP 8.

Error Handling حرفه‌ای در PHP — Exception، try-catch و Laravel

Error Handling حرفه‌ای در PHP — Exception، try-catch و Laravel

خطا در production اجتناب‌ناپذیر است — پرداخت ناموفق، رکورد duplicate، timeout API. تفاوت کد حرفه‌ای در این است که خطا قابل پیش‌بینی، قابل log و قابل نمایش امن به کاربر باشد.

Throwable — Exception و Error

PHP 7+ هر دو implements Throwable. Error برای engine (TypeError، ParseError) — معمولاً catch نمی‌کنید. Exception برای business و application.

try {
    $order = $service->place($data);
} catch (PaymentFailedException $e) {
    return back()->withErrors(['payment' => $e->getMessage()]);
} catch (Throwable $e) {
    report($e);
    throw $e;
}

Exception سفارشی domain

class OrderNotFoundException extends Exception
{
    public function __construct(public readonly int $orderId)
    {
        parent::__construct("Order {$orderId} not found");
    }
}

catch مشخص — نه Exception generic برای همه.

چه زمانی throw؟

  • وضعیت غیرقابل ادامه — نه expected flow
  • caller نمی‌تواند recover کند
  • برای validation ورودی — Form Request نه exception

Laravel Exception Handler

bootstrap/app.php یا App\Exceptions\Handler (نسخه‌های قدیم):

->withExceptions(function (Exceptions $exceptions) {
    $exceptions->reportable(function (PaymentFailedException $e) {
        // custom log context
    });

    $exceptions->render(function (OrderNotFoundException $e, Request $request) {
        if ($request->expectsJson()) {
            return response()->json(['message' => $e->getMessage()], 404);
        }
    });
})

API error handling.

report vs render

  • report — Sentry، log، Slack
  • render — پاسخ HTTP به کاربر

finally و transaction

DB::transaction(function () {
    // auto rollback on exception
});

Transaction — ترجیح بر try-finally دستی.

نمایش خطا production

APP_DEBUG=false — stack trace به کاربر نه. پیام عمومی + id در log.

logging context

Log::error('Payment failed', [
    'order_id' => $order->id,
    'gateway' => 'zarinpal',
    'code' => $e->getCode(),
]);

تست exception

expect(fn () => $action->handle($badData))
    ->toThrow(OrderNotFoundException::class);

تست Action.

anti-patterns

  • catch خالی
  • exception برای control flow عادی
  • پیام خطا با SQL یا path
  • @ suppression operator

جمع‌بندی

Exception domain-specific + handler مرکزی. PSR: PSR-3 log. Clean: Clean Code.

exception strategy