Symfony 7.3 is a minor release. According to the Symfony release process, there should be no significant
backward compatibility breaks. Minor backward compatibility breaks are prefixed in this document with
[BC BREAK], make sure your code is compatible with these entries before upgrading.
Read more about this in the Symfony documentation.
If you're upgrading from a version below 7.2, follow the 7.2 upgrade guide first.
Bundles
Bridges
Components
- AssetMapper
- Console
- DependencyInjection
- HttpFoundation
- Ldap
- OptionsResolver
- PropertyInfo
- Security
- Notifier
- Serializer
- TypeInfo
- Validator
- VarDumper
- VarExporter
- Workflow
ImportMapRequireCommandnow takesprojectDiras a required third constructor argument
-
Omitting parameter types or returning a non-integer value from a
\Closureset viaCommand::setCode()method is deprecatedBefore:
$command->setCode(function ($input, $output) { // ... });
After:
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; $command->setCode(function (InputInterface $input, OutputInterface $output): int { // ... return 0; });
-
Deprecate methods
Command::getDefaultName()andCommand::getDefaultDescription()in favor of the#[AsCommand]attribute -
#[AsCommand]attribute is now marked as@final; you should use separate attributes to add more logic to commands
- Deprecate
ContainerBuilder::getAutoconfiguredAttributes()in favor of thegetAttributeAutoconfigurators()method.
- Deprecate the
DoctrineExtractor::getTypes()method, useDoctrineExtractor::getType()instead
-
Not setting the
framework.property_info.with_constructor_extractoroption explicitly is deprecated because its default value will change in version 8.0 -
Deprecate the
--show-argumentsoption of thecontainer:debugcommand, as arguments are now always shown -
Deprecate the
framework.validation.cacheconfig option -
Deprecate the
RateLimiterFactoryautowiring aliases, useRateLimiterFactoryInterfaceinstead -
Deprecate setting the
framework.profiler.collect_serializer_dataconfig option tofalseWhen set to
true, normalizers must be injected using theNormalizerInterface, and not using any concrete implementation.Before:
public function __construct(ObjectNormalizer $normalizer) {}
After:
public function __construct(#[Autowire('@serializer.normalizer.object')] NormalizerInterface $normalizer) {}
-
The XML routing configuration files (
errors.xmlandwebhook.xml) are deprecated, use their PHP equivalent ones:Before:
when@dev: _errors: resource: '@FrameworkBundle/Resources/config/routing/errors.xml' prefix: /_error webhook: resource: '@FrameworkBundle/Resources/config/routing/webhook.xml' prefix: /webhook
After:
when@dev: _errors: resource: '@FrameworkBundle/Resources/config/routing/errors.php' prefix: /_error webhook: resource: '@FrameworkBundle/Resources/config/routing/webhook.php' prefix: /webhook
Request::getPreferredLanguage()now favors a more preferred language above exactly matching a locale
- Deprecate
LdapUser::eraseCredentials()in favor of__serialize()
- Deprecate defining nested options via
setDefault(), usesetOptions()instead
Before
$resolver->setDefault('option', function (OptionsResolver $resolver) {
// ...
});After
$resolver->setOptions('option', function (OptionsResolver $resolver) {
// ...
});- Deprecate the
Typeclass, useSymfony\Component\TypeInfo\Typeclass fromsymfony/type-infoinstead - Deprecate the
PropertyTypeExtractorInterface::getTypes()method, usePropertyTypeExtractorInterface::getType()instead - Deprecate the
ConstructorArgumentTypeExtractorInterface::getTypesFromConstructor()method, useConstructorArgumentTypeExtractorInterface::getTypeFromConstructor()instead
-
Deprecate
UserInterface::eraseCredentials()andTokenInterface::eraseCredentials(); erase credentials e.g. using__serialize()insteadBefore:
public function eraseCredentials(): void { }
After:
#[\Deprecated] public function eraseCredentials(): void { } // If your eraseCredentials() method was used to empty a "password" property: public function __serialize(): array { $data = (array) $this; unset($data["\0".self::class."\0password"]); return $data; }
-
Add argument
$votetoVoterInterface::vote()and toVoter::voteOnAttribute(); it should be used to report the reason of a vote. E.g:protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool { $vote?->addReason('A brief explanation of why access is granted or denied, as appropriate.'); }
-
Add argument
$accessDecisiontoAccessDecisionManagerInterface::decide()andAuthorizationCheckerInterface::isGranted(); it should be used to report the reason of a decision, including all the related votes. -
Add discovery support to
OidcTokenHandlerandOidcUserInfoTokenHandler
- Deprecate the
security.hide_user_not_foundconfig option in favor ofsecurity.expose_security_errors
- Deprecate the
Sms77transport, useSevenIoinstead
- Deprecate the
CompiledClassMetadataFactoryandCompiledClassMetadataCacheWarmerclasses
- Deprecate constructing a
CollectionTypeinstance as a list that is not an array - Deprecate the third
$asListargument ofTypeFactoryTrait::iterable(), useTypeFactoryTrait::list()instead
-
Deprecate defining custom constraints not supporting named arguments
Before:
use Symfony\Component\Validator\Constraint; class CustomConstraint extends Constraint { public function __construct(array $options) { // ... } }
After:
use Symfony\Component\Validator\Attribute\HasNamedArguments; use Symfony\Component\Validator\Constraint; class CustomConstraint extends Constraint { #[HasNamedArguments] public function __construct($option1, $option2, $groups, $payload) { // ... } }
-
Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead
Before:
new NotNull([ 'groups' => ['foo', 'bar'], 'message' => 'a custom constraint violation message', ])
After:
new NotNull( groups: ['foo', 'bar'], message: 'a custom constraint violation message', )
- Deprecate
ResourceCaster::castCurl(),ResourceCaster::castGd()andResourceCaster::castOpensslX509() - Mark all casters as
@internal
- Deprecate using
ProxyHelper::generateLazyProxy()when native lazy proxies can be used - the method should be used to generate abstraction-based lazy decorators only - Deprecate
LazyGhostTraitandLazyProxyTrait, use native lazy objects instead - Deprecate
ProxyHelper::generateLazyGhost(), use native lazy objects instead
-
The XML routing configuration files (
profiler.xmlandwdt.xml) are deprecated, use their PHP equivalent ones:Before:
when@dev: web_profiler_wdt: resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' prefix: /_wdt web_profiler_profiler: resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' prefix: /_profiler
After:
when@dev: web_profiler_wdt: resource: '@WebProfilerBundle/Resources/config/routing/wdt.php' prefix: /_wdt web_profiler_profiler: resource: '@WebProfilerBundle/Resources/config/routing/profiler.php prefix: /_profiler
-
Deprecate
Event::getWorkflow()methodBefore:
use Symfony\Component\Workflow\Attribute\AsCompletedListener; use Symfony\Component\Workflow\Event\CompletedEvent; class MyListener { #[AsCompletedListener('my_workflow', 'to_state2')] public function terminateOrder(CompletedEvent $event): void { $subject = $event->getSubject(); if ($event->getWorkflow()->can($subject, 'to_state3')) { $event->getWorkflow()->apply($subject, 'to_state3'); } } }
After:
use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Component\Workflow\Attribute\AsCompletedListener; use Symfony\Component\Workflow\Event\CompletedEvent; use Symfony\Component\Workflow\WorkflowInterface; class MyListener { public function __construct( #[Target('your_workflow_name')] private readonly WorkflowInterface $workflow, ) { } #[AsCompletedListener('your_workflow_name', 'to_state2')] public function terminateOrder(CompletedEvent $event): void { $subject = $event->getSubject(); if ($this->workflow->can($subject, 'to_state3')) { $this->workflow->apply($subject, 'to_state3'); } } }
Or:
use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\DependencyInjection\Attribute\AutowireLocator; use Symfony\Component\Workflow\Attribute\AsTransitionListener; use Symfony\Component\Workflow\Event\TransitionEvent; class GenericListener { public function __construct( #[AutowireLocator('workflow', 'name')] private ServiceLocator $workflows ) { } #[AsTransitionListener()] public function doSomething(TransitionEvent $event): void { $workflow = $this->workflows->get($event->getWorkflowName()); } }