Моделирование LIKE в PHP

Есть ли способ имитировать оператор LIKE SQL в PHP с тем же синтаксисом? ( % и _ подстановочные знаки и общий $escape escape escape-символ)? Чтобы иметь:

 $value LIKE $string ESCAPE $escape 

у вас может быть функция, которая возвращает оценку PHP этого без использования базы данных? (учтите, что $value , $string и $escape уже установлены).

Хорошо, после долгих забав и игр вот что я придумал:

 function preg_sql_like ($input, $pattern, $escape = '\\') { // Split the pattern into special sequences and the rest $expr = '/((?:'.preg_quote($escape, '/').')?(?:'.preg_quote($escape, '/').'|%|_))/'; $parts = preg_split($expr, $pattern, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); // Loop the split parts and convert/escape as necessary to build regex $expr = '/^'; $lastWasPercent = FALSE; foreach ($parts as $part) { switch ($part) { case $escape.$escape: $expr .= preg_quote($escape, '/'); break; case $escape.'%': $expr .= '%'; break; case $escape.'_': $expr .= '_'; break; case '%': if (!$lastWasPercent) { $expr .= '.*?'; } break; case '_': $expr .= '.'; break; default: $expr .= preg_quote($part, '/'); break; } $lastWasPercent = $part == '%'; } $expr .= '$/i'; // Look for a match and return bool return (bool) preg_match($expr, $input); } 

Я не могу сломать его, может быть, вы можете найти что-то, что будет. Основной способ, которым шахта отличается от @ nickb's, заключается в том, что моя «анализирует» (ish) входное выражение в токенах, чтобы генерировать регулярное выражение, а не преобразовывать его в регулярное выражение in situ.

Первые 3 аргумента функции должны быть достаточно понятными. Четвертый позволяет передать модификаторы PCRE, чтобы повлиять на последнее регулярное выражение, используемое для совпадения. Основная причина, по которой я заключаю это, – это позволить вам передать i поэтому он нечувствителен к регистру – я не могу придумать никаких других модификаторов, которые будут безопасны в использовании, но это может быть не так. Удалено за комментарии ниже

Функция просто возвращает логическое значение, указывающее, соответствует ли текст $input $pattern или нет.

Вот его код

EDIT К сожалению, был сломан, теперь исправлен. Новый код

EDIT Удаленный четвертый аргумент и сделал все матчи без учета регистра в комментариях ниже

EDIT Несколько небольших исправлений / улучшений:

  • Добавлено начало / конец строковых утверждений сгенерированным регулярным выражением
  • Добавлено отслеживание последнего токена, чтобы избежать нескольких .*? последовательности в сгенерированном регулярном выражении

Это в основном то, как вы реализуете что-то вроде этого:

 $input = '%ST!_ING_!%'; $value = 'ANYCHARS HERE TEST_INGS%'; // Mapping of wildcards to their PCRE equivalents $wildcards = array( '%' => '.*?', '_' => '.'); // Escape character for preventing wildcard functionality on a wildcard $escape = '!'; // Shouldn't have to modify much below this $delimiter = '/'; // regex delimiter // Quote the escape characters and the wildcard characters $quoted_escape = preg_quote( $escape); $quoted_wildcards = array_map( function( $el) { return preg_quote( $el); }, array_keys( $wildcards)); // Form the dynamic regex for the wildcards by replacing the "fake" wildcards with PRCE ones $temp_regex = '((?:' . $quoted_escape . ')?)(' . implode( '|', $quoted_wildcards) . ')'; // Escape the regex delimiter if it's present within the regex $wildcard_replacement_regex = $delimiter . str_replace( $delimiter, '\\' . $delimiter, $temp_regex) . $delimiter; // Do the actual replacement $regex = preg_replace_callback( $wildcard_replacement_regex, function( $matches) use( $wildcards) { return !empty( $matches[1]) ? preg_quote( $matches[2]) : $wildcards[$matches[2]]; }, preg_quote( $input)); // Finally, test the regex against the input $value, escaping the delimiter if it's present preg_match( $delimiter . str_replace( $delimiter, '\\' . $delimiter, $regex) . $delimiter .'i', $value, $matches); // Output is in $matches[0] if there was a match var_dump( $matches[0]); 

Это создает динамическое регулярное выражение, основанное на $wildcards и $escape , чтобы заменить все «поддельные» подстановочные знаки на их эквиваленты PCRE, если только «поддельный» подстановочный знак не префикс escape-символа, и в этом случае замена не производится. Для этой замены создается $wildcard_replacement_regex .

$wildcard_replacement_regex выглядит примерно так, как только все будет сказано и сделано:

 /((?:\!)?)(%|_)/ 

Поэтому он использует две группы захвата для (необязательно) захвата escape-символа и одной из подстановочных знаков. Это позволяет нам проверить, не захватил ли этот символ escape в обратном вызове. Если ему удалось получить символ escape перед шаблоном, $matches[1] будет содержать escape-символ. Если нет, $matches[1] будет пустым. Вот как я определяю, следует ли заменять подстановочный знак эквивалентом PCRE, или оставить его в покое только с помощью preg_quote() .

Вы можете играть с ним в кодексе .

Вы можете использовать regexp, например: preg_match .

Другие примеры были слишком сложны для моего вкуса (и болезненны для моих чистых глаз кода), поэтому я переопределил функциональность в этом простом методе:

 public function like($needle, $haystack, $delimiter = '~') { // Escape meta-characters from the string so that they don't gain special significance in the regex $needle = preg_quote($needle, $delimiter); // Replace SQL wildcards with regex wildcards $needle = str_replace('%', '.*?', $needle); $needle = str_replace('_', '.', $needle); // Add delimiters, beginning + end of line and modifiers $needle = $delimiter . '^' . $needle . '$' . $delimiter . 'isu'; // Matches are not useful in this case; we just need to know whether or not the needle was found. return (bool) preg_match($needle, $haystack); } 

Модификаторы :

  • i : Игнорировать корпус.
  • s : Сделать метасимвол точки совпадающим с чем угодно, включая символы новой строки.
  • u : совместимость с UTF-8.