Exception Handling

Configuration

config/exception.php

return [
    // Configure the exception handling class here
    '' => support\exception\Handler::class,
];

In multi-app mode, you can configure an exception handling class for each application individually. Refer to Multi-App for more details.

Default Exception Handling Class

In webman, exceptions are handled by the support\exception\Handler class by default. You can modify the configuration file config/exception.php to change the default exception handling class. The exception handling class must implement the Webman\Exception\ExceptionHandlerInterface interface.

interface ExceptionHandlerInterface
{
    /**
     * Log the exception
     * @param Throwable $e
     * @return mixed
     */
    public function report(Throwable $e);

    /**
     * Render the response
     * @param Request $request
     * @param Throwable $e
     * @return Response
     */
    public function render(Request $request, Throwable $e): Response;
}

Rendering Responses

The render method in the exception handling class is used to render the response.

If the debug value is set to true in the configuration file config/app.php (referred to as app.debug=true from now on), the detailed exception information will be returned. Otherwise, a simplified version of the exception information will be returned.

If the request expects a JSON response, the exception information will be returned in JSON format, like this:

{
    "code": "500",
    "msg": "Exception message"
}

If app.debug=true, the JSON data will also include a trace field that contains detailed stack trace information.

You can write your own exception handling class to modify the default exception handling logic.

BusinessException

Sometimes, we want to stop the request inside a nested function and return an error message to the client. In such cases, you can throw a BusinessException.

For example:

<?php
namespace app\controller;

use support\Request;
use support\exception\BusinessException;

class FooController
{
    public function index(Request $request)
    {
        $this->checkInput($request->post());
        return response('hello index');
    }

    protected function checkInput($input)
    {
        if (!isset($input['token'])) {
            throw new BusinessException('Parameter error', 3000);
        }
    }
}

In the above example, it will return:

{"code": 3000, "msg": "Parameter error"}

Note
The BusinessException does not need to be caught by the business logic. It will be automatically caught by the framework and the appropriate output will be returned based on the request type.

Custom Business Exception

If the above response does not meet your requirements, for example, if you want to change msg to message, you can define a custom MyBusinessException.

Create a new file app/exception/MyBusinessException.php with the following content:

<?php

namespace app\exception;

use support\exception\BusinessException;
use Webman\Http\Request;
use Webman\Http\Response;

class MyBusinessException extends BusinessException
{
    public function render(Request $request): ?Response
    {
        // Return JSON data for JSON requests
        if ($request->expectsJson()) {
            return json(['code' => $this->getCode() ?: 500, 'message' => $this->getMessage()]);
        }
        // Return a page for non-JSON requests
        return new Response(200, [], $this->getMessage());
    }
}

Now, when you throw a MyBusinessException in your code like this:

use app\exception\MyBusinessException;

throw new MyBusinessException('Parameter error', 3000);

A JSON request will receive a JSON response like this:

{"code": 3000, "message": "Parameter error"}

Note
BusinessException exceptions are business exceptions (e.g., user input parameter errors) and are expected. Therefore, the framework does not consider them as fatal errors and does not log them.

Summary

You can consider using BusinessException whenever you want to interrupt the current request and return information to the client.