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

New in Symfony 8.1: Misc Improvements (Part 1)

In addition to the main features announced in previous posts of this series,
Symfony 8.1 includes many smaller improvements that make day-to-day work
easier. This post highlights the first batch.

Convert Between UUIDv7 and UUIDv4


Nicolas Grekas
Contributed by
Nicolas Grekas
in
#63593

UUIDv7 identifiers are time-ordered, making them ideal as database primary keys.
However, that property also leaks record creation times when you expose those
identifiers in APIs. The new Uuid47Transformer class lets you store UUIDv7
internally while emitting UUIDv4-looking identifiers at your application boundaries:

use SymfonyComponentUidUuid47Transformer;
use SymfonyComponentUidUuidV7;

// the secret must be at least 16 bytes; longer secrets are hashed automatically
$transformer = new Uuid47Transformer($secret);

$uuid = new UuidV7();
// returns a UuidV4 instance that hides the timestamp information
$external = $transformer->encode($uuid);

// returns the original UuidV7 instance (when using the same secret)
$original = $transformer->decode($external);

The conversion masks the UUIDv7 timestamp with a keyed SipHash-2-4 digest,
making it reversible only with the same secret. When using FrameworkBundle, the
transformer is registered as a service automatically (using kernel.secret as
the key), so you can inject it anywhere by type-hinting Uuid47Transformer.

Convert Scalar Types During Denormalization


Jeroen Spee
Contributed by
Jeroen Spee
in
#52173

Some data formats represent all values as strings (e.g. HTTP query strings or
form data). When deserializing XML and CSV contents, Symfony already
casts those strings to the int, float or bool types expected by the
target properties. In Symfony 8.1, you can enable this behavior for any format
via the new ENABLE_TYPE_CONVERSION context option:

use SymfonyComponentSerializerNormalizerAbstractObjectNormalizer;
// ...

// all values are strings, as in an HTTP query string
$data = ['age' => '39', 'sportsperson' => '1'];

// 'age' is cast to int and 'sportsperson' to bool to match the Person property types
$person = $serializer->denormalize($data, Person::class, context: [
    AbstractObjectNormalizer::ENABLE_TYPE_CONVERSION => true,
]);

Set the option to false to disable the conversion, even for the xml and
csv formats. The option is also available in the serializer context builders.

Configurable Default Action in HTML Sanitizer


Adrien Roches
Contributed by
Adrien Roches
in
#57653

When the HTML sanitizer finds a tag that is not part of the configuration, it
drops the tag and all its children. In Symfony 8.1, you can change this
behavior with the new default_action option, which accepts drop (the
current default), block (remove the tag but keep its children) and allow:

# config/packages/html_sanitizer.yaml
framework:
    html_sanitizer:
        sanitizers:
            app.post_sanitizer:
                # ...

                # remove unconfigured tags, but keep processing their children
                default_action: 'block'
                # remove <figure> tags and their children entirely
                drop_elements: ['figure']

Reset the Kernel Between FrankenPHP Requests


Nicolas Grekas
Contributed by
Nicolas Grekas
in
#64055

In FrankenPHP worker mode, the same kernel instance handles every request for
the lifetime of the worker process. This is great for performance, but any state
kept by services that don’t implement ResetInterface may leak across requests.

Symfony 8.1 adds an opt-in feature: defining the FRANKENPHP_RESET_KERNEL
environment variable makes the runtime clone the kernel after each request, so
the next one starts with a fresh kernel and container:

# define this env var where you configure your FrankenPHP workers
FRANKENPHP_RESET_KERNEL=1

The default behavior doesn’t change: the kernel is still reused across
requests unless you set this variable. Resetting the kernel has a measurable
throughput cost on “hello world” benchmarks, but it’s still several times faster
than the classic non-worker mode and it restores full per-request isolation.

Null-Safe Array Access in Expressions


Alex Rothberg
Contributed by
Alex Rothberg
in
#62754

The ExpressionLanguage component supports the null-safe operator for property
access (foo?.bar) and method calls (foo?.getBar()). Symfony 8.1
completes the feature with null-safe array access using the same
?.[...] syntax as JavaScript optional chaining:

// before: this throws an exception when getItems() returns null
$expressionLanguage->evaluate('fruit.getItems()[0]', ['fruit' => $fruit]);

