PHP 8.6 arrives on November 19. DressCode already rewrites code for it. 16 days Upgrading to a newer PHP →

Upgrade to Symfony 8

In Symfony 8, $application->add() became addCommand(), a constraint no longer takes an array of options, and #[AutowireIterator] replaced #[TaggedIterator]. Symfony 8.0 removed what 7.x had deprecated, and it requires PHP 8.4. DressCode with the package dresscode/upgrade-symfony prepares your code for the upgrade from Symfony 7.4 to 8 while you are still on 7.4: it rewrites the code to the new API, and after the move it continues with what Symfony 8.1 deprecated.

The data of the package hold 217 changes that Symfony deprecated in versions 7.1 to 7.4 and removed in 8.0: DressCode rewrites 152 of them by itself and reports the other 65. Symfony 7.4, released together with 8.0 at the end of November 2025, has the same features according to UPGRADE-8.0.md, so every replacement already exists in 7.4.

What It Looks Like

A command and two functions of an application on Symfony 7.4, with the Symfony packages at 7.4.* in composer.json:

class ExportCommand extends Command
{
	public function __construct(
		#[TaggedIterator('app.exporter')]
		private iterable $exporters,
	) {
		parent::__construct();
	}


	public static function getDefaultName(): ?string
	{
		return 'app:export';
	}
}


function createApplication(ExportCommand $export): Application
{
	$application = new Application('shop');
	$application->add($export);
	return $application;
}


function nameConstraints(): array
{
	return [
		new NotBlank(['message' => 'Fill in the name.']),
		new Length(['min' => 3, 'max' => 50]),
	];
}

After dresscode upgrade today, still on Symfony 7.4:

#[AsCommand(name: 'app:export')]
class ExportCommand extends Command
{
	public function __construct(
		#[AutowireIterator('app.exporter')]
		private iterable $exporters,
	) {
		parent::__construct();
	}
}


function createApplication(ExportCommand $export): Application
{
	$application = new Application('shop');
	$application->addCommand($export);
	return $application;
}


function nameConstraints(): array
{
	return [
		new NotBlank(message: 'Fill in the name.'),
		new Length(min: 3, max: 50),
	];
}

Four changes from three versions of Symfony 7: #[TaggedIterator] became #[AutowireIterator] (7.1); the method getDefaultName() moved into the attribute #[AsCommand] and the options of the constraints became named arguments (7.3); add() became addCommand() (7.4). DressCode rewrote the imports of both attributes as well, and the code still runs on 7.4.

What Gets Rewritten

The tables show examples from the data of the package, each verified by a run of DressCode. The complete list is in the data, one file per component, for example validator.neon.

Deprecated in Symfony 7.1 to 7.4

old API new API since
#[TaggedIterator('app.exporter')] #[AutowireIterator('app.exporter')] symfony/dependency-injection 7.1
new UriSigner($secret, parameter: '_sig') new UriSigner($secret, hashParameter: '_sig') symfony/http-foundation 7.1
$type->isA(TypeIdentifier::STRING) $type->isIdentifiedBy(TypeIdentifier::STRING) symfony/type-info 7.2
getDefaultName() of a command #[AsCommand(name: 'app:export')] on the class symfony/console 7.3
new Length(['min' => 3, 'max' => 50]) new Length(min: 3, max: 50) symfony/validator 7.3
Type::iterable(Type::string(), asList: true) Type::list(Type::string()) symfony/type-info 7.3
$application->add($command) $application->addCommand($command) symfony/console 7.4
Symfony\Component\Serializer\Annotation\Groups Symfony\Component\Serializer\Attribute\Groups symfony/serializer 7.4
new Crawler($html, $uri, null, true) new Crawler($html, $uri, null) symfony/dom-crawler 7.4
new MastermindsParser() new NativeParser() symfony/html-sanitizer 7.4

The constraints of the validator account for 47 of these rewrites. For 46 constraint classes, DressCode turns an array of options into named arguments, and new Choice(['a', 'b']) becomes new Choice(choices: ['a', 'b']).

Deprecated in Symfony 8.1 and 8.2

Symfony 8.1 deprecated 37 more things, and DressCode rewrites 22 of them. It applies them once composer.json requires 8.1; with 8.0.*, it leaves them alone:

