web-dev-qa-db-fra.com

Attention: preg_replace (): modificateur inconnu ']'

J'ai l'erreur suivante:

Attention: preg_replace (): modificateur inconnu ']' dans xxx.php à la ligne 38

C'est le code à la ligne 38:

<?php echo str_replace("</ul></div>", "", preg_replace("<div[^>]*><ul[^>]*>", "", wp_nav_menu(array('theme_location' => 'nav', 'echo' => false)) )); ?>

Quelqu'un peut-il m'aider s'il vous plaît à résoudre ce problème?

34
user3122995

Pourquoi l'erreur se produit

En PHP, une expression régulière doit être incluse dans une paire de délimiteurs . Un délimiteur peut être n’importe quel caractère non alphanumérique, sans barre oblique inverse ni espace; /, #, ~ sont les plus couramment utilisés. Notez qu'il est également possible d'utiliser des délimiteurs de style entre crochets lorsque les crochets d'ouverture et de fermeture sont les délimiteurs de début et de fin, c'est-à-dire <pattern_goes_here>, [pattern_goes_here], etc. sont tous valides.

L'erreur " Unknown modificateur X " se produit généralement dans les deux cas suivants:

  • Lorsque votre expression régulière est des délimiteurs manquants .

  • Lorsque vous utilisez le délimiteur à l'intérieur du motif sans l'échapper , il.

Dans ce cas, l'expression régulière est <div[^>]*><ul[^>]*>. Le moteur de regex considère tout du < au > en tant que motif de regex, et tout ensuite en tant que modificateurs.

Regex: <div[^>  ]*><ul[^>]*>
       │     │  │          │
       └──┬──┘  └────┬─────┘
       pattern    modifiers

] ici est un modificateur inconnu, car il apparaît après le délimiteur de fermeture >. C’est pourquoi PHP renvoie cette erreur.

Selon le modèle, le problème du modificateur inconnu pourrait également concerner *, +, p, / ou ) ou presque toute autre lettre/symbole. imsxeADSUXJu sont uniquement modificateurs PCRE valides .

Comment le réparer

La solution est facile. Enveloppez simplement votre motif regex avec tous les délimiteurs valides. Dans ce cas, vous pourriez choisir ~ et obtenez ce qui suit:

~<div[^>]*><ul[^>]*>~
│                   │
│                   └─ ending delimiter
└───────────────────── starting delimiter

Si vous recevez cette erreur alors que vous avez utilisé un délimiteur, cela peut être dû au fait que le modèle lui-même contient des occurrences non échappées dudit délimiteur.

Ou des délimiteurs d'évasion

/foo[^/]+bar/i jetterait certainement une erreur. Donc, vous pouvez y échapper en utilisant un \ barre oblique inverse si elle apparaît n'importe où dans la regex:

/foo[^\/]+bar/i
│      │     │
└──────┼─────┴─ actual delimiters
       └─────── escaped slash(/) character

C'est un travail fastidieux si votre motif regex contient autant d'occurrences du caractère délimiteur.

La méthode la plus propre, bien sûr, consisterait à utiliser un délimiteur différent. Idéalement, un caractère qui n'apparaît nulle part dans le motif regex, par exemple, # - #foo[^/]+bar#i.

Plus de lecture:

84
Amal Murali

Autres exemples

