Migrer vos modules custom vers D11

Vos modules custom sont-ils compatibles Drupal 11? Comment auditer et adapter votre code.

Sur un site Drupal mature, le core et les modules contribués se mettent à jour sans trop d'effort grâce à Composer. Le vrai sujet d'une migration majeure se cache ailleurs : dans vos modules custom. Ce code écrit spécifiquement pour votre organisation porte souvent la logique métier la plus précieuse et c'est aussi lui qui résiste le plus au passage d'une version majeure à l'autre. La bonne nouvelle, c'est que l'écosystème Drupal a outillé cette migration de façon remarquable. Auditer la compatibilité de votre code, mesurer l'effort réel et automatiser une part importante des corrections sont aujourd'hui des opérations cadrées et prévisibles.

Cet article s'adresse aux décideurs qui doivent estimer le coût et le risque d'un passage vers Drupal 11 (et préparer déjà Drupal 12). Il explique comment identifier le code déprécié, ce qui change entre versions majeures, comment Rector automatise une partie du travail, quels sont les patterns de remplacement les plus courants et pourquoi les tests de régression sont la dernière brique indispensable.

Pourquoi le code custom est le point critique d'une migration

Une version majeure de Drupal supprime des API qui étaient marquées deprecated dans la version précédente. Le core et les modules contribués bien maintenus suivent cette cadence : leurs mainteneurs publient des versions compatibles avant la sortie de la nouvelle version majeure. Votre code custom, lui, n'a personne d'autre que votre équipe pour le mettre à jour.

Concrètement, un module custom qui fonctionnait parfaitement sur Drupal 10 peut appeler des fonctions, des services ou des signatures de méthodes qui n'existent plus sur Drupal 11. Le site ne plante pas forcément au déploiement : certaines dépréciations passent en erreur silencieuse, d'autres en fatal error, d'autres encore changent subtilement de comportement. C'est précisément pour cette raison qu'un audit préalable est non négociable. Une migration Drupal sérieuse commence toujours par cartographier l'écart entre votre code actuel et les API de la version cible.

Identifier le code déprécié : Upgrade Status et drupal-check

Avant d'écrire la moindre correction, il faut une cartographie objective. Deux outils dominent cette étape et ils sont complémentaires.

Upgrade Status est un module Drupal qui analyse l'ensemble de votre site (core, modules contribués et modules custom) et produit un rapport lisible directement dans l'interface d'administration. Il indique, module par module, le niveau de compatibilité avec la prochaine version majeure et liste les appels dépréciés détectés. C'est l'outil idéal pour donner à un chef de projet une vue d'ensemble en quelques minutes, sans connaissance technique approfondie.

composer require --dev drupal/upgrade_status
drush en upgrade_status
# Rapport accessible via Admin > Reports > Upgrade status

drupal-check est un outil en ligne de commande, basé sur PHPStan, qui scanne le code à la recherche d'API dépréciées et d'erreurs potentielles. Il s'intègre naturellement dans un pipeline d'intégration continue et cible précisément un répertoire de modules custom.

composer require --dev mglaman/drupal-check
vendor/bin/drupal-check web/modules/custom

La sortie liste, fichier par fichier et ligne par ligne, chaque appel déprécié avec sa version de suppression. C'est ce rapport qui permet de chiffrer l'effort : un module qui remonte deux ou trois avertissements se corrige en quelques heures, un module qui en remonte près d'une centaine demande un vrai chantier. Combiner les deux outils donne à la fois la vue stratégique (Upgrade Status) et la précision technique (drupal-check) nécessaire pour planifier sereinement.

Ce qui change entre deux versions majeures

Comprendre la nature des changements aide à anticiper l'effort. Les évolutions entre versions majeures de Drupal tournent autour de quelques grandes familles.

Les API du core qui évoluent. Des méthodes changent de signature, des classes sont déplacées dans un autre namespace, des constantes sont remplacées par des enums. Ces changements suivent une politique stricte : une API n'est jamais supprimée sans avoir été marquée deprecated au préalable, avec une alternative documentée.

Les dépendances tierces. Drupal 11 relève les versions minimales de PHP et de Symfony. Du code custom qui s'appuie directement sur des composants Symfony doit suivre ces montées de version. C'est souvent là que se nichent les surprises, notamment sur la gestion des événements et des requêtes HTTP.

Les conventions d'écriture. Les hooks procéduraux laissent progressivement la place aux hooks orientés objet, les plugins migrent vers des attributs PHP en remplacement des annotations et l'injection de dépendances devient la norme partout. Ces évolutions ne sont pas toujours bloquantes immédiatement, mais les adopter au moment de la migration évite d'accumuler une dette qui resurgira à la version suivante.

L'essentiel à retenir pour un décideur : ces changements sont documentés et prévisibles. Drupal publie pour chaque version un guide de changement détaillé. Le travail de migration n'est pas une exploration dans le noir, c'est l'application méthodique d'une liste connue.

Rector : automatiser une partie des corrections

C'est ici que la migration moderne se distingue de celle d'il y a quelques années. Rector est un outil de transformation automatique de code PHP, et l'écosystème Drupal fournit un jeu de règles dédié via le projet drupal-rector. Concrètement, Rector lit votre code, repère les patterns dépréciés qu'il connaît et réécrit automatiquement le code vers la nouvelle API.

composer require --dev palantirnet/drupal-rector
cp vendor/palantirnet/drupal-rector/rector.php .
# Vérifier ce qui serait changé, sans rien modifier
vendor/bin/rector process web/modules/custom --dry-run
# Appliquer les transformations
vendor/bin/rector process web/modules/custom