old API new API since
extends Symfony\Component\HttpKernel\DependencyInjection\Extension extends Symfony\Component\DependencyInjection\Extension\Extension symfony/http-kernel 8.1
new ValidatorExtension($validator, false) new ValidatorExtension($validator) symfony/form 8.1
mirror($source, $target, options: ['copy_on_windows' => $follow]) mirror($source, $target, options: ['follow_symlinks' => $follow]) symfony/filesystem 8.1

Symfony 8.2 is planned for November 2026 (Symfony 8.2). The data already hold six changes from its UPGRADE-8.2.md, and DressCode applies them once a project requires 8.2.

What Gets Reported

Where the replacement is used differently, or there is none, DressCode reports the place, leaves the code alone and says what to write instead:

Method `Symfony\Component\HttpFoundation\Request::get()` is deprecated: read the bag the value comes from, the `attributes`, `query` or `request` property, such as `query->get()`, since symfony/http-foundation 7.4.
Method `Symfony\Component\Security\Core\User\UserInterface::eraseCredentials()` is deprecated: there is no replacement; nothing calls it any more; leave the plain password out of `__serialize()` where the method erased it, since symfony/security-core 7.3.
old API what to write instead since
Symfony\Component\PropertyInfo\Type Symfony\Component\TypeInfo\Type, built by factories such as Type::int() or Type::list() symfony/property-info 7.3
LazyGhostTrait ReflectionClass::newLazyGhost(), a native lazy ghost of PHP 8.4 symfony/var-exporter 7.3
ConfigBuilderGenerator and the fluent config builders a PHP array returned from the config file symfony/config 7.4
XmlFileLoader of the container PhpFileLoader or YamlFileLoader, with the configuration rewritten in PHP or YAML symfony/dependency-injection 7.4
$request->query = new InputBag($query) the query parameters passed to the constructor or to initialize(), or $request->query->set() symfony/http-foundation 8.1
Bundle::registerCommands() each command as a service with #[AsCommand] or the tag console.command symfony/http-kernel 8.1
ConstraintValidatorInterface::validate() validateInContext(), which takes the execution context too symfony/validator 8.1

Confirm such a place with the comment // dresscode:ignore <decision> -- <reason>, or rewrite it; Manual Tasks Along the Way has the details. The data do not capture changes in YAML configuration, in Twig templates or in behavior; UPGRADE-8.0.md lists them.

Symfony 7.4 LTS or 8.x

Symfony 7.4 is a long-term support (LTS) release. It gets bug fixes until November 2028 and security fixes until November 2029, and it runs on PHP 8.2 (Symfony 7.4). The 8.x versions need PHP 8.4 and are maintained for a shorter time: support for 8.0 ended in July 2026, and 8.1 is supported until January 2027 (Symfony 8.1).

Either way, the first step is the same. On 7.4, dresscode upgrade today rewrites everything 7.1 to 7.4 deprecated, so an application that stays on the LTS has code written for the API of Symfony 8, as far as the data go, and moving to 8.x later is a smaller step.

Step by Step

DressCode needs PHP 8.4. If your application on Symfony 7.4 runs on an older PHP, install DressCode globally, with PHPStan and the data for Symfony:

composer global require dresscode/dresscode dresscode/upgrade-symfony phpstan/phpstan

On PHP 8.4 or newer, it can be a development dependency of the project, as Installation shows.

  1. Start from Symfony 7.4 with a clean working tree. Run dresscode upgrade today, and once more if it advises you to. Go through what it reports, run your tests and PHPStan, and commit.
  2. Raise require.php to at least 8.4. The next run of upgrade today then also fixes or reports what PHP deprecated up to 8.4, see Upgrading to a newer PHP.
  3. Raise the Symfony packages in composer.json to 8.0.*; with Symfony Flex, raise extra.symfony.require too. Run composer update "symfony/*" -W --prefer-lowest and dresscode upgrade today again, then run the tests and commit.
  4. Move on to 8.1.* the same way. Only now does DressCode rewrite what 8.1 deprecated, such as the base class Extension of a bundle extension.

DressCode reads the version of each package from its constraint in composer.json. If a Symfony package has only * there and leaves the version to extra.symfony.require, DressCode does not see the lower bound and only reports the changes of that package. Write the version into the constraint, such as 7.4.*.

Upgrade Your Code Step by Step explains why --prefer-lowest and why one version at a time.

Where to Go Next

Show on GitHub · Report a problem with this page on GitHub · Nov 2, 2026