Предупреждение: preg_replace (): неизвестный модификатор ']'

34

У меня есть следующая ошибка:

Предупреждение: preg_replace(): Неизвестный модификатор ']' в xxx.php в строке 38

Это код в строке 38:

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

Кто-нибудь может помочь мне решить эту проблему?

  • 6
    Добавьте разделители вокруг шаблона: "/<div[^>]*><ul[^>]*>/"
  • 0
    Вы пытались прочитать документацию о preg_replace() ?
Показать ещё 14 комментариев
Теги:
preg-replace

4 ответа

79
Лучший ответ

Почему возникает ошибка

В PHP регулярное выражение должно быть заключено в пару delimiters. Разделителем может быть любой небуквенный символ, не обратный слэш, не-пробельный символ; /, #, ~ являются наиболее часто используемыми. Обратите внимание, что также можно использовать ограничители стиля скобок, где открывающие и закрывающие скобки являются начальным и конечным разделителем, то есть <pattern_goes_here>, [pattern_goes_here] и т.д. Все действительны.

Ошибка "Неизвестный модификатор X" обычно возникает в следующих двух случаях:

  • Когда ваше регулярное выражение отсутствует разделители.

  • Когда вы используете разделитель внутри шаблона без экранирования.

В этом случае регулярное выражение <div[^>]*><ul[^>]*>. Механизм регулярных выражений рассматривает все от < до > как шаблон регулярного выражения, а затем все как модификаторы.

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

] здесь неизвестный модификатор, поскольку он появляется после закрывающего разделителя >. Вот почему PHP генерирует эту ошибку.

В зависимости от шаблона жалоба неизвестного модификатора могла бы быть примерно *, +, p, / или ) или почти любой другой буквы/символа. Только imsxeADSUXJu являются действительными модификаторами PCRE.

Как исправить его

Исправить легко. Просто оберните шаблон регулярного выражения любыми действительными разделителями. В этом случае вы можете выбрать ~ и получить следующее:

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

Если вы получаете эту ошибку, несмотря на использование разделителя, возможно, это связано с тем, что сам шаблон содержит несвязанные вхождения указанного разделителя.

Или разделители исключений

/foo[^/]+bar/i, безусловно, вызовет ошибку. Таким образом, вы можете избежать этого с помощью обратного слэша \, если он появляется где-нибудь в пределах регулярного выражения:

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

Это утомительная работа, если ваш шаблон регулярного выражения содержит так много вхождений символа разделителя.

Более чистый способ, конечно, состоял бы в том, чтобы использовать другой разделитель вообще. В идеале символ, который не появляется нигде внутри шаблона регулярного выражения, скажем # - #foo[^/]+bar#i.

Дополнительная информация:

  • 0
    Я заметил, что то же самое происходит, когда один из разделителей находится внутри preg_quote() , то есть что-то вроде preg_replace('/'.preg_quote('/').'/i','',$string); выдает такую же ошибку по теме. preg_quote() слэш не должен быть экранирован preg_quote() ?
  • 0
    Я столкнулся с этим при обновлении некоторых старых вызовов ereg до preg_match . Пришлось ввести разделители.
14

Другие примеры

