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 $isSameDatabasetoDoctrineDbalAdapter::configureSchema() - Drop support for Postgres < 9.5 and SQL Server < 2008 in
DoctrineDbalAdapter
- Require explicit argument when calling
NodeBuilder::setParent()
-
Remove
Command::$defaultNameandCommand::$defaultDescription, use theAsCommandattribute 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_STRINGorStringInput::REGEX_QUOTED_STRINGinstead -
Add method
__toString()toInputInterface
-
Rename
#[MapDecorated]to#[AutowireDecorated] -
Remove
ProxyHelper, useSymfony\Component\VarExporter\ProxyHelperinstead -
Remove
ReferenceSetArgumentTrait -
Remove support of
@requiredannotation, use theSymfony\Contracts\Service\Attribute\Requiredattribute instead -
Require explicit argument when calling
ContainerAwareTrait::setContainer() -
Remove
PhpDumperoptionsinline_factories_parameterandinline_class_loader_parameter, use optionsinline_factoriesandinline_class_loaderwith the direct boolean value instead -
Parameter names of
ParameterBagcannot be numerics -
Remove
ContainerAwareInterfaceandContainerAwareTrait, 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 = nullandbool &$asGhostObject = nulltoLazyProxy\PhpDumper\DumperInterface::isProxyCandidate()andgetProxyCode() -
Add parameter
string $source = nulltoFileLoader::registerClasses()
-
Remove
DoctrineDbalCacheAdapterSchemaSubscriber, useDoctrineDbalCacheAdapterSchemaListenerinstead -
Remove
MessengerTransportDoctrineSchemaSubscriber, useMessengerTransportDoctrineSchemaListenerinstead -
Remove
RememberMeTokenProviderDoctrineSchemaSubscriber, useRememberMeTokenProviderDoctrineSchemaListenerinstead -
Remove
DbalLogger, use a middleware instead -
Remove
DoctrineDataCollector::addLogger(), use aDebugDataHolderinstead -
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 $isSameDatabasetoDoctrineTokenProvider::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 = truetoCrawler::innerText() - Add parameter
string $default = nulltoCrawler::attr()
- The
inandnot inoperators now use strict comparison
- Add parameter
bool $lock = falsetoFilesystem::appendToFile()
- Throw when using
DateTimeorDateTimeImmutablemodel data with a different timezone than configured with themodel_timezoneoption 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
$duplicatePreferredChoicesparameter toChoiceListFactoryInterface::createView()
-
Renamed command
translation:updatetotranslation:extract -
Remove the
Symfony\Component\Serializer\Normalizer\ObjectNormalizerandSymfony\Component\Serializer\Normalizer\PropertyNormalizerautowiring aliases, type-hint againstSymfony\Component\Serializer\Normalizer\NormalizerInterfaceor implementNormalizerAwareInterfaceinstead -
Remove the
Http\Client\HttpClientservice, usePsr\Http\Client\ClientInterfaceinstead -
Remove
AbstractController::renderForm(), pass theFormInterfaceas 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$debugof HttpKernel'sLoggerinstead -
Remove
AddDebugLogProcessorPass::configureLogger(), use HttpKernel'sDebugLoggerConfiguratorinstead -
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_overridetruefalseframework.handle_all_throwablesfalsetrueframework.php_errors.log'%kernel.debug%'trueframework.session.cookie_securefalseautoframework.session.cookie_samesitenull'lax'framework.session.handler_id'session.handler.native'nullifsave_pathis not set,'session.handler.native_file'otherwiseframework.uid.default_uuid_version67framework.uid.time_based_uuid_version67framework.validation.email_validation_mode'loose''html5' -
Remove the
framework.validation.enable_annotationsconfig option, useframework.validation.enable_attributesinstead -
Remove the
framework.serializer.enable_annotationsconfig option, useframework.serializer.enable_attributesinstead -
Remove the
routing.loader.annotationservice, use therouting.loader.attributeservice instead -
Remove the
routing.loader.annotation.directoryservice, use therouting.loader.attribute.directoryservice instead -
Remove the
routing.loader.annotation.fileservice, use therouting.loader.attribute.fileservice instead -
Remove
AnnotatedRouteControllerLoader, useAttributeRouteControllerLoaderinstead -
Remove
AddExpressionLanguageProvidersPass, useSymfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPassinstead -
Remove
DataCollectorTranslatorPass, useSymfony\Component\Translation\DependencyInjection\DataCollectorTranslatorPassinstead -
Remove
LoggingTranslatorPass, useSymfony\Component\Translation\DependencyInjection\LoggingTranslatorPassinstead -
Remove
WorkflowGuardListenerPass, useSymfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPassinstead
- Calling
ParameterBag::filter()on an invalid value throws anUnexpectedValueExceptioninstead of returningfalse. The exception is more specific forInputBagwhich throws aBadRequestExceptionwhen invalid value is found. The flagFILTER_NULL_ON_FAILUREcan be used to returnnullinstead of throwing an exception. - The methods
ParameterBag::getInt()andParameterBag::getBool()no longer fallback to0orfalsewhen the value cannot be converted to the expected type. They throw aUnexpectedValueExceptioninstead. - Remove
RequestMatcher, useChainRequestMatcherinstead - Remove
ExpressionRequestMatcher, useRequestMatcher\ExpressionRequestMatcherinstead - Rename
Request::getContentType()toRequest::getContentTypeFormat() - Throw an
InvalidArgumentExceptionwhen 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 = nulltoResponse::sendHeaders()andStreamedResponse::sendHeaders()
- Remove implementing
Http\Message\RequestFactoryfromHttplugClient
- Add parameter
\ReflectionFunctionAbstract $reflector = nulltoArgumentResolverInterface::getArguments()andArgumentMetadataFactoryInterface::createArgumentMetadata() - Add argument
$buildDirtoWarmableInterface - Remove
ArgumentValueResolverInterface, useValueResolverInterfaceinstead - Remove
StreamedResponseListener - Remove
AbstractSurrogate::$phpEscapeMap - Rename
HttpKernelInterface::MASTER_REQUESTtoHttpKernelInterface::MAIN_REQUEST - Remove
terminate_on_cache_hitoption fromHttpCache, it will now always act asfalse - Require explicit argument when calling
ConfigDataCollector::setKernel(),RouterListener::setCurrentRequest() - Remove
FileLinkFormatter, useFileLinkFormatterfrom the ErrorHandler component instead - Remove
UriSigner, useUriSignerfrom the HttpFoundation component instead - Remove
Kernel::stripComments() - Add argument
$filtertoProfiler::find()andFileProfilerStorage::find()
- Add parameter
\Closure $isSameDatabasetoDoctrineDbalStore::configureSchema() - Rename
gcProbablity(notice the typo) option togcProbabilityin theMongoDbStore
- Remove the OhMySmtp bridge in favor of the MailPace bridge
-
Add parameter
\Closure $isSameDatabasetoDoctrineTransport::configureSchema() -
Remove
MessageHandlerInterfaceandMessageSubscriberInterface, 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
StopWorkerOnSigtermSignalListenerin favor of using theSignalableCommandInterface -
Remove
StopWorkerOnSignalsListenerin favor of using theSignalableCommandInterface -
Rename
Symfony\Component\Messenger\Transport\InMemoryTransportandSymfony\Component\Messenger\Transport\InMemoryTransportFactorytoSymfony\Component\Messenger\Transport\InMemory\InMemoryTransportandSymfony\Component\Messenger\Transport\InMemory\InMemoryTransportFactoryrespectively -
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'sDebugLoggerConfiguratorinstead
- Add method
isNullSafe()toPropertyPathInterface - Require explicit argument when calling
PropertyAccessorBuilder::setCacheItemPool()
- Remove the bridge, use VarExporter's lazy objects instead
- Add parameter
array $routeParameterstoUrlMatcher::handleRouteRequirements() - Remove Doctrine annotations support in favor of native attributes. Use
Symfony\Component\Routing\Annotation\Routeas native attribute now - Remove
AnnotationClassLoader, useAttributeClassLoaderinstead - Remove
AnnotationDirectoryLoader, useAttributeDirectoryLoaderinstead - Remove
AnnotationFileLoader, useAttributeFileLoaderinstead
- Add parameter
string $badgeFqcn = nulltoPassport::addBadge() - Add parameter
int $lifetime = nulltoLoginLinkHandlerInterface::createLoginLink() - Require explicit argument when calling
TokenStorage::setToken() - Change argument
$lastUsedofTokenProviderInterface::updateToken()to acceptDateTimeInterface - Throw when calling the constructor of
DefaultLoginRateLimiterwith an empty secret
- Enabling SecurityBundle and not configuring it is not allowed, either remove the bundle or configure at least one firewall
- Remove the
require_previous_sessionconfig option from authenticators
-
Add method
getSupportedTypes()toDenormalizerInterfaceandNormalizerInterface -
Remove denormalization support for
AbstractUidinUidNormalizer, use one ofAbstractUidchild class instead -
Denormalizing to an abstract class in
UidNormalizernow throws an\Error -
Remove
ContextAwareDenormalizerInterfaceandContextAwareNormalizerInterface, useDenormalizerInterfaceandNormalizerInterfaceinsteadBefore
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, useNormalizerInterfaceandDenormalizerInterfaceinsteadBefore
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:
ConstraintViolationListNormalizerCustomNormalizerDataUriNormalizerDateIntervalNormalizerDateTimeNormalizerDateTimeZoneNormalizerGetSetMethodNormalizerJsonSerializableNormalizerObjectNormalizerPropertyNormalizer
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, useAttributeLoaderinstead
- Remove the component; use Twig instead
- Remove
PhpStringTokenParser - Remove
PhpExtractorin favor ofPhpAstExtractor
- Remove the
Twig_Environmentautowiring alias, useTwig\Environmentinstead - Remove option
twig.autoescape; create a class that implements your escaping strategy (checkFileExtensionEscapingStrategy::guess()for inspiration) and reference it using thetwig.autoescape_serviceoption instead - Drop support for Twig 2
- Add methods
getConstraint(),getCause()and__toString()toConstraintViolationInterface - Add method
__toString()toConstraintViolationListInterface - Add method
disableTranslation()toConstraintViolationBuilderInterface - Remove static property
$errorNamesfrom all constraints, use constERROR_NAMESinstead - Remove static property
$versionsfrom theIpconstraint, use theVERSIONSconstant instead - Remove
VALIDATION_MODE_LOOSEfromEmailconstraint, useVALIDATION_MODE_HTML5instead - Remove constraint
ExpressionLanguageSyntax, useExpressionSyntaxinstead. 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, useAttributeLoaderinstead
- Add parameter
string $label = nulltoVarDumper::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/constinstead (without the colon)