Le mode --dry-run est précieux : il affiche un diff de ce qui serait modifié sans toucher au code, ce qui permet de réviser les changements avant de les appliquer. Selon la nature du code, Rector traite couramment entre la moitié et les trois quarts des dépréciations de façon automatique. Le gain de temps est considérable et, surtout, il réduit le risque d'erreur humaine sur des corrections répétitives.

Rector n'est pas magique pour autant. Il corrige les transformations qu'il a apprises, mais la logique métier complexe, les cas tordus et les API que les règles ne couvrent pas encore restent à la charge d'un développeur. La bonne approche consiste à laisser Rector faire le gros du travail mécanique, puis à concentrer l'expertise humaine sur le reste. C'est exactement le découpage qui rend une migration efficace : automatiser le prévisible, réserver le jugement aux cas qui le méritent.

Les patterns de remplacement courants

Au delà de l'outillage, certains motifs reviennent dans presque toutes les migrations de code custom. Les connaître aide à comprendre ce que voit l'équipe technique dans le code.

Récupération de services. L'appel statique global \Drupal::service('...') reste fonctionnel mais il est déconseillé dans les classes. Le pattern recommandé est l'injection de dépendances via le constructeur.

// Pattern à éviter dans une classe
$entityTypeManager = \Drupal::service('entity_type.manager');

// Pattern recommandé : dependency injection
public function __construct(
  protected EntityTypeManagerInterface $entityTypeManager,
) {}

public static function create(ContainerInterface $container): static {
  return new static(
    $container->get('entity_type.manager'),
  );
}

Hooks orientés objet. Drupal modernise la déclaration des hooks. Là où l'on écrivait une fonction mymodule_entity_presave() dans un fichier .module, on déclare désormais une méthode dans une classe dédiée annotée avec l'attribut #[Hook]. Cette évolution améliore la testabilité et la lisibilité.

Annotations vers attributs PHP. Les plugins (blocks, field formatters, et autres) déclaraient leurs métadonnées via des annotations en commentaire. Drupal 11 privilégie les attributs PHP natifs, plus robustes et reconnus par les outils d'analyse statique.

// Ancien style : annotation
/**
 * @Block(
 *   id = "mon_bloc",
 *   admin_label = @Translation("Mon bloc"),
 * )
 */

// Nouveau style : attribut PHP
#[Block(
  id: 'mon_bloc',
  admin_label: new TranslatableMarkup('Mon bloc'),
)]

Méthodes de classes du core renommées ou déplacées. Certaines méthodes utilitaires changent de nom ou migrent vers un autre service. Ce sont typiquement les corrections que Rector applique tout seul, à condition que la règle correspondante existe.

Ces patterns ne sont pas une corvée à subir : bien menés, ils laissent une base de code plus propre, plus testable et mieux alignée sur les standards actuels. La migration devient ainsi une occasion d'assainir la dette technique accumulée plutôt qu'un simple rattrapage.

Tests de régression : valider que rien n'a cassé

Une fois le code adapté, comment garantir que le comportement est identique à l'avant migration? La réponse tient en deux mots : tests de régression. Modifier du code, même avec l'aide de Rector, introduit un risque de régression. Sans filet, la seule vérification possible est manuelle, lente et incomplète.

Si votre code custom dispose déjà de tests, la migration devient nettement plus confortable : on lance la suite après chaque vague de corrections et on voit immédiatement ce qui passe et ce qui casse. Les tests deviennent alors la boussole qui guide le travail. C'est l'un des arguments les plus forts en faveur d'un investissement dans les tests automatisés Drupal bien avant la migration : ce que vous écrivez aujourd'hui pour sécuriser vos déploiements quotidiens devient votre garantie le jour du passage à la version majeure suivante.

Si votre code n'a pas encore de tests, la migration est le bon moment pour en ajouter au moins sur les parcours et la logique métier les plus critiques. Quelques tests fonctionnels sur les fonctionnalités clés de vos modules custom suffisent à transformer une migration anxiogène en opération maîtrisée. L'idée n'est pas de viser une couverture exhaustive, mais de couvrir ce dont l'échec coûterait le plus cher.

Concrètement, le cycle de validation d'une migration de code custom ressemble à ceci : audit avec Upgrade Status et drupal-check, corrections automatiques avec Rector, corrections manuelles des cas restants, puis exécution de la suite de tests à chaque étape jusqu'à ce que tout soit au vert. Ce déroulé méthodique est ce qui distingue une migration sereine d'un saut dans l'inconnu.

Une migration cadrée plutôt qu'un pari

Migrer vos modules custom vers Drupal 11 n'est pas une opération à l'aveugle. C'est une démarche outillée et prévisible : on cartographie l'écart, on automatise ce qui peut l'être, on traite manuellement les cas spécifiques et on valide par les tests. À chaque étape, l'effort est mesurable et le risque est maîtrisé. Le plus grand danger n'est pas la complexité technique de la migration, c'est de la reporter jusqu'à ce que la version en place ne soit plus supportée et que le rattrapage se fasse dans l'urgence.

Anticiper, c'est garder la main sur le calendrier et sur le budget. Une base de code auditée régulièrement, testée et mise à jour au fil de l'eau traverse les versions majeures sans drame.


Vos modules custom doivent passer à Drupal 11 et vous voulez un audit clair de l'effort réel? Parlons de votre projet : on cartographie votre code, on chiffre la migration et on vous propose un plan adapté à votre stack.

Experts Drupal

Besoin d'aide avec Drupal ?

Nos développeurs seniors sont disponibles pour vos projets de migration, maintenance, refonte ou développement sur mesure.

Trouver un expert Drupal →

Ce qu'on propose

  • Migration vers Drupal 11
  • Maintenance mensuelle sécurisée
  • Renfort technique ponctuel
  • Audit et optimisation
Discuter de votre projet