// now: this returns null instead of throwing an exception
$expressionLanguage->evaluate('fruit.getItems()?.[0]', ['fruit' => $fruit]);

// you can combine it with the other null-safe operators
$expressionLanguage->evaluate('order?.getItems()?.[0]?.getName()', ['order' => $order]);

Outline-Style Console Blocks


Guillaume Van Der Putten
Contributed by
Guillaume Van Der Putten
in
#63546

The success(), error(), warning() and similar SymfonyStyle
methods fill the entire line with a background color, which can be hard to read
on terminals with custom color schemes or high-contrast accessibility settings.
Symfony 8.1 adds outline-style alternatives that display a colored border
around the message while keeping the default text color:

// outlined alternatives exist for all the result methods:
// outlineSuccess(), outlineError(), outlineWarning(), outlineNote(),
// outlineInfo() and outlineCaution()
$io->outlineSuccess('Operation completed successfully.');
$io->outlineError('Something went wrong.');

// use outlineBlock() to customize the title and the colors
$io->outlineBlock('Deployment finished in 3.2s', 'Deploy', 'fg=cyan');

Disable Trailing Slash on Prefixed Root Routes


vvaswani
Contributed by
vvaswani
in
#63689

When you apply a prefix to a collection of routes defined with the PHP DSL, the
root route of the collection always gets a trailing slash (e.g. /categories/).
In Symfony 8.1, the prefix() method accepts a new trailingSlashOnRoot
argument (already available in YAML/XML imports) to disable this:

// config/routes.php
use SymfonyComponentRoutingLoaderConfiguratorRoutingConfigurator;

return function (RoutingConfigurator $routes): void {
    // this generates /categories (instead of /categories/) and /categories/{id}
    $routes->collection('category_')
        ->prefix('/categories', trailingSlashOnRoot: false)
        ->add('index', '/')
        ->add('show', '/{id}');
};

Get Parent Role Names


Pierre-Emmanuel CAPEL
Contributed by
Pierre-Emmanuel CAPEL
in
#53998

When using hierarchical roles, the getReachableRoleNames() method returns
all the roles inherited by the given roles. Symfony 8.1 adds the inverse operation:
getParentRoleNames() returns the roles that inherit from the given roles.
This is useful for finding which roles can access everything a given role can access.

Consider the following role hierarchy:

# config/packages/security.yaml
security:
    role_hierarchy:
        ROLE_ADMIN: ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN

use SymfonyComponentSecurityCoreRoleRoleHierarchyInterface;

class RoleService
{
    public function __construct(
        private RoleHierarchyInterface $roleHierarchy,
    ) {
    }

    public function getParentRoles(array $roles): array
    {
        // for ['ROLE_USER'] this returns an array with 'ROLE_USER', 'ROLE_ADMIN'
        // and 'ROLE_SUPER_ADMIN', because those roles inherit ROLE_USER permissions
        return $this->roleHierarchy->getParentRoleNames($roles);
    }
}

Mark Classes as Safe for Twig’s Escaper


Jérôme Tamarelle
Contributed by
Jérôme Tamarelle
in
#63929

If a value object wraps pre-escaped or trusted HTML content, you have to apply
the raw Twig filter every time you output it in a template. In Symfony 8.1,
you can mark the class as safe for Twig’s escaper using the new
twig.safe_class resource tag, so its output is no longer escaped:

# config/services.yaml
services:
    AppTwigHtmlString:
        resource_tags:
            - { name: twig.safe_class, strategy: html }

Unlike regular service tags, resource tags are attached to the class itself, not
to a service. The strategy option accepts a single escaping strategy or a
list of them, and you can tag the same class several times to mark it as safe
for multiple strategies.

New ContainerAwareInterface Contract


Nicolas Grekas
Contributed by
Nicolas Grekas
in
#63663

Symfony 8.1 adds a minimal ContainerAwareInterface to
symfony/service-contracts, providing a standard way for objects to expose
their service container:

namespace SymfonyContractsService;

use PsrContainerContainerInterface;

interface ContainerAwareInterface
{
    public function getContainer(): ContainerInterface;
}

The console Application class provided by FrameworkBundle implements it
(booting the kernel if needed). This enables decoupled use cases such as
Messenger parallel workers, which need to bootstrap the application and access
the container to resolve the message bus.


Sponsor the Symfony project.
Tagged:

Leave a Reply

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