Simulating LIKE in PHP

后端 未结 4 622
忘掉有多难
忘掉有多难 2021-01-05 05:21

Is there a way to simulate the LIKE operator of SQL in PHP with the same syntax? (% and _ wildcards and a generic $escape escape chara

4条回答
  •  一向
    一向 (楼主)
    2021-01-05 05:35

    OK, after much fun and games here's what I have come up with:

    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);
    
    }
    

    I can't break it, maybe you can find something that will. The main way in which mine differs from @nickb's is that mine "parses"(ish) the input expression into tokens to generate a regex, rather than converting it to a regex in situ.

    The first 3 arguments to the function should be fairly self explanatory. The fourth allows you to pass PCRE modifiers to affect the final regex used for the match. The main reason I put this in is to allow you to pass i so it is case insensitive - I can't think of any other modifiers that will be safe to use but that may not be the case. Removed per comments below

    Function simply returns a boolean indicating whether the $input text matched the $pattern or not.

    Here's a codepad of it

    EDIT Oops, was broken, now fixed. New codepad

    EDIT Removed fourth argument and made all matches case-insensitive per comments below

    EDIT A couple of small fixes/improvements:

    • Added start/end of string assertions to generated regex
    • Added tracking of last token to avoid multiple .*? sequences in generated regex

提交回复
热议问题