Automated Upgrades with Rector

Rector (github.com/rectorphp/rector (https://github.com/rectorphp/rector 10,430 ), 2.6.7, composer require --dev rector/rector) is built on PHPStan 376,908 : it parses code, applies typed rules and prints the result, which makes it the tool for moving PHP 7 code to 8.5.

rector.php: upgrade legacy/ to PHP 8.5 and add provable typesPHP
<?php
use Rector\Config\RectorConfig;
return RectorConfig::configure()
  ->withPaths([__DIR__ . '/legacy'])
  ->withPhpSets(php85: true)                    // every PHP upgrade set up to 8.5
  ->withPreparedSets(typeDeclarations: true);   // infer missing types where provable

vendor/bin/rector process --dry-run prints the diff and changes nothing:

Output of 236
...
-        switch ($code) {
-            case 1: return 'paid';
-            case 2: return 'shipped';
-            default: return 'pending';
-        }
+        return match ($code) {
+            1 => 'paid',
+            2 => 'shipped',
+            default => 'pending',
+        };
...
-        return count($this->items) ? $this->items[array_key_first($this->items)] : null;
+        return count($this->items) ? array_first($this->items) : null;
...

Seven rules fired: constructor promotion, match and str_contains() (PHP 8.0), readonly (8.1), array_first() (8.5), and bool and string return types. Untyped $code and $sku stayed untyped, since nothing proves their types. Run Rector on a clean branch with tests (Testing with PHPUnit), review the diff, then let PHP-CS-Fixer 13,556 tidy the layout. Rector 2.6 already accepts php86: true.