Angelov Dejan
Contributed by Angelov Dejan in #48352 and #48747

PHP attributes allow to define machine-readable metadata in your code instead of having to add that configuration in a separate file. With each new Symfony version we add more attributes that you can optionally use. In Symfony 6.3 we've added new attributes to configure HTTP exceptions.

Currently, to create your own HTTP exceptions, you need to implement HttpExceptionInterface (or extend the HttpException base class) and configure it in the framework.exceptions option:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use App\Domain\Exception\Order\OrderNotFound;
use Symfony\Component\HttpKernel\Exception\HttpException;

class OrderNotFound extends HttpException
{
    public static function create(string $id): self
    {
        return new self(
            statusCode: Response::HTTP_NOT_FOUND,
            message: sprintf('The order "%s" could not be found.', $id),
            headers: ['x-header' => 'foo'],
        );
    }
}
1
2
3
4
5
6
# config/packages/exceptions.yaml
framework:
    exceptions:
        App\Domain\Exception\Order\OrderNotFound:
            log_level: 'debug'
            status_code: 404

In Symfony 6.3, the above code and configuration still work, but you can optionally replace them by the following PHP attributes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use App\Domain\Exception\Order\OrderNotFound;
use Psr\Log\LogLevel;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\WithHttpStatus;
use Symfony\Component\HttpKernel\Attribute\WithLogLevel;

#[WithHttpStatus(Response::HTTP_NOT_FOUND, headers: ['x-header' => 'foo'])]
#[WithLogLevel(LogLevel::DEBUG)]
class OrderNotFound extends \Exception
{
    public function __construct(Order $order)
    {
        parent::__construct(
            message: sprintf('The order "%s" could not be found.', $order->getId())
        );
    }

    // ...
}

That's all. You no longer need to configure anything in the framework.exceptions option. In addition to having all the information in a single file, this also removes the coupling of your HTTP exceptions with the HttpKernel component. In other words, your domain exceptions are decoupled from the infrastructure code.

If your application uses both attributes and the framework.exceptions option, the configuration will have more priority than the attributes.

Published in #Living on the edge