PHP - Rector

Rector est un outil faisant partie de l’écosystème PHP, créé par Tomas Votruba, permettant de refactoriser le code de façon automatique. Il y a pour cela pas mal de rules qui vont définir quelle refactorisation on souhaite appliquer. L’exemple le plus connu (en tout cas celui que je connaissais) est d’utiliser les rules de montée de version de PHP ou de montée de version de Symfony. Mais il existe beaucoup d’autres règles de refactorisation (suppression du code mort, qualité de code, …).

Par exemple, si j’ai une application qui tourne sur du PHP 7.4 et que je veux migrer vers PHP 8.0, Rector va appliquer toutes les nouveautés du langage pour éviter / ralentir la dette technique (sur la syntaxe).

class Foo
{
    private string $bar;

    public function __construct(string $bar)
    {
        $this->bar = $bar;
    }
}

Le code ci-dessus sera remplacé par le code ci-dessous (appliquant la promotion de propriété du constructeur) :

class Foo
{
    public function __construct(private string $bar)
    {
    }
}

Environnement de développement

Rector peut être utilisé en local et lancé “à la main” par les développeurs.

// pour voir les changements sans les appliquer
vendor/bin/rector --dry-run
// faire la refactorisation
vendor/bin/rector

L’idéal à mon avis, c’est de faire une PR dédiée à cette refactorisation lorsqu’on utilise pour la première fois une rule, puis de lancer Rector avant de commiter lorsqu’on développe nos features, fix, etc.

Phpstorm supporte Rector nativement depuis la version 2022.2. On peut donc l’exécuter directement depuis l’IDE (via Run), d’abord en mode “dry-run” ce qui nous permet de voir les modifications potentielles, puis on peut appliquer ces changements.

Intégration continue

Rector est aussi très intéressant lorsqu’il est intégré dans une CI. Il peut être utilisé à plusieurs niveaux :

  • fail de le CI en cas de potentiels changements non appliqués
  • détection, refactorisation et commit des changements

La première possibilité est simplement d’ajouter le fait que Rector ne détecte rien à refactoriser pour autoriser le merge de la PR. Dans le cas où le “check” ne passe pas, on n’autorise pas le “merge”. Sur Github Actions par exemple ça peut se faire comme ça :

# .github/workflows/rector.yaml
name: Rector

on:
  pull_request: null
  push:
    branches:
      - main

jobs:
  rector-ci:
    runs-on: ubuntu-latest
    steps:
      -   uses: actions/checkout@v2
      -
        uses: shivammathur/setup-php@v1
        with:
          php-version: 8.0
          coverage: none
      -   run: composer install --no-progress
      -   run: vendor/bin/rector process --dry-run --ansi

La deuxième possibilité est d’autoriser Rector a exécuter la refactorisation qu’il détecte et “commiter” automatiquement. C’est super puissant parce qu’on n’a pas forcément envie de passer du temps à exécuter Rector, commiter, repusher et attendre la fin de la pipeline. Mais ça peut poser quelques soucis si la refactorisation n’est plus en accord avec vos “coding standards” entre autres. C’est probablement gérable tant que vous pouvez utiliser une commande pour corriger ces erreurs de CS. Un article est dispo ici pour vous présenter une façon de faire.

Conclusion

Rector est un outil très pratique qui permet de contenir la dette technique sur la syntaxe du langage et donc permettra des montés de versions beaucoup moins douloureuses.

Il permet de gagner énormément de temps sur des taches fastideuses, rébarbatives et de garder un code uniforme sans effort.

Rector fait partie intégrante de l’écosystème PHP et participe à rendre ce langage crédible, solide et “industrialisable”. C’est vraiment un “must have” de tout projet PHP.