« Fatal error: Cannot redeclare function() ». Cette ligne, familière à quiconque a géré des sites WordPress à fort volume d’extensions, résume à elle seule tout le risque du préfixage insuffisant. Deux extensions, développées indépendamment, choisissent le même nom court pour une fonction utilitaire, et le site entier s’effondre dès que les deux sont actives simultanément.
Cet article recense les pièges de collision les plus fréquents rencontrés en dix ans de maintenance d’extensions WordPress, avec pour chacun une explication de la cause réelle et la correction à appliquer.
Le piège du préfixe trop court
Un préfixe de deux ou trois lettres (ac_, wpx_) semble suffisant tant qu’on ne réfléchit pas au nombre d’extensions qui peuvent cohabiter sur un même site. Sur un site institutionnel ou e-commerce mature, il n’est pas rare de dépasser cinquante extensions actives. Un préfixe court multiplie mécaniquement le risque de collision.
// Risqué : "acme_" est court et générique
function acme_log( $message ) { /* ... */ }
// Plus sûr : un préfixe long et spécifique à l'extension, pas à l'agence
function acmecrm_synchroniser_log( $message ) { /* ... */ }
Le namespace PHP résout ce problème de façon définitive (voir notre article sur l’architecture avec PSR-4), mais toute portion de code qui doit rester dans le scope global — fonctions de template destinées aux thèmes, callbacks enregistrés par nom de chaîne dans certains hooks anciens — reste exposée au risque et doit conserver un préfixage rigoureux.
Les constantes, grandes oubliées du préfixage
Un développeur soigneux sur ses noms de fonctions oublie fréquemment d’appliquer la même rigueur aux constantes, qui vivent pourtant dans le même scope global partagé par toutes les extensions actives.
// Collision potentielle : VERSION est un nom générique
define( 'VERSION', '2.1.0' );
// Correct
define( 'ACMECRM_VERSION', '2.1.0' );
Une collision sur une constante ne provoque pas une fatale error immédiate comme pour une fonction dupliquée : PHP émet un simple avertissement (« Constant already defined ») et conserve la première valeur définie. C’est en réalité plus dangereux qu’un plantage franc, car le bug reste silencieux : une extension continue de fonctionner avec la mauvaise valeur de version ou de configuration, sans qu’aucune alerte visible ne signale le problème avant un comportement inattendu en production.

function_exists : une fausse sécurité
Certains développeurs entourent leurs déclarations de fonctions d’un function_exists() pour « éviter les erreurs de redéclaration ». Ce réflexe masque le symptôme sans traiter la cause, et peut provoquer un bug bien plus insidieux qu’une fatale error.
if ( ! function_exists( 'acme_calculer_remise' ) ) {
function acme_calculer_remise( $prix, $pourcentage ) {
return $prix * ( 1 - $pourcentage / 100 );
}
}
Si une autre extension a déjà déclaré une fonction du même nom, avec une logique différente (par exemple un pourcentage exprimé en décimal plutôt qu’en entier), ce garde-fou empêche la fatale error mais votre extension utilisera silencieusement la fonction de l’autre extension, avec des résultats de calcul erronés et aucune erreur visible pour alerter qui que ce soit. Un plantage explicite, aussi désagréable soit-il, reste souvent préférable à une corruption silencieuse de données métier.
Notre règle en revue de code :
function_existsautour d’une déclaration de fonction est acceptable uniquement quand la fonction sert de point d’extension volontaire et documenté (permettre à un thème de la surcharger), jamais comme filet de sécurité contre une collision de nommage accidentelle.
Les classes globales, même piège
Avant l’adoption des namespaces PHP, deux extensions qui déclarent chacune une classe Logger ou Settings dans le scope global entrent en collision de la même façon qu’avec des fonctions. Les namespaces éliminent structurellement ce risque, à condition que le namespace racine choisi soit lui-même suffisamment spécifique : un namespace Plugin\ reste tout aussi risqué qu’un préfixe de fonction générique.
// Namespace trop générique, risque de collision indirecte via des outils
// d'analyse ou des extensions qui scannent les namespaces actifs
namespace Plugin;
// Préférable
namespace AcmeCrm\Synchronisation;
Les hooks eux-mêmes peuvent collisionner
Un nom de hook personnalisé (do_action( 'sync_termine' )) trop générique peut être déclenché ou intercepté involontairement par une autre extension qui a choisi le même nom pour un usage complètement différent. La convention à appliquer est identique à celle des fonctions : préfixer systématiquement.
// Risqué
do_action( 'sync_termine' );
// Sûr
do_action( 'acmecrm_synchronisation_terminee' );
Diagnostiquer une collision en production
Quand une fatale error de redéclaration survient, WP-CLI aide à identifier rapidement le coupable sans devoir désactiver les extensions une par une en production :
wp plugin list --status=active --field=name
# Puis, en environnement de test, désactivation par dichotomie
wp plugin deactivate --all
wp plugin activate mon-extension autre-extension
Le message d’erreur PHP lui-même précise généralement le fichier et la ligne de la première déclaration, ce qui permet souvent d’identifier directement l’extension fautive sans même recourir à cette méthode par élimination.
En résumé
Le préfixage rigoureux — fonctions, constantes, classes hors namespace, noms de hooks — reste une discipline de base trop souvent négligée par excès de confiance. Un namespace PHP bien choisi élimine la plupart de ces risques pour le code orienté objet, mais tout ce qui reste dans le scope global exige la même vigilance qu’il y a quinze ans.