有没有一种方法可以用相同的语法在PHP中模拟SQL的LIKE运算符?(%
和_
通配符以及通用$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);
}
我无法打破它,也许你可以找到一些能打破它的东西。mine与@nickb's的主要区别在于,mine将输入表达式"解析"(ish)为令牌以生成正则表达式,而不是在原地将其转换为正则表达式。
函数的前3个参数应该是不言自明的第四个允许您传递PCRE修饰符来影响用于匹配的最终正则表达式。我把它放进去的主要原因是允许您传递根据下面的评论删除i
,所以它是不区分大小写的——我想不出任何其他可以安全使用的修饰符,但事实可能并非如此
函数只是返回一个布尔值,指示$input
文本是否与$pattern
匹配。
这是的代码板
编辑错误,已损坏,现已修复。新的码盘
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等价物替换所有"假"通配符,除非"假"的通配符以转义符为前缀,在这种情况下,不会进行替换。为了进行此替换,创建了$wildcard_replacement_regex
。
一旦一切都说了算,$wildcard_replacement_regex
看起来就像这样:
/((?:'!)?)(%|_)/
因此,它使用两个捕获组来(可选地)获取转义符和其中一个通配符。这使我们能够测试它是否在回调中获取了转义字符。如果能够在通配符之前获得转义符,$matches[1]
将包含转义符。否则,$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兼容性