Upgrade to Nette 4
Nette 4 is the 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. Constants got new names,
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
An application on Nette 3 wants to move to nette/utils 4.0. The estimate tells how much work that is before
anything changes:
DRESS|CODE 1.3.0 Config /var/www/shop/dresscode.neon Upgrade nette/utils 4.0 over 3 files Must change, 3 topics in 12 places nette/utils 7 places in 3 files after the raise, automatic nette.flagsAsNamedArguments 3 places in 1 file after the raise, automatic nette/utils 2 places in 1 file works today, automatic
Once nette/utils is raised to ^4.0, DressCode rewrites the code by itself:
$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 the code of the project has. Step by Step shows how to get there.
What Gets Rewritten
The tables show samples 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 |
You confirm such a place with the comment // dresscode:ignore <decision> -- <reason>, or rewrite it;
Places Left for You has the details.
Step by Step
DressCode needs nette/utils 4.1, so it does not fit into an application on Nette 3 as a development dependency.
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 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 nette/utils 4.0 --save-plan --test-command "vendor/bin/tester tests -o junit:{junit}". - Carry it out:
dresscode upgrade --continue. The tests run after each step, and a step after which a test newly fails goes back. At the Composer step, DressCode asks whether it may raisenette/utilsto^4.0. - Go through what is left for you, and run
--continueagain until the run ends withUPGRADED.
The other libraries move the same way, only with another target, such as
dresscode upgrade nette/php-generator 4.2. Without a target, DressCode proposes every library it has newer data about
than the project allows today. Upgrade Your Code Step by Step describes the whole
procedure with tests, commits and places left for you.
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.