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
- Measure how much work the upgrade takes:
dresscode upgrade estimate nette/utils 4.0. The lineComposersays whether something incomposer.lockrefuses the target version. - 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}". - 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 raisenette/utilsto^4.0. - Go through what is left for you, and run
upgrade runagain until the run ends withUPGRADED.
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
- Upgrade Your Code Step by Step, the estimate, the plan and the run in detail.
- Nette libraries, what the package
dresscode/upgrade-nettecovers for the other libraries and versions. - How upgrading libraries works, how DressCode knows what has changed.