The 5th Annual China PHP Conference

Модификаторы шаблонов

Ниже перечислены все доступные на сегодняшний день модификаторы. Имя, взятое в круглые скобки, указывает внутреннее PCRE имя для данного модификатора. Пробелы и переводы строк в модификаторах игнорируются, другие символы вызывают ошибки.

i (PCRE_CASELESS)
Если этот модификатор используется, символы в шаблоне соответствуют символам как верхнего, так и нижнего регистра.
m (PCRE_MULTILINE)
По умолчанию, PCRE обрабатывает данные как однострочную символьную строку (даже если она содержит несколько разделителей строк). Метасимвол начала строки '^' соответствует только началу обрабатываемого текста, в то время как метасимвол "конец строки" '$' соответствует концу текста, либо позиции перед завершающим текст переводом строки (в случае, если модификатор D не установлен). В Perl ситуация полностью аналогична. Если этот модификатор используется, метасимволы "начало строки" и "конец строки" также соответствуют позициям перед произвольным символом перевода и строки и, соответственно, после, как и в самом начале и в самом конце строки. Это соответствует Perl-модификатору /m. В случае, если обрабатываемый текст не содержит символов перевода строки, либо шаблон не содержит метасимволов '^' или '$', данный модификатор не имеет никакого эффекта.
s (PCRE_DOTALL)
Если данный модификатор используется, метасимвол "точка" в шаблоне соответствует всем символам, включая перевод строк. Без него - всем, за исключением переводов строк. Этот модификатор эквивалентен записи /s в Perl. Класс символов, построенный на отрицании, например [^a], всегда соответствует переводу строки, независимо от наличия этого модификатора.
x (PCRE_EXTENDED)
Если используется данный модификатор, неэкранированные пробелы, символы табуляции и пустой строки будут проигнорированы в шаблоне, если они не являются частью символьного класса. Также игнорируются все символы между неэкранированным символом '#' (если он не является частью символьного класса) и символом перевода строки (включая сами символы '\n' и '#'). Это эквивалентно Perl-модификатору /x, и позволяет размещать комментарий в сложных шаблонах. Замечание: это касается только символьных данных. Пробельные символы не фигурируют в служебных символьных последовательностях, к примеру, в последовательности '(?(', открывающей условную подмаску.
e (PREG_REPLACE_EVAL)
Внимание

Эта функциональность считается УСТАРЕВШЕЙ с PHP 5.5.0, и УДАЛЕНА в PHP 7.0.0.

Если используется данный устаревший модификатор, preg_replace() после выполнения стандартных подстановок в заменяемой строке интерпретирует ее как PHP-код и использует результат для замены искомой строки. Одинарные и двойные кавычки, обратные слеши (\) NULL-символы будут проэкранированы обратными слешами в подставляемых обратных ссылках.
Предостережение

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

Предостережение

Убедитесь, что параметр replacement содержит строку с корректным PHP-кодом, иначе PHP сообщит об ошибке парсинга на строке, содержащей вызов preg_replace().

Предостережение

Использовать этот модификатор не рекомендуется, так как это может легко добавить уязвимости в системе безопасности:

<?php
$html 
$_POST['html'];

// Заголовки в верхнем регистре
$html preg_replace(
    
'(<h([1-6])>(.*?)</h\1>)e',
    
'"<h$1>" . strtoupper("$2") . "</h$1>"',
    
$html
);

Приведенный выше код может быть легко скомпрометирован путем передачи такой строки как <h1>{${eval($_GET[php_code])}}</h1>. Это дает атакующему возможность исполнить произвольный PHP-код и по существу предоставляет ему почти полный доступ к вашему серверу.

Для предотвращения этого типа уязвимости с удаленным исполнением кода следует использовать функцию preg_replace_callback():

<?php
$html 
$_POST['html'];

// Заголовки в верхнем регистре
$html preg_replace_callback(
    
'(<h([1-6])>(.*?)</h\1>)',
    function (
$m) {
        return 
"<h$m[1]>" strtoupper($m[2]) . "</h$m[1]>";
    },
    
$html
);

Замечание:

Этот модификатор используется только в функции preg_replace(), в других PCRE функциях он игнорируется.