Ссылочный ответ уже объясняет причину появления предупреждений "Неизвестный модификатор". Это просто сравнение других типичных вариантов.

  • Если вы забудете добавить регулярное выражение / разделители /, первый небуквенный символ будет считаться одним. Поэтому предупреждение часто о том, что следует за метасимволом группировки (…), […]:

    preg_match("[a-zA-Z]+:\s*.$"
                ↑      ↑
    
  • Иногда ваше регулярное выражение уже использует пользовательский разделитель (: здесь), но по-прежнему содержит тот же символ, что и литерал без экранирования. Затем его ошибочно принимают за преждевременный разделитель. Вот почему следующий символ получает трофей "Неизвестный модификатор:":

    preg_match(":\[[\d:/]+\]:"
                ↑          ↑
    
  • При использовании классического / разделителя, будьте осторожны, чтобы буквально не содержать его в регулярном выражении. Это чаще всего происходит при попытке сопоставления неэкранированных имен файлов:

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

    Или при сопоставлении тегов стиля угловой/квадратной скобки:

    preg_match("/<%tmpl:id>(.*)</%tmpl:id>/Ui"
                ↑                        ↑
    
  • Шаблоны регулярных выражений в стиле шаблонов (Smarty или BBCode) часто требуют {…} или […] скобок. Обе обычно следует избегать. (Самая внешняя пара {} является исключением).

    Они также неверно интерпретируются как парные разделители, когда фактический разделитель не используется. Если они затем также используются как буквальный символ внутри, то это, конечно... ошибка.

    preg_match("{bold[^}]+}"
                ↑        ↑
    
  • Всякий раз, когда в предупреждении говорится " Разделитель не должен быть буквенно-цифровым или обратным слешем ", вы также полностью забываете разделители:

    preg_match("ab?c*"
                ↑
    
  • " Неизвестный модификатор" g " " часто указывает на регулярное выражение, которое было скопировано дословно из JavaScript или Perl.

    preg_match("/abc+/g"
                      
    

    PHP не использует глобальный флаг /g. Вместо этого функция preg_replace работает во всех случаях, а preg_match_all является "глобальным" поисковым кулоном для единственного случая preg_match.

    Итак, просто удалите флаг /g.

    Смотрите также:
    · Предупреждение: preg_replace(): неизвестный модификатор 'g'
    · Preg_replace: bad regex == 'Неизвестный модификатор'?

  • Более специфический случай относится к флагу PCRE_EXTENDED /x. Это часто (или должно быть) используется для того, чтобы сделать регулярные выражения более высокими и удобочитаемыми.

    Это позволяет использовать встроенные # комментарии. PHP реализует разделители регулярных выражений поверх PCRE. Но это не относится к # каким-либо особым образом. Вот как буквальный разделитель в комментарии # может стать ошибкой:

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

    (Также следует отметить, что использование # качестве разделителя #abc+#x может быть вдвойне нежелательно.)

  • Для интерполяции переменных в регулярное выражение требуется, чтобы они были предварительно -escaped или действительными регулярными выражениями. Вы не можете заранее сказать, сработает ли это:

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

    В таких случаях лучше всего применять $var = preg_quote($var, "/").

    Смотрите также:
    · Неизвестный модификатор '/' в...? что это?

    Другая альтернатива - использование escape-символов \Q…\E для строк без кавычек:

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

    Обратите внимание, что это просто ярлык удобства для метасимволов, а не надежный/безопасный. Он развалился бы в случае, если бы $var содержал сам литерал '\E' (хотя вряд ли). И это не маскирует сам разделитель.

  • Устаревший модификатор /e - это совершенно другая проблема. Это не имеет ничего общего с разделителями, но режим интерпретации неявных выражений постепенно сокращается. Смотрите также: Замените устаревший preg_replace/e на preg_replace_callback

Альтернативные разделители регулярных выражений

Как уже упоминалось, самым быстрым решением этой ошибки является выбор отдельного разделителя. Можно использовать любой не буквенный символ. Визуально отличительные из них часто предпочтительнее:

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

Технически вы можете использовать $abc$ или |abc| для разделителей. Однако лучше избегать символов, которые сами являются метасимволами регулярных выражений.

Хеш # как разделитель тоже довольно популярен. Но следует соблюдать осторожность в сочетании с модификатором читаемости x/PCRE_EXTENDED. Вы не можете использовать # inline или (?#…) Комментарии тогда, потому что они будут перепутаны как разделители.

Разделители только для цитат

Иногда вы видите, что " и ' используются в качестве разделителей регулярных выражений в паре со своим контрагентом в виде строкового вложения PHP:

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

Что совершенно верно в отношении PHP. Иногда это удобно и ненавязчиво, но не всегда разборчиво в IDE и редакторах.

Парные разделители

Интересным вариантом являются парные разделители. Вместо использования одного и того же символа на обоих концах регулярного выражения вы можете использовать любую комбинацию <...> (...) [...] {...} скобок/фигурных скобок.

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

Хотя большинство из них также служат метасимволами регулярных выражений, вы часто можете использовать их без дополнительных усилий. Пока эти конкретные скобки/парены в регулярном выражении соединены или экранированы правильно, эти варианты вполне читабельны.

Необычные разделители регулярных выражений

Несколько ленивый трюк (который не подтверждается) использует непечатаемые символы ASCII в качестве разделителей. Это легко работает в PHP, используя двойные кавычки для строки регулярного выражения и восьмеричные экранирующие символы для разделителей:

 preg_match("\001 abc+ \001mix"

\001 - это просто управляющий символ , который обычно не требуется. Поэтому маловероятно, что он появится в большинстве шаблонов регулярных выражений. Что делает его здесь подходящим, хотя и не очень разборчивым.

К сожалению, вы не можете использовать символы Юникода качестве разделителей. PHP допускает только однобайтовые символы. И почему так? Хорошо, рад, что вы спросили:

Разграничители PHP на PCRE

В preg_* используется механизм регулярных выражений PCRE, который сам по себе не заботится о разделителях и не предоставляет их. Для сходства с Perl их preg_* функции preg_*. Вот почему вы также можете использовать буквы-модификаторы /ism вместо констант в качестве параметра.

Смотрите ext/pcre/php_pcre.c о том, как строка regex предварительно обрабатывается:

  • Сначала игнорируются все ведущие пробелы.

  • Любой не буквенно-цифровой символ принимается как предполагаемый разделитель. Обратите внимание, что PHP учитывает только однобайтовые символы:

    delimiter = *p++;
    if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
            php_error_docref(NULL,E_WARNING, "Delimiter must not…");
            return NULL;
    }
    
  • Остальная часть строки регулярного выражения проходит слева направо. Только символы обратной косой черты \\ -escaped игнорируются. \Q и \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++;
    }
    

    Он ищет правильно спаренный левый и правый разделитель, но игнорирует другие типы скобок/скобок при подсчете.

  • Необработанная строка регулярного выражения передается в бэкэнд PCRE только после того, как флаги разделителя и модификатора были удалены.

Теперь все это несколько не имеет значения. Но объясняет, откуда приходят предупреждения разделителя. И вся эта процедура должна иметь как минимум совместимость с Perl. Конечно, есть несколько незначительных отклонений, таких как контекст класса символов […] не получает специальной обработки в PHP.

Больше ссылок

  • 0
    Очень хорошее объяснение
0

Если вы хотите получить исключение (InvalidPatternException) вместо предупреждений или использования preg_last_error() - рассмотрите возможность использования библиотеки T-Regx:

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

Если вы хотите получить исключение (InvalidPatternException) вместо предупреждений - рассмотрите возможность использования библиотеки T-Regx.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню