Lors d’une mise à jour PHP, un vieux site peut soudain afficher une erreur qui sent bon la regex fatiguée :
preg_match(): Compilation failed: invalid range in character class at offset 20Langage du code : JavaScript (javascript)
Le message paraît obscur. Pourtant, la cause est souvent très simple : une expression régulière contient un tiret - mal placé dans une classe de caractères.
Depuis PHP 7.3, PHP utilise PCRE2 pour les expressions régulières. Cette migration a rendu certains patterns plus stricts. Résultat : une regex qui passait silencieusement sur une ancienne version PHP peut échouer après une montée de version.
Pourquoi cette erreur apparaît-elle ?
Dans une expression régulière, les crochets définissent une classe de caractères.
Par exemple :
/[abc]/
Cette regex accepte un caractère parmi a, b ou c.
Autre exemple :
/[a-z]/
Ici, le tiret indique une plage : toutes les lettres de a à z.
Le problème apparaît quand PHP rencontre un tiret à un endroit ambigu. Par exemple :
/[\w-.]+/
Dans cette classe, le moteur PCRE2 peut interpréter le tiret comme un opérateur de plage entre \w et .. Cette plage n’a pas de sens. PHP refuse donc de compiler l’expression régulière.
C’est pour cela que le message parle de Compilation failed. Le problème ne vient pas de la chaîne testée. Il vient du pattern lui-même.
La correction la plus claire : échapper le tiret
La correction la plus lisible consiste à échapper le tiret avec un antislash :
/[\w\-.]+/
Dans cette version, \- indique clairement que le tiret doit être traité comme un caractère littéral, et non comme une plage.
Exemple PHP complet :
<?php
$filename = 'exemple-test.php';
if ( 1 === preg_match( '/^[\w\-.]+$/', $filename ) ) {
echo 'Nom valide';
}Langage du code : HTML, XML (xml)
Ici, la regex accepte les caractères de mot, l’underscore, le tiret et le point. Elle compile correctement avec PHP 7.3, PHP 8.4 et PHP 8.5.
Autre correction valide : placer le tiret à la fin
Une autre solution consiste à placer le tiret à la fin de la classe :
Marre des agences qui sous-traitent ?
Avec moi, vous parlez directement au développeur qui fait le travail. Pas d'intermédiaire, pas de promesses creuses. Juste du code propre et un interlocuteur joignable.
Travaillons directement ensemble →/[\w.-]+/
Dans cette position, le tiret ne peut plus être interprété comme une plage.
Vous pouvez aussi le placer au début :
/[-\w.]+/
Les trois formes suivantes sont donc valides :
/[\w\-.]+/
/[\w.-]+/
/[-\w.]+/
Personnellement, je préfère souvent \-. L’intention reste claire à la relecture, surtout dans du code maintenu par plusieurs personnes.
Exemple avant / après
Voici une regex fragile :
<?php
preg_match( '/^[\w-.]+$/', 'exemple-test.php' );Langage du code : HTML, XML (xml)
Elle peut déclencher :
preg_match(): Compilation failed: invalid range in character classLangage du code : JavaScript (javascript)
Version corrigée :
<?php
preg_match( '/^[\w\-.]+$/', 'exemple-test.php' );Langage du code : HTML, XML (xml)
Ou, si vous préférez placer le tiret à la fin :
<?php
preg_match( '/^[\w.-]+$/', 'exemple-test.php' );Langage du code : HTML, XML (xml)
Dans les deux cas, PHP comprend que le tiret est un caractère autorisé, pas le début d’une plage étrange.
Comment retrouver la regex responsable ?
Le message d’erreur indique généralement le fichier et la ligne concernés :
session.php on line 278Langage du code : CSS (css)
Ouvrez ce fichier, puis cherchez un appel à l’une des fonctions PHP suivantes :
preg_match()preg_match_all()preg_replace()preg_replace_callback()preg_split()preg_grep()preg_filter()
Ensuite, inspectez les classes de caractères entre crochets. Les patterns suspects ressemblent souvent à ceci :
/[\w-.]+/
/[a-zA-Z0-9-_\s]+/
/[0-9-_]+/
/[\w-:]+/
/[A-z-0-9]+/
Attention au dernier exemple : [A-z] est presque toujours une mauvaise idée. Il ne correspond pas seulement aux lettres. Il inclut aussi des caractères ASCII situés entre Z et a. Préférez [A-Za-z].
Chercher toutes les regex potentiellement fragiles
Sur un projet PHP ou WordPress, vous pouvez commencer par chercher les appels aux fonctions preg_* :
grep -RIn --include='*.php' 'preg_' .Langage du code : PHP (php)
Pour une recherche plus ciblée des classes contenant un tiret, utilisez plutôt ripgrep si disponible :
rg "preg_.*\[[^]]*-" --glob "*.php"Langage du code : JavaScript (javascript)
Cette commande ne remplace pas une revue humaine, mais elle aide à repérer les zones à inspecter.
Dans WordPress, vérifiez en priorité :
- le thème actif ;
- le thème enfant ;
- les plugins anciens ;
- les mu-plugins ;
- les snippets personnalisés ;
- les vieux fichiers copiés depuis un ancien projet ;
- les bibliothèques embarquées dans un plugin.
Si l’erreur vient d’un plugin tiers, évitez de modifier directement le fichier du plugin. Mettez-le à jour, signalez le bug, ou remplacez le plugin s’il n’est plus maintenu.
Tester une regex rapidement en PHP
Vous pouvez tester un pattern directement en ligne de commande :
php -r "var_dump(preg_match('/^[\w\-.]+$/', 'exemple-test.php'));"Langage du code : JavaScript (javascript)
Si la regex compile et correspond, PHP retourne :
int(1)
Si elle compile mais ne correspond pas, PHP retourne :
int(0)
Si elle ne compile pas, PHP émet un warning. Vous pouvez aussi contrôler la dernière erreur PCRE avec preg_last_error() et preg_last_error_msg().
<?php
$result = preg_match( '/^[\w\-.]+$/', 'exemple-test.php' );
if ( false === $result ) {
echo preg_last_error_msg();
}Langage du code : HTML, XML (xml)
Sur PHP récent, preg_last_error_msg() donne un message plus lisible que le simple code numérique retourné par preg_last_error().
Ne corrigez pas toutes les regex avec un remplacement automatique
Le mauvais réflexe serait de remplacer tous les - par \- dans toutes les regex du projet.
Pourquoi ? Parce que certains tirets sont de vraies plages :
/[a-z]/
/[0-9]/
/[A-Fa-f0-9]/Langage du code : JavaScript (javascript)
Dans ces exemples, le tiret doit rester un opérateur de plage. Le modifier casserait le sens de la regex.
Il faut donc corriger uniquement les tirets qui doivent représenter un caractère littéral. Oui, c’est un peu moins rapide qu’un remplacement global. Mais c’est aussi nettement moins suicidaire.
Cas fréquent : valider un slug ou un nom de fichier
Pour valider un slug simple contenant lettres, chiffres, underscores et tirets :
<?php
$slug = 'mon-article-2026';
if ( 1 === preg_match( '/^[A-Za-z0-9_-]+$/', $slug ) ) {
echo 'Slug valide';
}Langage du code : HTML, XML (xml)
Ici, le tiret est placé à la fin de la classe. Il n’est donc pas ambigu.
Pour un nom de fichier simple avec point autorisé :
<?php
$filename = 'archive-2026.zip';
if ( 1 === preg_match( '/^[A-Za-z0-9_.-]+$/', $filename ) ) {
echo 'Nom de fichier valide';
}Langage du code : HTML, XML (xml)
Cette regex accepte les lettres ASCII, les chiffres, l’underscore, le point et le tiret.
Cas Unicode : attention à \w
Le raccourci \w peut sembler pratique, mais son comportement dépend du mode Unicode et de la version PCRE2 utilisée. En PHP 8.4, PCRE2 apporte notamment des changements autour de certaines classes Unicode et de \w en mode Unicode.
Si vous validez un identifiant strictement ASCII, préférez une classe explicite :
/^[A-Za-z0-9_.-]+$/
Si vous voulez accepter des lettres Unicode, soyez explicite et ajoutez le modificateur u :
/^[\p{L}\p{N}_.-]+$/u
Cette version accepte les lettres et chiffres Unicode, ainsi que l’underscore, le point et le tiret.
Faut-il modifier PHP ou le serveur ?
Non, dans la grande majorité des cas. Il ne faut pas rétrograder PHP ni désactiver une option serveur pour corriger cette erreur.
L’erreur vient du pattern regex. La bonne correction consiste donc à rendre l’expression régulière valide pour PCRE2.
Autrement dit : ce n’est pas un problème d’infrastructure. C’est une correction de code.
Et dans WordPress ?
Sur WordPress, cette erreur apparaît souvent après une montée de version PHP. Le cœur WordPress est rarement responsable. Le problème vient plutôt d’un plugin ancien, d’un thème peu maintenu ou d’un snippet personnalisé.
Avant de modifier quoi que ce soit en production :
- reproduisez l’erreur sur un staging ;
- activez les logs WordPress ;
- identifiez le fichier et la ligne ;
- mettez le plugin ou thème à jour ;
- corrigez le code personnalisé si c’est votre code ;
- remplacez le plugin s’il n’est plus maintenu.
Si vous préparez une migration PHP plus large, consultez aussi Tester la compatibilité WordPress avec PHP 8. L’objectif est simple : éviter que la production serve de laboratoire.
Checklist de correction
- Lire le fichier et la ligne indiqués par le warning PHP.
- Trouver l’appel à
preg_match(),preg_replace()ou autre fonctionpreg_*. - Inspecter les classes de caractères entre crochets.
- Repérer les tirets ambigus.
- Échapper le tiret avec
\-si c’est un caractère littéral. - Ou placer le tiret au début ou à la fin de la classe.
- Ne pas modifier les vraies plages comme
a-zou0-9. - Tester la regex avec la version PHP utilisée en production.
- Déployer la correction sur staging avant production.
À retenir
invalid range in character classindique une plage invalide dans une classe de caractères.- Le tiret
-est souvent le coupable. - Depuis PHP 7.3, PCRE2 valide certains patterns plus strictement.
/[\w-.]+/est fragile./[\w\-.]+/est clair et compatible./[\w.-]+/est également valide.- Il faut corriger la regex, pas rétrograder PHP.
Articles liés sur SkyMinds
Pour compléter ce dépannage PHP et WordPress, ces articles peuvent vous aider :
- Tester la compatibilité WordPress avec PHP 8
- PHP : résoudre l’erreur “Creating default object from empty value”
- Installer PHP 8.5 sous Ubuntu Server
- Installer PHP 8.4 sur le serveur
- Valider votre projet VSCode avec PHP CodeSniffer et WordPress Coding Standards
- Auditer les problèmes de performance WordPress avec wp profile
FAQ
Que signifie “invalid range in character class” ?
Cette erreur signifie qu’une classe de caractères contient une plage invalide. Le cas le plus fréquent est un tiret mal placé dans une regex, par exemple /[\w-.]+/.
Pourquoi l’erreur apparaît-elle après une mise à jour PHP ?
Depuis PHP 7.3, PHP utilise PCRE2 pour les expressions régulières. PCRE2 valide certains patterns plus strictement. Une regex tolérée auparavant peut donc échouer après migration.
Quelle est la meilleure correction ?
Échappez le tiret avec \- lorsqu’il doit être lu comme un caractère littéral. Vous pouvez aussi le placer au début ou à la fin de la classe de caractères.
Faut-il rétrograder PHP pour corriger cette erreur ?
Non. Il faut corriger la regex. Rétrograder PHP masque le problème et retarde la correction. Le pattern doit être compatible avec PCRE2.
Puis-je remplacer tous les tirets par \- automatiquement ?
Non. Certains tirets définissent de vraies plages, comme [a-z] ou [0-9]. Il faut inspecter chaque classe de caractères et corriger uniquement les tirets littéraux ambigus.
Cette erreur peut-elle venir d’un plugin WordPress ?
Oui. Elle apparaît souvent dans un plugin ancien, un thème peu maintenu ou un snippet personnalisé après une montée de version PHP. Mettez d’abord les extensions à jour, puis corrigez ou remplacez le code fautif.
Sources
- PHP Manual — Character classes
- PHP Manual — preg_match
- PHP.Watch — PHP 7.3: PCRE to PCRE2 migration
- PHP.Watch — PHP 8.4 PCRE2 regular expression changes
- PHP RFC — PCRE2 migration
Besoin d'un coup de main ?
Ce bug qui traîne depuis des semaines, ce plugin qui casse votre mise en page, cette fonctionnalité que personne n'arrive à implémenter proprement — c'est exactement ce que je règle au quotidien depuis 20 ans.
Parlons de votre problème →



Merci pour le coup de pouce.
C’est quand même débile. Je dois revoir tout mes fichiers de tous mes sites pour voir si j’en ai pas d’autre…