A hosted service is the worker you would have written

A long-running process alongside the web application is a first-class concept here, rather than a separate supervisor configuration and a script.

public class OrderConsumer : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var message = await _queue.ReceiveAsync(stoppingToken);
            await _handler.Handle(message, stoppingToken);
        }
    }
}

// services.AddHostedService<OrderConsumer>();

The cancellation token threaded through every call is what makes shutdown graceful — a SIGTERM cancels it and the loop finishes the current message rather than being killed mid-write. That is the part a PHP worker builds by hand with signal handlers, and getting it right is fiddly. Running the consumer in the same process as the web application couples their scaling, which is a decision worth making deliberately rather than by default.