Nicolas Grekas
Contributed by Nicolas Grekas in #65948 , #65887 and #66021

FrameworkBundle is the glue between Symfony components and Symfony applications. Over the years, we used it to define the configuration and services of most components. This caused some issues:

  • Adding a feature to a component (e.g. a new mailer option) required changes in both the component and FrameworkBundle;
  • Some sections depended on others in non-obvious ways (e.g. enabling forms also enabled validation);
  • Config files had an unneeded root level (e.g. the config in config/packages/mailer.yaml starts with framework:).

In Symfony 8.2, each component ships its own bundle with its configuration, its services and the classes related to them. There are 29 new bundles such as CacheBundle, HttpClientBundle, MailerBundle, MessengerBundle, etc.

What This Means for Your Applications

You don't have to change anything. The new bundles are registered automatically when their component is installed, so there's nothing to add to config/bundles.php. Thanks to a new configuration aliasing feature, all your existing framework.* keys (framework.mailer, framework.messenger, etc.) keep working exactly as before and nothing is deprecated.

However, if you prefer, you can now remove the framework level from these config files:

1
2
3
4
5
6
# config/packages/mailer.yaml
- framework:
-     mailer:
-         dsn: '%env(MAILER_DSN)%'
+ mailer:
+     dsn: '%env(MAILER_DSN)%'

The same works in PHP config files:

1
2
3
4
5
6
7
8
// config/packages/mailer.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'mailer' => [
        'dsn' => env('MAILER_DSN'),
    ],
]);

Most keys keep their name at the root level, except these: framework.assets is now asset, framework.translator is now translation and framework.workflows is now workflow.

The lock and semaphore keys also accept a DSN directly, as a shortcut for their resources option (before, a string value at the root level was silently ignored):

1
2
# config/packages/lock.yaml
lock: '%env(LOCK_DSN)%'

Other minor changes you might notice:

  • debug:config framework mailer no longer works; use debug:config mailer instead;
  • Enabling forms no longer enables validation automatically; installing symfony/validator does;
  • Some classes moved from FrameworkBundle to their components and the old ones are deprecated. The ones you are most likely to use in your apps are RedirectController (now in the Routing component), TemplateController (now in TwigBundle), AsRoutingConditionService (now in the Routing component) and RouteLoaderInterface (replaced by the #[AsRouteLoader] attribute of the Routing component).

Registering 29 more bundles doesn't slow down your apps. Most of these bundles are only needed when compiling the container, so in production Symfony no longer creates their objects on each request (it only does it when some code asks for them).

New Config Features for Bundle Authors

Nicolas Grekas HypeMC
Contributed by Nicolas Grekas and HypeMC in #65884 and #64876

This work required some new features in the Config component that you can also use in your own bundles. The aliasOf() method makes a root-level node forward its value to the configuration of another extension. This is how framework.workflows keeps working while the Workflow component now owns the workflow configuration:

1
2
3
4
$rootNode
    ->children()
        ->variableNode('workflows')->aliasOf('workflow')->end()
    ->end();

The new appendFromCallback() method lets you append several nodes from a callback without breaking the fluent chain of the tree builder. This is useful when the same group of options is repeated in different parts of your config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$rootNode
    ->children()
        ->arrayNode('default_options')
            ->children()
                ->appendFromCallback($this->addCommonOptions(...))
            ->end()
        ->end()
    ->end();

// ...

private function addCommonOptions(NodeBuilder $builder): void
{
    $builder
        ->integerNode('max_redirects')->end()
        ->scalarNode('http_version')->end()
    ;
}
Published in #Living on the edge