php[architect] logo

Want to check out an issue? Sign up to receive a special offer.

Code Coverage Validation With Codecov

Posted by on June 6, 2023

Every year we’re getting new versions of PHP, which is great but it means we’re going to have to constantly put time into upgrading our code as features are added, deprecated, and removed from the core language. Because our code base is only going to have more lines of code over time, it can be a real pain to manage these upgrades without a large push by your development team.

Thankfully we can look to the wonderful open-source tool [Rector](https://getrector.com/) to help us vastly speed up this process. In this article, we’ll discuss what Rector is, why we should include it as part of our CI/CD pipeline, and get it set up so we can start using it today.

Disclaimer

As always, when working with a tool that makes changes to your code automatically, make sure you’re fully testing these changes BEFORE you push them, as it can mess up your day.

Why Use Rector

When PHP 8 was released, it was a painful upgrade for some of us because it made a lot of changes under the hood and removed functionality that our code depended on. PHP has been slowly deprecating more and more functionality as we slowly work our way through the PHP 8.x lifecycle, and if we’re not careful, upgrading from PHP 8 to PHP 9 could be just as painful as upgrading from PHP 7 to PHP 8.

Our process for upgrading a project from PHP 7.4 to PHP 8 took several developers multiple weeks because we had to manually fix thousands of lines of code by hand.

With Rector, we could have sped up the process to the point that I’m wishing we had known about it before.

Rector also comes with ways to find dead code and automatically remove it so it’s not part of our maintenance load when we do need to upgrade our code.

Finally, Rector provides rules that allow us to automatically clean up our code so it’s easier to read and maintain.

Installing

Installing Rector is done using the standard [`composer require`](https://www.youtube.com/watch?v=4Cfbje5WHdk) command that we’re used to. We’ll add it to the `require-dev` section as we won’t be running this on production.
composer require rector/rector --dev

Now we need to initialize Rector using its `init` command.
% ./vendor/bin/rector init

No “rector.php” config found. Should we generate it for you? [yes]:
> yes

[OK] The config is added now. Re-run the command to make Rector do the work!

This command generates a file named “rector.php” in our current directory that looks like the following.

__DIR__ . ‘/src’,
__DIR__ . ‘/tests’,
]);
// register a single rule
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
// define sets of rules
// $rectorConfig->sets([
// LevelSetList::UP_TO_PHP_80
// ]);
};

This creates a function that we will use to configure Rector for our project. The first portion sets the paths that we are going to have Rector check. Our project only has an “src” and “tests” directory but notice that it’s not running on the “vendor” directory as we’re not the ones maintaining that code.

Next, we define the rules and sets of rules that we want to apply to our codebase. We currently only have the `InlineConstructorDefaultToPropertyRector` enabled which moves an assignment of a constant value into the property creation:

// before
final class SomeClass
{
private $name;

public function __construct()
{
$this->name = ‘John’;
}
}

// after
final class SomeClass
{
private $name = ‘John’;

public function __construct()
{
}
}

It’s a good example of how to enable a single rule but what’s more helpful to us is the section below where it enables a set of rules. A set of rules is just what it sounds like and it includes multiple rules that are included in the set so the rector.php file doesn’t contain hundreds of lines of all the rules we want to be enabled.

In this case, the rule is named `LevelSetList::UP_TO_PHP_80` which will include all of the rules needed to make sure our code has all the changes necessary to make our code as compliant as possible for PHP 8.0. There’s also an`UP_TO_PHP_81` and `UP_TO_PHP_82` if you want to go higher.

We’ll enable this and remove the single rule.

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . ‘/src’,
__DIR__ . ‘/tests’,
]);

// define sets of rules
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_80
]);
};

To make sure we’re not going to destroy our application we’re going to want to make sure we try any new rules in a “dry-run” first.

This is done by running `vendor/bin/rector –dry-run`.

class FirstName
{
– protected string $value;

– public function __construct(string $value)
+ public function __construct(protected string $value)
{
– $this->value = $value;
}
}

In my case, Rector found a place where I can use the Constructor Property Promotion feature added in PHP 8.0.

To have Rector apply the changes we’ll run it without the `–dry-run` argument.

We’ve tackled the “keeping our code up to date” promise of Rector but there’s more that we can do with it to make our code easier to maintain and read.

Removing Dead Code

The next thing I want to bring to your attention is its “dead code” cleanup functionality. With this set of rules in place Rector will replace code that doesn’t run with a shorter version and less code with the same functionality is always a great goal.

It’s included by adding the `Rector\Set\ValueObject\SetList::DEAD_CODE` set to our configuration.


__DIR__ . ‘/src’,
__DIR__ . ‘/tests’,
]);

// define sets of rules
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_80,
SetList::DEAD_CODE,
]);
};

An example of this is the `RemoveEmptyClassMethodRector` rule. In this rule, Rector will remove any empty class methods.

// before
class JunkClass
{
public function __construct()
{
}
}

//after
class JunkClass
{
}

Quality and Style

There are two more sets of rules I want to discuss, and they are the `Rector\Set\ValueObject\SetList::CODE_QUALITY` and `Rector\Set\ValueObject\SetList::CODING_STYLE` sets. These have rules to improve code quality and coding style, respectively.

An example of this is the `SymplifyQuoteEscapeRector` rule. In this rule, Rector prefers quotes that aren’t inside the string to make our code easier to read.

// before
class JunkClass
{
public function __construct()
{
$name = “\” Tom”;
$name = ‘\’ Sara’;
}
}

// after
class JunkClass
{
public function __construct()
{
$name = ‘” Tom’;
$name = “‘ Sara”;
}
}

Automating

So far, we’ve run Rector manually from the command line, but I want to automate everything I can so I don’t forget to perform these critical steps. To do so we can set up a GitHub Action to automatically run Rector and commit the results directly to our repository.

You can either create this as its own GitHub action or add it as the first task in your current pipeline. I’m going to assume you don’t currently have one (check out our article on how to use GitHub Actions to test your code) for more information on GitHub Actions.

Create a file in “./github/workflows” called “rector.yml” with the following contents.

name: Rector

on:
pull_request:
branches: [ “main” ]

permissions:
contents: write

jobs:
rector:
runs-on: ubuntu-latest

steps:
– uses: actions/checkout@v3
– uses: shivammathur/setup-php@v2
with:
php-version: 8.0

– name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles(‘**/composer.lock’) }}
restore-keys: |
${{ runner.os }}-php-

– run: vendor/bin/rector –ansi

– name: Commit Changes
uses: stefanzweifel/git-auto-commit-action@v4
if: always()

After we push this to our repository in a pull request a new commit will be automatically created with the Rector changes.

Custom Rules

One of the truly amazing things about Rector is that it’s written in PHP, so we, as PHP developers, can extend it with our own rules to fit our specific needs. This can be helpful if we’re performing a large change to our codebase and don’t want to have to make a bunch of manual edits to the files.

For example, we might be renaming our “email” property in our `User` class so it’s now “login”. We could do a search and replace of our codebase and hope it’s catching all of the changes. Or we could write a custom rule for Rector that does this.

What You Need To Know

  • Rector PHP provides a tool to refactor our code automatically
  • Uses rules to define what to do
  • Integrate it into our code review process to maximize its usefulness
  • Provides the ability to create custom rules for custom challenges

Tags:
 

Leave a comment

Use the form below to leave a comment: