Symfony 6.4 and Symfony 7.0 are released simultaneously at the end of November 2023. According to the Symfony release process, both versions have the same features, but Symfony 7.0 doesn't include any deprecated features. To upgrade, make sure to resolve all deprecation notices. Read more about this in the Symfony documentation.
Symfony 7.0 introduced many native return and property types. Read the announcement blogpost on how to quickly make your code compatible.
Bundles
Bridges
Components
- Cache
- Config
- Console
- DependencyInjection
- DomCrawler
- ExpressionLanguage
- Filesystem
- Form
- HttpFoundation
- HttpClient
- HttpKernel
- Lock
- Mailer
- Messenger
- Mime
- PropertyAccess
- Routing
- Security
- Serializer
- Templating
- Translation
- Validator
- VarDumper
- VarExporter
- Workflow
- Yaml
- Add parameter
\Closure $isSameDatabase
toDoctrineDbalAdapter::configureSchema()
- Drop support for Postgres < 9.5 and SQL Server < 2008 in
DoctrineDbalAdapter
- Require explicit argument when calling
NodeBuilder::setParent()
-
Remove
Command::$defaultName
andCommand::$defaultDescription
, use theAsCommand
attribute insteadBefore
use Symfony\Component\Console\Command\Command; class CreateUserCommand extends Command { protected static $defaultName = 'app:create-user'; protected static $defaultDescription = 'Creates users'; // ... }
After
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; #[AsCommand(name: 'app:create-user', description: 'Creates users')] class CreateUserCommand extends Command { // ... }
-
Require explicit argument when calling
*Command::setApplication()
,*FormatterStyle::setForeground/setBackground()
,Helper::setHelpSet()
,Input*::setDefault()
andQuestion::setAutocompleterCallback/setValidator()
-
Remove
StringInput::REGEX_STRING
, useStringInput::REGEX_UNQUOTED_STRING
orStringInput::REGEX_QUOTED_STRING
instead -
Add method
__toString()
toInputInterface
-
Rename
#[MapDecorated]
to#[AutowireDecorated]
-
Remove
ProxyHelper
, useSymfony\Component\VarExporter\ProxyHelper
instead -
Remove
ReferenceSetArgumentTrait
-
Remove support of
@required
annotation, use theSymfony\Contracts\Service\Attribute\Required
attribute instead -
Require explicit argument when calling
ContainerAwareTrait::setContainer()
-
Remove
PhpDumper
optionsinline_factories_parameter
andinline_class_loader_parameter
, use optionsinline_factories
andinline_class_loader
with the direct boolean value instead -
Parameter names of
ParameterBag
cannot be numerics -
Remove
ContainerAwareInterface
andContainerAwareTrait
, use dependency injection insteadBefore
class MailingListService implements ContainerAwareInterface { use ContainerAwareTrait; public function sendMails() { $mailer = $this->container->get('mailer'); // ... } }
After
use Symfony\Component\Mailer\MailerInterface; class MailingListService { public function __construct( private MailerInterface $mailer, ) { } public function sendMails() { $mailer = $this->mailer; // ... } }
To fetch services lazily, you can use a service subscriber.
-
Add parameter
string $id = null
andbool &$asGhostObject = null
toLazyProxy\PhpDumper\DumperInterface::isProxyCandidate()
andgetProxyCode()
-
Add parameter
string $source = null
toFileLoader::registerClasses()
-
Remove
DoctrineDbalCacheAdapterSchemaSubscriber
, useDoctrineDbalCacheAdapterSchemaListener
instead -
Remove
MessengerTransportDoctrineSchemaSubscriber
, useMessengerTransportDoctrineSchemaListener
instead -
Remove
RememberMeTokenProviderDoctrineSchemaSubscriber
, useRememberMeTokenProviderDoctrineSchemaListener
instead -
Remove
DbalLogger
, use a middleware instead -
Remove
DoctrineDataCollector::addLogger()
, use aDebugDataHolder
instead -
Remove
ContainerAwareLoader
, use dependency injection in your fixtures instead -
ContainerAwareEventManager::getListeners()
must be called with an event name -
DoctrineBridge now requires
doctrine/event-manager:^2
-
Add parameter
\Closure $isSameDatabase
toDoctrineTokenProvider::configureSchema()
-
Remove support for Doctrine subscribers in
ContainerAwareEventManager
, use listeners insteadBefore
use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; use Doctrine\ORM\Event\PostFlushEventArgs; use Doctrine\ORM\Events; class InvalidateCacheSubscriber implements EventSubscriberInterface { public function getSubscribedEvents(): array { return [Events::postFlush]; } public function postFlush(PostFlushEventArgs $args): void { // ... } }
After
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\Event\PostFlushEventArgs; use Doctrine\ORM\Events; // Instead of PHP attributes, you can also tag this service with "doctrine.event_listener" #[AsDoctrineListener(event: Events::postFlush)] class InvalidateCacheSubscriber { public function postFlush(PostFlushEventArgs $args): void { // ... } }
- Add parameter
bool $normalizeWhitespace = true
toCrawler::innerText()
- Add parameter
string $default = null
toCrawler::attr()
- The
in
andnot in
operators now use strict comparison
- Add parameter
bool $lock = false
toFilesystem::appendToFile()
- Throw when using
DateTime
orDateTimeImmutable
model data with a different timezone than configured with themodel_timezone
option inDateType
,DateTimeType
, andTimeType
- Make the "widget" option of date/time form types default to "single_text"
- Require explicit argument when calling
Button/Form::setParent()
,ButtonBuilder/FormConfigBuilder::setDataMapper()
,TransformationFailedException::setInvalidMessage()
PostSetDataEvent::setData()
throws an exception, usePreSetDataEvent::setData()
insteadPostSubmitEvent::setData()
throws an exception, usePreSubmitDataEvent::setData()
orSubmitDataEvent::setData()
instead- Add
$duplicatePreferredChoices
parameter toChoiceListFactoryInterface::createView()
-
Renamed command
translation:update
totranslation:extract
-
Remove the
Symfony\Component\Serializer\Normalizer\ObjectNormalizer
andSymfony\Component\Serializer\Normalizer\PropertyNormalizer
autowiring aliases, type-hint againstSymfony\Component\Serializer\Normalizer\NormalizerInterface
or implementNormalizerAwareInterface
instead -
Remove the
Http\Client\HttpClient
service, usePsr\Http\Client\ClientInterface
instead -
Remove
AbstractController::renderForm()
, pass theFormInterface
as parameter torender()
Before
$this->renderForm(..., ['form' => $form]);
After
$this->render(..., ['form' => $form]);
-
Remove the integration of the Doctrine annotations library, use native attributes instead
-
Remove
EnableLoggerDebugModePass
, use argument$debug
of HttpKernel'sLogger
instead -
Remove
AddDebugLogProcessorPass::configureLogger()
, use HttpKernel'sDebugLoggerConfigurator
instead -
Add
array $tokenAttributes = []
optional parameter toKernelBrowser::loginUser()
-
Change default of some config options:
option default Symfony <7.0 default in Symfony 7.0+ framework.http_method_override
true
false
framework.handle_all_throwables
false
true
framework.php_errors.log
'%kernel.debug%'
true
framework.session.cookie_secure
false
auto
framework.session.cookie_samesite
null
'lax'
framework.session.handler_id
'session.handler.native'
null
ifsave_path
is not set,'session.handler.native_file'
otherwiseframework.uid.default_uuid_version
6
7
framework.uid.time_based_uuid_version
6
7
framework.validation.email_validation_mode
'loose'
'html5'
-
Remove the
framework.validation.enable_annotations
config option, useframework.validation.enable_attributes
instead -
Remove the
framework.serializer.enable_annotations
config option, useframework.serializer.enable_attributes
instead -
Remove the
routing.loader.annotation
service, use therouting.loader.attribute
service instead -
Remove the
routing.loader.annotation.directory
service, use therouting.loader.attribute.directory
service instead -
Remove the
routing.loader.annotation.file
service, use therouting.loader.attribute.file
service instead -
Remove
AnnotatedRouteControllerLoader
, useAttributeRouteControllerLoader
instead -
Remove
AddExpressionLanguageProvidersPass
, useSymfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPass
instead -
Remove
DataCollectorTranslatorPass
, useSymfony\Component\Translation\DependencyInjection\DataCollectorTranslatorPass
instead -
Remove
LoggingTranslatorPass
, useSymfony\Component\Translation\DependencyInjection\LoggingTranslatorPass
instead -
Remove
WorkflowGuardListenerPass
, useSymfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass
instead
- Calling
ParameterBag::filter()
on an invalid value throws anUnexpectedValueException
instead of returningfalse
. The exception is more specific forInputBag
which throws aBadRequestException
when invalid value is found. The flagFILTER_NULL_ON_FAILURE
can be used to returnnull
instead of throwing an exception. - The methods
ParameterBag::getInt()
andParameterBag::getBool()
no longer fallback to0
orfalse
when the value cannot be converted to the expected type. They throw aUnexpectedValueException
instead. - Remove
RequestMatcher
, useChainRequestMatcher
instead - Remove
ExpressionRequestMatcher
, useRequestMatcher\ExpressionRequestMatcher
instead - Rename
Request::getContentType()
toRequest::getContentTypeFormat()
- Throw an
InvalidArgumentException
when callingRequest::create()
with a malformed URI - Require explicit argument when calling
JsonResponse::setCallback()
,Response::setExpires/setLastModified/setEtag()
,MockArraySessionStorage/NativeSessionStorage::setMetadataBag()
,NativeSessionStorage::setSaveHandler()
- Add parameter
int $statusCode = null
toResponse::sendHeaders()
andStreamedResponse::sendHeaders()
- Remove implementing
Http\Message\RequestFactory
fromHttplugClient
- Add parameter
\ReflectionFunctionAbstract $reflector = null
toArgumentResolverInterface::getArguments()
andArgumentMetadataFactoryInterface::createArgumentMetadata()
- Add argument
$buildDir
toWarmableInterface
- Remove
ArgumentValueResolverInterface
, useValueResolverInterface
instead - Remove
StreamedResponseListener
- Remove
AbstractSurrogate::$phpEscapeMap
- Rename
HttpKernelInterface::MASTER_REQUEST
toHttpKernelInterface::MAIN_REQUEST
- Remove
terminate_on_cache_hit
option fromHttpCache
, it will now always act asfalse
- Require explicit argument when calling
ConfigDataCollector::setKernel()
,RouterListener::setCurrentRequest()
- Remove
FileLinkFormatter
, useFileLinkFormatter
from the ErrorHandler component instead - Remove
UriSigner
, useUriSigner
from the HttpFoundation component instead - Remove
Kernel::stripComments()
- Add argument
$filter
toProfiler::find()
andFileProfilerStorage::find()
- Add parameter
\Closure $isSameDatabase
toDoctrineDbalStore::configureSchema()
- Rename
gcProbablity
(notice the typo) option togcProbability
in theMongoDbStore
- Remove the OhMySmtp bridge in favor of the MailPace bridge
-
Add parameter
\Closure $isSameDatabase
toDoctrineTransport::configureSchema()
-
Remove
MessageHandlerInterface
andMessageSubscriberInterface
, use#[AsMessageHandler]
insteadBefore
use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; class SmsNotificationHandler implements MessageHandlerInterface { public function __invoke(SmsNotification $message): void { // ... } } class UploadedImageHandler implements MessageSubscriberInterface { public static function getHandledMessages(): iterable { yield ThumbnailUploadedImage::class => ['method' => 'handleThumbnail']; yield ProfilePictureUploadedImage::class => ['method' => 'handleProfilePicture']; } // ... }
After
use Symfony\Component\Messenger\Attribute\AsMessageHandler; #[AsMessageHandler] class SmsNotificationHandler { public function __invoke(SmsNotification $message): void { // ... } } class UploadedImageHandler { #[AsMessageHandler] public function handleThumbnail(ThumbnailUploadedImage $message): void { // ... } #[AsMessageHandler] public function handleThumbnail(ProfilePictureUploadedImage $message): void { // ... } }
-
Remove
StopWorkerOnSigtermSignalListener
in favor of using theSignalableCommandInterface
-
Remove
StopWorkerOnSignalsListener
in favor of using theSignalableCommandInterface
-
Rename
Symfony\Component\Messenger\Transport\InMemoryTransport
andSymfony\Component\Messenger\Transport\InMemoryTransportFactory
toSymfony\Component\Messenger\Transport\InMemory\InMemoryTransport
andSymfony\Component\Messenger\Transport\InMemory\InMemoryTransportFactory
respectively -
Remove
HandlerFailedException::getNestedExceptions()
,HandlerFailedException::getNestedExceptionsOfClass()
andDelayedMessageHandlingException::getExceptions()
which are replaced by a newgetWrappedExceptions()
method
- Remove
Email::attachPart()
method, useEmail::addPart()
instead - Require explicit argument when calling
Message::setBody()
- Drop support for monolog < 3.0
- Remove class
Logger
, use HttpKernel'sDebugLoggerConfigurator
instead
- Add method
isNullSafe()
toPropertyPathInterface
- Require explicit argument when calling
PropertyAccessorBuilder::setCacheItemPool()
- Remove the bridge, use VarExporter's lazy objects instead
- Add parameter
array $routeParameters
toUrlMatcher::handleRouteRequirements()
- Remove Doctrine annotations support in favor of native attributes. Use
Symfony\Component\Routing\Annotation\Route
as native attribute now - Remove
AnnotationClassLoader
, useAttributeClassLoader
instead - Remove
AnnotationDirectoryLoader
, useAttributeDirectoryLoader
instead - Remove
AnnotationFileLoader
, useAttributeFileLoader
instead
- Add parameter
string $badgeFqcn = null
toPassport::addBadge()
- Add parameter
int $lifetime = null
toLoginLinkHandlerInterface::createLoginLink()
- Require explicit argument when calling
TokenStorage::setToken()
- Change argument
$lastUsed
ofTokenProviderInterface::updateToken()
to acceptDateTimeInterface
- Throw when calling the constructor of
DefaultLoginRateLimiter
with an empty secret
- Enabling SecurityBundle and not configuring it is not allowed, either remove the bundle or configure at least one firewall
- Remove the
enable_authenticator_manager
config option - Remove the
security.firewalls.logout.csrf_token_generator
config option, usesecurity.firewalls.logout.csrf_token_manager
instead - Remove the
require_previous_session
config option from authenticators
-
Add method
getSupportedTypes()
toDenormalizerInterface
andNormalizerInterface
-
Remove denormalization support for
AbstractUid
inUidNormalizer
, use one ofAbstractUid
child class instead -
Denormalizing to an abstract class in
UidNormalizer
now throws an\Error
-
Remove
ContextAwareDenormalizerInterface
andContextAwareNormalizerInterface
, useDenormalizerInterface
andNormalizerInterface
insteadBefore
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; class TopicNormalizer implements ContextAwareNormalizerInterface { public function normalize($topic, string $format = null, array $context = []) { } }
After
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class TopicNormalizer implements NormalizerInterface { public function normalize($topic, string $format = null, array $context = []) { } }
-
Remove
CacheableSupportsMethodInterface
, useNormalizerInterface
andDenormalizerInterface
insteadBefore
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; class TopicNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface { public function supportsNormalization($data, string $format = null, array $context = []): bool { return $data instanceof Topic; } public function hasCacheableSupportsMethod(): bool { return true; } // ... }
After
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class TopicNormalizer implements NormalizerInterface { public function supportsNormalization($data, string $format = null, array $context = []): bool { return $data instanceof Topic; } public function getSupportedTypes(?string $format): array { return [ Topic::class => true, ]; } // ... }
-
Require explicit argument when calling
AttributeMetadata::setSerializedName()
andClassMetadata::setClassDiscriminatorMapping()
-
Add parameter
array $context = []
toNormalizerInterface::supportsNormalization()
andDenormalizerInterface::supportsDenormalization()
-
Remove Doctrine annotations support in favor of native attributes
-
Remove the annotation reader parameter from the constructor of
AnnotationLoader
-
The following Normalizer classes have become final, use decoration instead of inheritance:
ConstraintViolationListNormalizer
CustomNormalizer
DataUriNormalizer
DateIntervalNormalizer
DateTimeNormalizer
DateTimeZoneNormalizer
GetSetMethodNormalizer
JsonSerializableNormalizer
ObjectNormalizer
PropertyNormalizer
Before
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; class TopicNormalizer extends ObjectNormalizer { // ... public function normalize($topic, string $format = null, array $context = []): array { $data = parent::normalize($topic, $format, $context); // ... } }
After
use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class TopicNormalizer implements NormalizerInterface { public function __construct( #[Autowire(service: 'serializer.normalizer.object')] private NormalizerInterface&DenormalizerInterface $objectNormalizer, ) { } public function normalize($topic, string $format = null, array $context = []): array { $data = $this->objectNormalizer->normalize($topic, $format, $context); // ... } // ... }
-
Remove
AnnotationLoader
, useAttributeLoader
instead
- Remove the component; use Twig instead
- Remove
PhpStringTokenParser
- Remove
PhpExtractor
in favor ofPhpAstExtractor
- Remove the
Twig_Environment
autowiring alias, useTwig\Environment
instead - Remove option
twig.autoescape
; create a class that implements your escaping strategy (checkFileExtensionEscapingStrategy::guess()
for inspiration) and reference it using thetwig.autoescape_service
option instead - Drop support for Twig 2
- Add methods
getConstraint()
,getCause()
and__toString()
toConstraintViolationInterface
- Add method
__toString()
toConstraintViolationListInterface
- Add method
disableTranslation()
toConstraintViolationBuilderInterface
- Remove static property
$errorNames
from all constraints, use constERROR_NAMES
instead - Remove static property
$versions
from theIp
constraint, use theVERSIONS
constant instead - Remove
VALIDATION_MODE_LOOSE
fromEmail
constraint, useVALIDATION_MODE_HTML5
instead - Remove constraint
ExpressionLanguageSyntax
, useExpressionSyntax
instead. The new constraint is ignored when the value is null or blank, consistently with the other constraints in this component - Remove Doctrine annotations support in favor of native attributes
- Remove
ValidatorBuilder::setDoctrineAnnotationReader()
- Remove
ValidatorBuilder::addDefaultDoctrineAnnotationReader()
- Remove
ValidatorBuilder::enableAnnotationMapping()
, useValidatorBuilder::enableAttributeMapping()
instead - Remove
ValidatorBuilder::disableAnnotationMapping()
, useValidatorBuilder::disableAttributeMapping()
instead - Remove
AnnotationLoader
, useAttributeLoader
instead
- Add parameter
string $label = null
toVarDumper::dump()
- Require explicit argument when calling
VarDumper::setHandler()
- Remove support for per-property lazy-initializers
- Require explicit argument when calling
Definition::setInitialPlaces()
GuardEvent::getContext()
method has been removed. Method was not supposed to be called within guard event listeners as it always returned an empty array anyway.
- Remove the
!php/const:
tag, use!php/const
instead (without the colon)