PHP で SQL の LIKE 演算子を同じ構文でシミュレートする方法はありますか? (%
および_
ワイルドカードと一般的な$escape
エスケープ文字)? そのため:
$value LIKE $string ESCAPE $escape
データベースを使用せずにその PHP 評価を返す関数を使用できますか? ( 、$value
、$string
およびの$escape
値は既に設定されていると考えてください)。
OK、たくさんの楽しみとゲームの後で、これが私が思いついたものです:
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と異なる主な方法は、入力式をその場で正規表現に変換するのではなく、入力式をトークンに「解析」(ish)して正規表現を生成することです。
関数の最初の3つの引数は、かなり自明である必要があります。4つ目は、 PCRE修飾子を渡して、一致に使用される最終的な正規表現に影響を与えることができます。私がこれを入れた主な理由は、大文字と小文字を区別しないように合格できるようにすることです。安全に使用できる他の修飾子は考えられませんが、そうではない場合があります。 以下のコメントごとに削除i
$input
関数は、テキストが一致したかどうかを示すブール値を返すだけ$pattern
です。
編集おっと、壊れていましたが、修正されました。新しいコードパッド
編集4番目の引数を削除し、以下のコメントごとにすべての一致で大文字と小文字を区別しないようにしました
編集いくつかの小さな修正/改善:
.*?
生成された正規表現で複数のシーケンスを回避するために、最後のトークンの追跡を追加しましたこれは基本的に、次のようなものを実装する方法です。
$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]);
これは、「偽の」ワイルドカード文字の前にエスケープ文字が付けられていない限り、すべての「偽の」ワイルドカードを対応する PCRE に置き換えるために、それに基づいて動的な正規表現を形成します。この場合、置換は行われませ$wildcards
ん。$escape
この置換を行うために、$wildcard_replacement_regex
が作成されます。
$wildcard_replacement_regex
すべてが完了したら、次のようになります。
/((?:\!)?)(%|_)/
したがって、2 つのキャプチャ グループを使用して、(オプションで) エスケープ文字とワイルドカードの 1 つを取得します。これにより、コールバックでエスケープ文字を取得したかどうかをテストできます。ワイルドカードの前にエスケープ文字を取得できた場合は$matches[1]
、エスケープ文字が含まれます。そうでない場合は$matches[1]
空になります。これは、ワイルドカードを対応する PCRE に置き換えるか、そのままにしておくかを決定する方法ですpreg_quote()
。
codepadでそれをいじることができます。
たとえば、正規表現を使用できます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 互換。