Home / Symfony / New in Symfony 8.1: DX Improvements (Part 1)

New in Symfony 8.1: DX Improvements (Part 1)

Every Symfony release ships dozens of small developer experience (DX) improvements
that make day-to-day work more pleasant. This article highlights some of those
improvements in Symfony 8.1.

Copy Requests as cURL Commands


Sylvain Combraque
Contributed by
Sylvain Combraque
in
#62320

When debugging an issue, you often need to replay a request outside the browser to
tweak it or share it with a colleague. The profiler already shows every detail of
the request, but rebuilding a curl command by hand from its headers, cookies,
and body is tedious and error-prone.

Symfony 8.1 adds a “Copy as cURL” button to the Request/Response tab of the
profiler. It generates a ready-to-run command that includes the HTTP method, full
URL, request headers, cookies, and body for write requests. Paste it into your
terminal to reproduce the exact same request in seconds.

Symfony 8.1 profiler includes a feature to copy requests as cURL so you can replay them

A More Accessible Web Debug Toolbar


Martin Gilbert
Contributed by
Martin Gilbert
in
#63943

The Web Debug Toolbar revealed its panels only on mouse hover, making the
information within them inaccessible to keyboard and screen reader users.

Symfony 8.1 makes the toolbar fully keyboard-navigable and adds the WAI-ARIA
semantics that assistive technologies rely on. You can now move between items with
the arrow keys, open a panel with the Down arrow, and close it with Escape. Panels
also open on keyboard focus, not just on hover, and each item exposes a descriptive
label and the proper toolbar and dialog roles.

Sorting Routes in the debug:router Command


Michaël Thieulin
Contributed by
Michaël Thieulin
in
#63905

The debug:router command lists routes in the order Symfony evaluates them,
making it hard to locate a specific route in a large application. Sorting the list
meant piping the output through tools like sort, which broke the clickable
links to the route definitions.

Symfony 8.1 adds a --sort option to sort routes by any column: name,
path, method, scheme, or host:

$ php bin/console debug:router --sort=path

# the option is case-insensitive and works for all output formats
$ php bin/console debug:router --sort=name --format=json

Sorting Scheduled Messages by Next Run Date


yoann aparici
Contributed by
yoann aparici
in
#63135

When you have many scheduled tasks, the debug:scheduler command lists them in
the order they were defined, making it hard to tell which one runs next or spot
tasks that fire at the same time.

Symfony 8.1 adds a --sort option that orders recurring messages by their next
run date, so the task that runs soonest appears first:

$ php bin/console debug:scheduler --sort

Matching Transport Names with Regular Expressions


Santiago San Martin
Contributed by
Santiago San Martin
in
#62875

The messenger:consume command expects you to list the transports to consume
by their exact names. In applications with many similarly named transports (for
example, one transport per scheduler), spelling out every name is tedious.

In Symfony 8.1, the transport names argument also accepts regular expressions,
which are matched against configured transport names:

# consume all transports whose name starts with "scheduler_"
$ php bin/console messenger:consume 'scheduler_.*'

# use the (?i) flag for case-insensitive matching
$ php bin/console messenger:consume '(?i)async.*'

# you can still mix regular expressions and explicit names
$ php bin/console messenger:consume 'scheduler_.*' async

Each pattern is anchored (it must match the full transport name), and matched
transports are consumed in their configuration order. Regular expressions are only
supported when the command runs non-interactively.

Mocking Non-Shared Services in Tests


HypeMC
Contributed by
HypeMC
in
#62909

In functional tests, you can replace a service with a test double by calling
getContainer()->set(). Until now this only worked for shared services;
non-shared services, which are created fresh every time they are requested, could
not be replaced.

Symfony 8.1 lifts that restriction. Because a non-shared service returns a new
instance on each request, you replace it with a closure that acts as a factory
instead of a single object. The closure runs every time the service is fetched:

use SymfonyBundleFrameworkBundleTestKernelTestCase;

class OrderProcessorTest extends KernelTestCase
{
    public function testProcess(): void
    {
        $container = static::getContainer();

        $container->set('app.payment_gateway', function () {
            return $this->createMock(PaymentGateway::class);
        });

        // ...
    }
}

Asserting Flash Messages in Tests


Alex Rock
Contributed by
Alex Rock
in
#60008

Checking that a controller added a flash message used to require several steps:
fetching the request, making sure it has a session, retrieving the flash bag, and
inspecting its messages.

Symfony 8.1 replaces all of that with a single assertion called
assertSessionHasFlashMessage():

// assert that a flash message of the given type exists
$this->assertSessionHasFlashMessage('success');

// optionally, assert its exact content too
$this->assertSessionHasFlashMessage('success', 'Your changes were saved.');

Controlling Time in Messenger Stamps


Mohammad Eftekhari
Contributed by
Mohammad Eftekhari
in
#62572

The DelayStamp and RedeliveryStamp classes computed time using PHP’s native
time() function and DateTimeImmutable objects, making any logic that
depends on their values impossible to test deterministically.

Symfony 8.1 updates both stamps to rely on the Clock component instead. In your
tests, you can now freeze time with MockClock and get fully predictable delays
and redelivery timestamps:

use SymfonyComponentClockClock;
use SymfonyComponentClockMockClock;
use SymfonyComponentMessengerStampRedeliveryStamp;

Clock::set(new MockClock('2026-01-01 00:00:00'));

// the stamp now reads the current time from the frozen clock
$stamp = new RedeliveryStamp(retryCount: 1);


Sponsor the Symfony project.
Tagged:

Leave a Reply

Your email address will not be published. Required fields are marked *