A (PCRE_ANCHORED)
Если используется данный модификатор, соответствие шаблону будет достигаться только в том случае, если он "заякорен", т.е. соответствует началу строки, в которой производится поиск. Того же эффекта можно достичь подходящей конструкцией с вложенным шаблоном, которая является единственным способом реализации этого поведения в Perl.
D (PCRE_DOLLAR_ENDONLY)
Если используется данный модификатор, метасимвол $ в шаблоне соответствует только окончанию обрабатываемых данных. Без этого модификатора метасимвол $ соответствует также позиции перед последним символом, в случае, если им является перевод строки (но не распространяется на любые другие переводы строк). Данный модификатор игнорируется, если используется модификатор m. В языке Perl аналогичный модификатор отсутствует.
S
В случае, если планируется многократно использовать шаблон, имеет смысл потратить немного больше времени на его анализ, чтобы уменьшить время его выполнения. В случае, если данный модификатор используется, проводится дополнительный анализ шаблона. В настоящем это имеет смысл только для "незаякоренных" шаблонов, не начинающихся с какого-либо определенного символа.
U (PCRE_UNGREEDY)
Этот модификатор инвертирует жадность квантификаторов, таким образом они по умолчанию не жадные. Но становятся жадными, если за ними следует символ ?. Такая возможность не совместима с Perl. Его также можно установить с помощью (?U) установки модификатора внутри шаблона или добавив знак вопроса после квантификатора (например, .*?).

Замечание:

В нежадном режиме обычно невозможно совпадение символов превышающих pcre.backtrack_limit.

X (PCRE_EXTRA)
Этот модификатор включает дополнительную функциональность PCRE, которая не совместима с Perl: любой обратный слеш в шаблоне, за которым следует символ, не имеющий специального значения, приводят к ошибке. Это обусловлено тем, что подобные комбинации зарезервированы для дальнейшего развития. По умолчанию же, как и в Perl, слеш со следующим за ним символом без специального значения трактуется как опечатка. На сегодняшний день это все возможности, которые управляются данным модификатором
J (PCRE_INFO_JCHANGED)
Модификатор (?J) меняет значение локальной опции PCRE_DUPNAMES - подшаблоны могут иметь одинаковые имена.
u (PCRE_UTF8)
Этот модификатор включает дополнительную функциональность PCRE, которая не совместима с Perl: шаблон и целевая строка обрабатываются как UTF-8 строки. Недопустимая целевая строка приводит к тому, что функции preg_* ничего не находят, а неправильный шаблон приводит к ошибке уровня E_WARNING. Пятый и шестой октеты UTF-8 последовательности рассматриваются недопустимыми с PHP 5.3.4 (согласно PCRE 7.3 2007-08-28); ранее они считались допустимыми.

add a note add a note

User Contributed Notes 6 notes

up
22
hfuecks at nospam dot org
11 years ago
Regarding the validity of a UTF-8 string when using the /u pattern modifier, some things to be aware of;

