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

Upgrade to Nette 4

Nette 4 is not one release but a set of libraries that moved to a new major version: nette/utils 4, nette/php-generator 4, nette/mail 4 and nette/robot-loader 4. Their constants were renamed, flags became named arguments, and a few methods disappeared. With the package dresscode/upgrade-nette, DressCode rewrites what can be rewritten and, for the rest, says what to write instead.

What It Looks Like

Before anything changes in a Nette 3 application, the estimate tells you how much work moving to nette/utils 4.0 takes. In this case, it means twelve places in three files, and DressCode rewrites all of them by itself:

DRESS|CODE 1.3.0
Config     /var/www/shop/dresscode.neon
Upgrade    nette/utils 4.0 over 3 files

Must change, 2 topics with 12 findings
  nette/utils  10 findings in 3 files    needs ^4.0, automatic
  nette/utils  2 findings in 1 file      works on ^3.2, automatic

Three of those places, after nette/utils is raised to ^4.0:

$image->resize(300, 200, Image::FIT | Image::SHRINK_ONLY);
file_put_contents($this->file, Json::encode($products, Json::PRETTY));
return Strings::startsWith($code, 'sale-');
$image->resize(300, 200, Image::OrSmaller | Image::ShrinkOnly);
file_put_contents($this->file, Json::encode($products, pretty: true));
return \str_starts_with($code, 'sale-');

It rewrites a method call only where the types from PHPStan say the method belongs to that class, and in the style of the project's code. Step by Step shows how to get there.

What Gets Rewritten

The tables show examples from the data of the package dresscode/upgrade-nette, each verified by a run of DressCode. The complete list is in the data of the package, one file per library, for example utils.neon.

nette/utils 4

old API new API since
Strings::startsWith($s, 'x') \str_starts_with($s, 'x') 4.0
Strings::normalizeNewLines($s) Strings::unixNewLines($s) 4.0
Image::FIT | Image::SHRINK_ONLY Image::OrSmaller | Image::ShrinkOnly 4.0
Json::encode($v, Json::PRETTY) Json::encode($v, pretty: true) 4.0
Arrays::grep($a, $p, PREG_GREP_INVERT) Arrays::grep($a, $p, invert: true) 4.0
Arrays::map($a, callback: $fn) Arrays::map($a, transformer: $fn) 4.0.4
Html::fromHtml($s) Html::html($s) 4.1.5

The other Image constants (EXACT, FILL, STRETCH, EMPTY_GIF), Strings::contains() and endsWith(), and the flags of Json::decode(), Strings::match(), matchAll() and split() are rewritten the same way.

nette/php-generator 4

old API new API since
ClassType::class('Order') new ClassType('Order') 4.0.1
ClassType::VISIBILITY_PUBLIC Visibility::Public 4.0.10
$method->body $method->getBody() 4.0
ClassType::withBodiesFrom($c) ClassType::from($c, withBodies: true) 4.0

DressCode adds the import of Nette\PhpGenerator\Visibility by itself.

nette/mail 4 and nette/robot-loader 4

old API new API since
Message::HIGH Message::High nette/mail 4.0
$mail->subject $mail->getSubject() nette/mail 4.0
$loader->setTempDirectory($dir) $loader->setCacheDirectory($dir) nette/robot-loader 4.1.3

What Gets Reported

Where there is no replacement, or a person has to choose it, DressCode reports the place and leaves the code alone. The message says what to write instead:

app/Samples.php:30:22: error: Static method `Nette\Utils\Finder::findFiles()` is deprecated: pass the masks as one array, since nette/utils 4.0. [upgrading.api.forbiddenMembers]
app/Samples.php:32:9: error: Static property `Nette\Utils\Html::$xhtml` is removed: there is no replacement; the output is always HTML5; drop the setting, since nette/utils 4.0. [upgrading.api.forbiddenMembers]
old API what to write instead since
Finder::findFiles('*.php', '*.phpt') the masks as one array, findFiles(['*.php', '*.phpt']) nette/utils 4.0
Html::$xhtml nothing, the output is always HTML5; delete the setting nette/utils 4.0
Image::rgb() ImageColor::rgb(), which returns an object and takes the opacity as a number from 0 to 1 nette/utils 4.0.3
Nette\Iterators\Mapper Iterables::map(), which returns a generator you can iterate only once nette/utils 4.0.5
ClassType::addExtend() setExtends(), a class extends at most one class nette/php-generator 4.0.1

Confirm such a place with the comment // dresscode:ignore <decision> -- <reason>, or rewrite it; Manual Tasks Along the Way has the details.

Step by Step

DressCode needs nette/utils 4.1, so it cannot be a development dependency of a Nette 3 application. Install it globally, with PHPStan and the data for Nette:

composer global require dresscode/dresscode dresscode/upgrade-nette phpstan/phpstan
  1. Measure how much work the upgrade takes: dresscode upgrade estimate nette/utils 4.0. The line Composer says whether something in composer.lock refuses the target version.
  2. Save the plan with the test command, so that the team can review it: dresscode upgrade plan nette/utils 4.0 --save --test-command "vendor/bin/tester tests -o junit:{junit}".
  3. Carry it out: dresscode upgrade run. The tests run after each step, and if a test newly fails after a step, that step is undone. At the Composer step, DressCode asks whether it may raise nette/utils to ^4.0.
  4. Go through what is left for you, and run upgrade run again until the run ends with UPGRADED.

The other libraries are upgraded the same way, only with a different target, such as dresscode upgrade estimate nette/php-generator 4.2. Without a target, DressCode proposes every library for which it has data about a newer version than the project allows today. Upgrade Your Code Step by Step describes the whole procedure with tests, commits and manual tasks.

Where to Go Next

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