Safely upgrade from PHP 7.4 to 8.1 using Rector
Tags: php, laravel, refactoring, rector
Published: 2022-09-25 20:53:17

I have to say it from the start, the only thing that comes close to safely upgrading a codebase is having a solid set of automated tests. Nothing gives you more comfort than a bunch of green checkmarks dancing on the screen.

With that said, we're going to upgrade a Laravel project running on the soon-to-be-dead [PHP 7.4 (Nov 28)](https://www.php.net/supported-versions.php). Currently, the last stable PHP version is 8.1 and we're going to upgrade to it using a tool called [Rector](https://getrector.org/). Rector will help us move on to the new syntax in a very comfortable way.

Without further ado, here are the steps I followed.

## Change `composer.json` to the new PHP 8.1
```json
"require": {
    "php": "^8.1",
    ...
}
```

Alongside manually changing the version in `composer.json`, I also changed my environment to use PHP 8.1. In Homestead, that's done simply by running `php81`. In Valet that command is `valet use php@8.1`. If you're on Windows and using Wamp or Xampp, well, good luck 😁.

## Do a `composer update`
After changing the environment, I ran a nice and risky `composer update`. Not entirely sure about this, as there might be a cleaner way, but I guess composer needs to check and update all packages to the specified PHP version.

This is the point where you'll probably get stuck and face composer errors because not all packages you use support the new PHP.

## Check for packages that might break
In this particular project, the only package that did not have support was `fzaninotto/faker`, and that's because that project has said [goodnight since 2020](https://marmelab.com/blog/2020/10/21/sunsetting-faker.html). Good news though, there's an alternative to it called [fakerphp/faker](https://fakerphp.github.io/), which you can just use directly in composer.json (`"fzaninotto/faker" => "fakerphp/faker"`). And you don't need to change a thing in your factories, it just works.

## Run the tests
At this point, I run the tests, and I notice nothing breaks, they're all passing. Phew.
However, if it doesn't go as smoothly for you, or you don't have tests, check out the deprecations [here](https://www.php.net/releases/8.1/en.php#deprecations_and_bc_breaks). Just make sure that you've handled those and ignore the new syntax, because we're not going to upgrade to it the old fashion way. Just commit your changes so you have a clean slate for the syntax changes.

## Install, configure, and run Rector

```bash
composer require rector/rector --dev
vendor/bin/rector init
```

This command will install Rector for you and create a `rector.php` file at the root of your project. You can modify it to tell Rector to upgrade all the codebase up to PHP 8.1, with this configuration:

```php
return static function (RectorConfig $rectorConfig): void {
    $rectorConfig->paths([
        __DIR__ . '/app',
    ]);

    $rectorConfig->importNames();

    $rectorConfig->sets([
        LevelSetList::UP_TO_PHP_81,
    ]);

    $rectorConfig->skip([
        SpatieEnumClassToEnumRector::class,
    ]);
};
```

We'll discuss that last rule shortly, but now you should be able to run `vendor/bin/rector --dry-run`. If your codebase is huge, you'll have to wait a minute or two. When it's done you'll see all the changes that are to be applied, like a git diff. If you omit the `--dry-run` it will instead go ahead and do all those changes to your code, so you can pick what you want to keep in your diff browser of choice.

That last rule `SpatieEnumClassToEnumRector` is skipped because I'm using the `spatie/enum` [package](https://github.com/spatie/enum), and I don't want it to be converted to PHP native enums automatically. The reason for that is that the Spatie package has some nice little helpers, and it's safer in my opinion to migrate some of the enums manually.

Another little gotcha I want to point out is with the `match` expression. It does strict comparisons, unlike `switch`, and that might do some damage if left unchecked. As a remedy, you can use the `match (true) {...}` version, which is not as beautiful but will do a loose comparison like the `switch`.

I'm not going to go over what's new in 8.1 and what Rector [will do for you](https://blog.genijaho.dev/improve-code-quality-in-laravel-using-rector), but damn it feels good to see all the code clean itself. PHPStorm supports it out of the box. You just have to try it, heck, let the tests break, just run it.

---

Originally published at [https://blog.genijaho.dev](https://blog.genijaho.dev/safely-upgrade-from-php-74-to-81-using-rector).