1. If the pattern itself contains an invalid UTF-8 character, you get an error (as mentioned in the docs above - "UTF-8 validity of the pattern is checked since PHP 4.3.5"

2. When the subject string contains invalid UTF-8 sequences / codepoints, it basically result in a "quiet death" for the preg_* functions, where nothing is matched but without indication that the string is invalid UTF-8

3. PCRE regards five and six octet UTF-8 character sequences as valid (both in patterns and the subject string) but these are not supported in Unicode ( see section 5.9 "Character Encoding" of the "Secure Programming for Linux and Unix HOWTO" - can be found at http://www.tldp.org/ and other places )

4. For an example algorithm in PHP which tests the validity of a UTF-8 string (and discards five / six octet sequences) head to: http://hsivonen.iki.fi/php-utf8/

The following script should give you an idea of what works and what doesn't;

<?php
$examples
= array(
   
'Valid ASCII' => "a",
   
'Valid 2 Octet Sequence' => "\xc3\xb1",
   
'Invalid 2 Octet Sequence' => "\xc3\x28",
   
'Invalid Sequence Identifier' => "\xa0\xa1",
   
'Valid 3 Octet Sequence' => "\xe2\x82\xa1",
   
'Invalid 3 Octet Sequence (in 2nd Octet)' => "\xe2\x28\xa1",
   
'Invalid 3 Octet Sequence (in 3rd Octet)' => "\xe2\x82\x28",

   
'Valid 4 Octet Sequence' => "\xf0\x90\x8c\xbc",
   
'Invalid 4 Octet Sequence (in 2nd Octet)' => "\xf0\x28\x8c\xbc",
   
'Invalid 4 Octet Sequence (in 3rd Octet)' => "\xf0\x90\x28\xbc",
   
'Invalid 4 Octet Sequence (in 4th Octet)' => "\xf0\x28\x8c\x28",
   
'Valid 5 Octet Sequence (but not Unicode!)' => "\xf8\xa1\xa1\xa1\xa1",
   
'Valid 6 Octet Sequence (but not Unicode!)' => "\xfc\xa1\xa1\xa1\xa1\xa1",
);

echo
"++Invalid UTF-8 in pattern\n";
foreach (
$examples as $name => $str ) {
    echo
"$name\n";
   
preg_match("/".$str."/u",'Testing');
}

echo
"++ preg_match() examples\n";
foreach (
$examples as $name => $str ) {
   
   
preg_match("/\xf8\xa1\xa1\xa1\xa1/u", $str, $ar);
    echo
"$name: ";

    if (
count($ar) == 0 ) {
        echo
"Matched nothing!\n";
    } else {
        echo
"Matched {$ar[0]}\n";
    }
   
}

echo
"++ preg_match_all() examples\n";
foreach (
$examples as $name => $str ) {
   
preg_match_all('/./u', $str, $ar);
    echo
"$name: ";
   
   
$num_utf8_chars = count($ar[0]);
    if (
$num_utf8_chars == 0 ) {
        echo
"Matched nothing!\n";
    } else {
        echo
"Matched $num_utf8_chars character\n";
    }
   
}
?>
up
9
phpman at crustynet dot org dot uk
6 years ago
The description of the "u" flag is a bit misleading. It suggests that it is only required if the pattern contains UTF-8 characters, when in fact it is required if either the pattern or the subject contain UTF-8. Without it, I was having problems with preg_match_all returning invalid multibyte characters when given a UTF-8 subject string.

It's fairly clear if you read the documentation for libpcre:

       In  order  process  UTF-8 strings, you must build PCRE to include UTF-8
       support in the code, and, in addition,  you  must  call  pcre_compile()
       with  the  PCRE_UTF8  option  flag,  or the pattern must start with the
       sequence (*UTF8). When either of these is the case,  both  the  pattern
       and  any  subject  strings  that  are matched against it are treated as
       UTF-8 strings instead of strings of 1-byte characters.

[from http://www.pcre.org/pcre.txt]
up
8
Daniel Klein
5 years ago
If the _subject_ contains utf-8 sequences the 'u' modifier should be set, otherwise a pattern such as /./ could match a utf-8 sequence as two to four individual ASCII characters. It is not a requirement, however, as you may have a need to break apart utf-8 sequences into single bytes. Most of the time, though, if you're working with utf-8 strings you should use the 'u' modifier.

If the subject doesn't contain any utf-8 sequences (i.e. characters in the range 0x00-0x7F only) but the pattern does, as far as I can work out, setting the 'u' modifier would have no effect on the result.
up
6
michal dot kocarek at brainbox dot cz
8 years ago
In case you're wondering, what is the meaning of "S" modifier, this paragraph might be useful:

When "S" modifier is set, PHP calls the pcre_study() function from the PCRE API before executing the regexp. Result from the function is passed directly to pcre_exec().

For more information about pcre_study() and "Studying the pattern" check the PCRE manual on http://www.pcre.org/pcre.txt

PS: Note that function names "pcre_study" and "pcre_exec" used here refer to PCRE library functions written in C language and not to any PHP functions.
up
4
varrah NO_GARBAGE_OR_SPAM AT mail DOT ru
11 years ago
Spent a few days, trying to understand how to create a pattern for Unicode chars, using the hex codes. Finally made it, after reading several manuals, that weren't giving any practical PHP-valid examples. So here's one of them:

For example we would like to search for Japanese-standard circled numbers 1-9 (Unicode codes are 0x2460-0x2468) in order to make it through the hex-codes the following call should be used:
preg_match('/[\x{2460}-\x{2468}]/u', $str);

Here $str is a haystack string
\x{hex} - is an UTF-8 hex char-code
and /u is used for identifying the class as a class of Unicode chars.

Hope, it'll be useful.
up
4
ebarnard at marathonmultimedia dot com
10 years ago
When adding comments with the /x modifier, don't use the pattern delimiter in the comments. It may not be ignored in the comments area. Example:

<?php
$target
= 'some text';
if(
preg_match('/
                e # Comments here
               /x'
,$target)) {
    print
"Target 1 hit.\n";
}
if(
preg_match('/
                e # /Comments here with slash
               /x'
,$target)) {
    print
"Target 1 hit.\n";
}
?>

prints "Target 1 hit." but then generates a PHP warning message for the second preg_match():

Warning:  preg_match() [function.preg-match]: Unknown modifier 'C' in /ebarnard/x-modifier.php on line 11
To Top