Rector et ses pouvoirs : maîtrisez l'évolution de votre code Symfony
Par Louis-Arnaud Catoire
Mis à jour le

Comprendre Rector et l'AST
Rector est un outil de refactoring automatique pour PHP, essentiel dans toute démarche de modernisation applicative. Là où un développeur modifie du code manuellement, renommer une méthode, ajouter un type hint, remplacer une annotation par un attribut, Rector effectue ces transformations de manière programmatique sur l'ensemble d'une base de code.
Le mécanisme repose sur l'AST (Abstract Syntax Tree). Plutôt que de manipuler du texte brut avec des expressions régulières, Rector utilise nikic/php-parser pour convertir chaque fichier PHP en arbre syntaxique. Chaque élément du code, classe, méthode, affectation, condition, devient un nœud typé dans cet arbre. Une affectation $x = 1; produit un nœud Assign contenant un nœud Variable et un nœud LNumber. Un double !!$var se traduit par deux nœuds BooleanNot imbriqués.
Le cycle de transformation suit trois phases : le parsing convertit le source en AST, la traversée applique les règles sur chaque nœud correspondant, et l'impression reconvertit l'arbre modifié en code PHP en préservant le style d'origine (indentation, espaces) pour minimiser les diffs. Cette approche structurelle garantit que seuls les patterns exacts sont modifiés, ce qui élimine la catégorie entière de bugs liés au rechercher-remplacer textuel.
Installation et premier lancement
Rector s'installe comme dépendance de développement via Composer :
composer require rector/rector --dev
La génération du fichier de configuration se fait en une commande :
php vendor/bin/rector init
Le fichier rector.php produit constitue le point d'entrée de toute la configuration :
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/config',
__DIR__ . '/public',
__DIR__ . '/src',
__DIR__ . '/tests',
]);
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_83,
]);
};
La prévisualisation des changements sans application se fait avec --dry-run, l'application effective sans ce flag :
php vendor/bin/rector process --dry-run
php vendor/bin/rector process
Stratégie de rule sets
Le choix et l'ordonnancement des rule sets déterminent la qualité d'une migration. Rector propose plusieurs catégories de sets, chacune avec un périmètre précis :
SetList::DEAD_CODEsupprime le code mort : variables inutilisées, conditions toujours vraies, méthodes privées jamais appelées.SetList::CODE_QUALITYsimplifie les conditions, élimine les redondances, améliore la lisibilité.SetList::CODING_STYLEuniformise le style : ternaires, early returns, conventions d'écriture.SetList::TYPE_DECLARATIONajoute les type hints manquants sur les paramètres, retours et propriétés.SetList::NAMINGrenomme variables et méthodes selon les conventions PSR.
Pour Symfony, des sets dédiés couvrent chaque version majeure et les transformations structurelles :
$rectorConfig->sets([
SymfonySetList::SYMFONY_60,
SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES,
SymfonySetList::SYMFONY_CODE_QUALITY,
SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION,
]);
La règle ANNOTATIONS_TO_ATTRIBUTES est particulièrement stratégique lors du passage à Symfony 6.x, dont le guide officiel de mise à jour détaille les changements attendus. Elle transforme les annotations en attributs PHP 8 sur les routes, les entités Doctrine (@Entity, @Column, @OneToMany) et les contraintes de validation (@Assert\NotBlank, @Assert\Email) :
#[Route('/users', name: 'user_list', methods: ['GET'])]
public function list(): Response
L'ordre d'application compte. Commencer par DEAD_CODE réduit la surface de code avant d'appliquer les transformations plus lourdes. Enchaîner avec CODE_QUALITY puis les sets de version produit des diffs plus lisibles et des reviews plus efficaces.
Exclure des fichiers
Certains fichiers méritent un traitement spécifique ou une exclusion temporaire :
$rectorConfig->skip([
SimplifyIfReturnBoolRector::class => [
__DIR__ . '/src/ComplicatedFile.php',
__DIR__ . '/src/ComplicatedDirectory',
],
]);
Besoin d'accompagnement sur votre projet ?
Parlons-enIntégration CI et workflow par lots
Rector prend toute sa valeur quand il est intégré au pipeline d'intégration continue. Le principe est simple : --dry-run dans la CI vérifie qu'aucun fichier ne nécessite de transformation, garantissant que tout code poussé est déjà conforme.
Dans un workflow GitHub Actions :
- name: Rector
run: vendor/bin/rector process --dry-run
Si Rector détecte des modifications nécessaires, le pipeline échoue. Les développeurs exécutent Rector localement, commitent les changements et poussent à nouveau. Ce mécanisme fonctionne identiquement avec GitLab CI, Jenkins ou tout autre outil.
Le workflow par lots est la clé d'une migration maîtrisée. Plutôt que d'activer tous les sets d'un coup et de produire un diff de plusieurs milliers de lignes, chaque itération cible un set ou une règle unique :
- Activer une règle dans
rector.php. - Exécuter
rector process, vérifier le diff. - Lancer la suite de tests complète.
- Commiter et créer une pull request dédiée.
- Passer à la règle suivante.
Ce découpage produit des PRs atomiques, facilite la review et permet de reverter chirurgicalement en cas de régression.
Règles personnalisées
Les règles intégrées couvrent les cas génériques. Les besoins spécifiques à un projet, remplacer un helper legacy maison, migrer un pattern interne, appliquer une convention d'équipe, nécessitent des règles sur mesure.
Une règle personnalisée étend AbstractRector et implémente trois méthodes :
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class ReplaceLegacyHelperRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Replace legacy_helper() calls with new_helper()', []);
}
public function getNodeTypes(): array
{
return [FuncCall::class];
}
public function refactor(Node $node): ?Node
{
if (! $this->isName($node, 'legacy_helper')) {
return null;
}
$node->name = new Node\Name('new_helper');
return $node;
}
}
getNodeTypes() déclare les types de nœuds AST ciblés. refactor() reçoit chaque nœud correspondant et retourne le nœud modifié ou null si aucune transformation ne s'applique. La puissance de cette API réside dans l'accès complet au graphe AST : on peut inspecter les nœuds parents, les annotations de type via PHPStan, la portée des variables, et composer des transformations complexes impliquant plusieurs nœuds.
Pour une équipe qui maintient des dizaines de règles internes, structurer ces règles dans un package dédié avec leurs propres tests PHPUnit garantit leur fiabilité lors des mises à jour de Rector.
Manipulation avancée de l'AST
Derrière l'API simplifiée d'AbstractRector se cache la mécanique complète de nikic/php-parser. Les Node Visitors permettent d'intervenir à différents moments de la traversée (beforeTraverse, enterNode, leaveNode, afterTraverse). Rector orchestre ces visitors dans un ordre déterministe, résout les dépendances entre règles et gère les cas où une transformation invalide le contexte d'une autre.
Pour les développeurs souhaitant approfondir ces concepts, les évolutions de PHP 9.0 ajouteront de nouvelles opportunités de refactoring automatisé. Comprendre ce niveau de détail devient nécessaire quand une règle doit effectuer des transformations multi-nœuds : déplacer une méthode d'une classe vers un trait, extraire un service à partir d'un code procédural, ou restructurer une hiérarchie d'héritage. Ces opérations requièrent de manipuler des nœuds ClassLike, ClassMethod et Stmt en coordination, en s'assurant que les imports (use statements) et les références croisées restent cohérents.
Rector comme outil de gouvernance technique
À l'échelle d'une organisation qui maintient plusieurs applications Symfony, Rector dépasse le rôle d'outil de migration ponctuel pour devenir un instrument de gouvernance du code.
Un set de règles partagé entre équipes, distribué comme package Composer interne, encode les standards techniques de l'organisation. Quand une décision architecturale est prise, migrer de ArrayCollection vers un wrapper typé, bannir les appels statiques dans la couche domaine, imposer les readonly classes, elle se traduit en règle Rector. La CI l'applique automatiquement sur tous les projets. Le standard n'est plus un document qu'on espère voir respecté, mais une contrainte enforcée par l'outillage.
Migration par phases sur les grandes bases de code
Sur un monolithe de plusieurs centaines de milliers de lignes, une migration frontale est irréaliste. L'approche par phases utilise la capacité de Rector à cibler des répertoires spécifiques :
php vendor/bin/rector process src/Module/Billing
Chaque module migre indépendamment, avec sa propre PR, ses propres tests et sa propre validation. Les modules critiques passent en dernier, après que l'équipe a accumulé de la confiance sur les modules périphériques. Un fichier rector.php par module peut coexister temporairement pour gérer les vitesses de migration différentes.
Gestion du risque
Rector ne peut pas garantir l'équivalence comportementale de toutes les transformations. Les transformations syntaxiques pures (promotion de propriétés dans le constructeur, match expression) sont sûres. Les transformations sémantiques (remplacement de patterns, modification de nommage) exigent une couverture de tests solide et une review humaine.
La stratégie de mitigation repose sur trois piliers : une suite de tests avec un coverage suffisant sur le code transformé, un passage PHPStan au niveau le plus élevé possible avant et après migration, et des PRs suffisamment petites pour qu'un reviewer puisse les analyser en profondeur. Si un de ces piliers manque, ralentir le rythme de migration est la décision responsable. Rector est d'ailleurs un allié de premier plan pour automatiser une migration Symfony entre versions majeures, dans le cadre d'une démarche de modernisation applicative structurée. Pour structurer la démarche d'ensemble, un guide de migration dans un projet Symfony détaille la méthodologie complète, étape par étape.
Conclusion
Rector transforme la maintenance applicative Symfony d'un effort manuel en processus systématique. Pour un développeur confirmé, c'est un accélérateur de migrations. Pour un lead, c'est un outil d'intégration continue qui enforce les standards. Pour un architecte, c'est un levier de gouvernance technique qui permet d'appliquer des décisions structurelles à l'échelle d'une organisation. La condition de son efficacité reste constante : une couverture de tests fiable, une progression par lots maîtrisés et une review humaine sur les transformations à risque. Avant de lancer Rector sur un projet, un audit de code PHP permet de mesurer l'ampleur des transformations nécessaires et de prioriser les chantiers.
Pour aller plus loin
- PHPStan 2.0 : niveau 10 et nouvelles fonctionnalités, compléter Rector avec l'analyse statique
- Comment PHPStan peut vous aider à améliorer la qualité de votre code PHP, analyse statique en complément du refactoring
- La dette technique : faut-il vraiment en avoir peur ?, comprendre la dette que Rector aide à résorber
- Documentation officielle Rector, guide complet
- Rector sur GitHub, code source et contributions
- Symfony Upgrade Fixer, guide officiel de migration Symfony
Vous faites face à un projet legacy ou une migration PHP ?
Notre offre de modernisation d'application PHP accompagne la migration de votre base de code vers Symfony, étape par étape et sans interruption de service.
Découvrir notre offre de modernisationArticles connexes

Sylius vs Prestashop : quelle solution e-commerce choisir ?
Sylius et Prestashop sont deux solutions e-commerce PHP open source. Comparaison technique pour choisir la plateforme adaptée à votre activité.
Lire la suite →
Migration MySQL vers PostgreSQL avec Doctrine : retour d'expérience et guide pratique
Migrer de MySQL vers PostgreSQL avec Doctrine sur un projet Symfony. Différences de typage, génération du schéma et migration des données.
Lire la suite →
Commandes Symfony invocables : fini le boilerplate, place aux attributs
Depuis Symfony 7.3, une seule méthode __invoke() remplace configure(), interact() et execute(). Moins de code et des types PHP natifs.
Lire la suite →