La réponse de référence explique déjà le motif des avertissements "Modificateur inconnu". Ceci est juste une comparaison d'autres variantes typiques.

  • En oubliant d’ajouter regex /delimiters/, le premier symbole non-lettre sera supposé en être un. Par conséquent, l'avertissement concerne souvent ce qui suit un méta symbole (…), […] de groupement:

    preg_match("[a-zA-Z]+:\s*.$"
                ↑      ↑⬆
    
  • Parfois, votre expression rationnelle utilise déjà un délimiteur personnalisé (: ici), mais contient toujours le même caractère que le littéral non échappé. Il est alors confondu avec un délimiteur prématuré. C'est pourquoi le tout prochain symbole reçoit le trophée "Inconnu du modificateur":

    preg_match(":\[[\d:/]+\]:"
                ↑     ⬆     ↑
    
  • Lorsque vous utilisez le délimiteur classique /, veillez à ne pas l’avoir littéralement dans l’expression régulière. Cela se produit le plus souvent lorsque vous essayez de faire correspondre les noms de fichiers non échappés :

    preg_match("/pathname/filename/i"
                ↑        ⬆         ↑
    

    Ou lorsque vous faites correspondre des balises de style angle/crochet :

    preg_match("/<%tmpl:id>(.*)</%tmpl:id>/Ui"
                ↑               ⬆         ↑
    
  • Les modèles de regex de type modèle (Smarty ou BBCode) nécessitent souvent des crochets {…} ou […]. Les deux devraient normalement être échappés. (Une paire {} la plus à l'extérieur étant toutefois l'exception).

    Ils sont également mal interprétés comme délimiteurs appariés lorsqu'aucun délimiteur réel n'est utilisé. S'ils sont également utilisés comme caractère littéral à l'intérieur, c'est bien sûr… une erreur.

    preg_match("{bold[^}]+}"
                ↑      ⬆  ↑
    
  • Lorsque l'avertissement indique ", le délimiteur ne doit pas être alphanumérique ni antislash ", vous avez également complètement oublié les délimiteurs:

    preg_match("ab?c*"
                ↑
    
  • " Le modificateur inconnu 'g' " indique souvent une expression régulière copiée telle quelle à partir de JavaScript ou de Perl.

    preg_match("/abc+/g"
                      ⬆
    

    PHP n'utilise pas l'indicateur global /g. Au lieu de cela, la fonction preg_replace fonctionne sur toutes les occurrences, et preg_match_all est le pendant "global" de la recherche à une occurrence preg_match .

    Supprimez simplement l'indicateur /g.

    Voir également:
    · Avertissement: preg_replace (): modificateur inconnu 'g'
    · preg_replace: mauvaise expression rationnelle == 'Modificateur inconnu'?

  • Un cas plus particulier concerne l'indicateur PCRE_EXTENDED /x. Ceci est souvent (ou devrait être) utilisé pour rendre les expressions rationnelles plus hautes et plus lisibles.

    Cela permet d’utiliser des commentaires # en ligne. PHP implémente les délimiteurs de regex sur PCRE. Mais cela ne traite pas # de manière particulière. Voici comment un délimiteur littéral dans un commentaire # peut devenir une erreur:

    preg_match("/
       ab?c+  # Comment with / slash in between
    /x"
    

    (Il convient également de noter que l’utilisation de # comme séparateur #abc+#x peut être doublement déconseillée.)

  • Pour interpoler des variables dans une expression rationnelle, elles doivent être pré-échappées ou être des expressions rationnelles valides elles-mêmes. Vous ne pouvez pas dire à l'avance si ça va marcher:

     preg_match("/id=$var;/"
                 ↑    ↺   ↑
    

    Il est préférable d'appliquer $var = preg_quote($var, "/") dans de tels cas.

    Voir également:
    · Modificateur inconnu '/' dans ...? qu'est-ce que c'est?

    Une autre alternative consiste à utiliser \Q…\E échappements pour les chaînes littérales non entre guillemets:

     preg_match("/id=\Q{$var}\E;/mix");
    

    Notez que ceci est simplement un raccourci de commodité pour les méta-symboles, pas fiable/sûr. Il s'effondrerait au cas où $var contiendrait un '\E' littéral lui-même (bien que cela soit peu probable). Et cela ne masque pas le délimiteur lui-même.

  • Le modificateur obsolète/e est un problème totalement différent. Cela n'a rien à voir avec les délimiteurs, mais le mode d'interprétation de l'expression implicite est progressivement supprimé. Voir aussi: Remplacez preg_replace/e obsolète par preg_replace_callback

Délimiteurs alternatifs de regex

Comme mentionné précédemment, la solution la plus rapide à cette erreur consiste simplement à choisir un délimiteur distinct. Tout symbole non-lettre peut être utilisé. Les visuellement distinctifs sont souvent préférés:

  • ~abc+~
  • !abc+!
  • @abc+@
  • #abc+#
  • =abc+=
  • %abc+%

Techniquement, vous pouvez utiliser $abc$ ou |abc| pour les délimiteurs. Cependant, il est préférable d'éviter les symboles qui servent de méta-caractères regex eux-mêmes.

Le hash # comme délimiteur est également très populaire. Toutefois, vous devez faire attention en combinaison avec le modificateur de lisibilité x/PCRE_EXTENDED. Vous ne pouvez pas utiliser les commentaires # inline ou (?#…) alors, car ils seraient confondus en tant que délimiteurs.

Délimiteurs de devis uniquement

De temps en temps, " et ' sont utilisés comme délimiteurs de regex associés à leur contrepartie sous la forme PHP chaîne.

  preg_match("'abc+'"
  preg_match('"abc+"'

Ce qui est parfaitement valable en ce qui concerne PHP. C'est parfois pratique et discret, mais pas toujours lisible dans les IDE et les éditeurs.

Délimiteurs appariés

Une variante intéressante sont les délimiteurs appariés. Au lieu d'utiliser le même symbole aux deux extrémités d'une expression rationnelle, vous pouvez utiliser n'importe quelle combinaison <...>(...)[...]{...}.

  preg_match("(abc+)"   # just delimiters here, not a capture group

Bien que la plupart d'entre eux servent également de méta-caractères regex, vous pouvez souvent les utiliser sans effort supplémentaire. Tant que ces accolades/parenthèses spécifiques au sein de la regex sont appariées ou échappées correctement, ces variantes sont assez lisibles.

Délimiteurs de regex fantaisie

Une astuce quelque peu paresseuse (qui n’est pas approuvée par la présente) consiste à utiliser des caractères non imprimables ASCII comme délimiteurs. Cela fonctionne facilement dans PHP en utilisant des guillemets doubles pour la chaîne regex et des échappements octaux pour les délimiteurs:

 preg_match("\001 abc+ \001mix"

Le \001 est juste un caractère de contrôle  ce n'est généralement pas nécessaire. Par conséquent, il est hautement improbable d'apparaître dans la plupart des motifs regex. Ce qui le rend approprié ici, même s'il n'est pas très lisible.

Malheureusement, vous ne pouvez pas utiliser les glyps Unicode comme délimiteurs. PHP n'autorise que les caractères à octet unique. Et pourquoi est-ce que? Eh bien, content que vous ayez demandé:

Délimiteurs de PHP sur PCRE

Les fonctions preg_* utilisent le moteur de regex PCRE , qui lui-même ne tient pas compte des délimiteurs. Pour ressembler à Perl, les fonctions preg_* les implémentent. C'est aussi pourquoi vous pouvez utiliser les lettres de modificateur /ism au lieu de simplement constantes en tant que paramètre .

Voir ext/pcre/php_pcre.c pour savoir comment la chaîne de regex est prétraitée:

  • Tout d'abord, tous les espaces sont ignorés.

  • Tout symbole non alphanumérique est considéré comme un séparateur présumé. Notez que PHP n'honore que les caractères à octet unique:

    delimiter = *p++;
    if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
            php_error_docref(NULL,E_WARNING, "Delimiter must not…");
            return NULL;
    }
    
  • Le reste de la chaîne d'expression rationnelle est parcouru de gauche à droite. Seuls les barres obliques inverses \\- échappées sont ignorées. \Q et \E d'échappement n'est pas honoré.

  • Si le délimiteur est retrouvé, il est vérifié que le reste ne contient que des lettres modificatrices.

  • Si le délimiteur est l'un des accolades/crochets pouvant être appariés ([{< )]}> )]}>, la logique de traitement est plus élaborée.

    int brackets = 1;   /* brackets nesting level */
    while (*pp != 0) {
            if (*pp == '\\' && pp[1] != 0) pp++;
            else if (*pp == end_delimiter && --brackets <= 0)
                    break;
            else if (*pp == start_delimiter)
                    brackets++;
            pp++;
    }
    

    Il recherche les délimiteurs gauche et droit correctement appariés, mais ignore les autres types d'accolades/crochets lors du comptage.

  • La chaîne de regex brute est transmise au backend PCRE uniquement après que les indicateurs de délimiteur et de modificateur ont été supprimés.

Maintenant, tout cela est un peu hors de propos. Mais explique d'où viennent les avertissements de délimiteur. Et toute cette procédure doit avoir un minimum de compatibilité Perl. Bien sûr, il existe quelques différences mineures, comme le contexte de la classe de caractères […] qui ne reçoit pas de traitement spécial en PHP.

Plus de références

14
mario

Si vous souhaitez obtenir une exception (InvalidPatternException) au lieu d'avertissements ou d'utilisation de preg_last_error(), envisagez d'utiliser la bibliothèque T-Regx :

<?php
try 
{
    return pattern('invalid] pattern')->match($s)->all();
}
catch (InvalidPatternException $e) 
{
    // your pattern was invalid
}
0
Danon