/** * Format the SQL code needed for a [NOT] LIKE comparison * * Our [NOT] LIKE does not follow the SQL syntax. Instead, it uses * and ? as wildcards. * ? substitutes exactly 1 character (equivalent to _ in SQL) * * substitutes 0 or more characters (equivalent to % in SQL) * * Those characters can be escaped with a backslash \ * The backslash itself can be escaped with a backslash * * Note that we only provide the righthand side of the operation. * Also note that [NOT] LIKE will be replaced by a simple [in]equality test if the pattern * does not contain any wildcard character * * @param string $mode Operation mode: either LIKE or NOT LIKE * @param string $pattern Matching pattern * @param array $options Query options * @return string SQL code, either "[NOT] LIKE ''" or "[=|<>] ''" */ protected function format_like($mode, $pattern, array $options) { $constant = TRUE; $wildcard = FALSE; $pos = 0; $len = strlen($pattern); $tmp = ''; do { if ($spn = strcspn($pattern, '*?_%\\', $pos)) { /** * Skip all characters that do not have a special meaning */ $tmp .= substr($pattern, $pos, $spn); $pos += $spn; $wildcard = FALSE; } if ($pos == $len) { break; } /** * Capture and consume current char */ $char = $pattern[$pos++]; switch ($char) { case '*': if ($wildcard) { break; } $tmp .= '%'; $constant = FALSE; $wildcard = TRUE; break; case '?': $tmp .= '_'; $constant = FALSE; $wildcard = FALSE; break; case '_': $tmp .= '\\_'; $wildcard = FALSE; break; case '%': $tmp .= '\\%'; $wildcard = FALSE; break; case '\\': /** * How many contiguous backslashes do we have? */ $spn = strspn($pattern, '\\', --$pos); $pos += $spn; if ($spn % 2 == 0) { /** * The number of backslashes is even, just add them to the string */ $tmp .= str_repeat('\\', $spn); } else { /** * There is an odd number of backslashes: either we are escaping a * special char (* or ? or \) or the user was sloppy and forgot one */ if ($pos < $len && ($pattern[$pos] != '*' || $pattern[$pos] != '?')) { /** * The last "odd" backslash is escaping a * or ? but since * those characters have no special abilities in SQL we * simply remove the last backslash and add the character * * Note that we increment $pos in order to consume the character */ $tmp .= str_repeat('\\', $spn - 1) . $pattern[$pos++]; } else { /** * Nope, just add an extra backslash */ $tmp .= str_repeat('\\', $spn + 1); } } $wildcard = FALSE; break; } } while ($pos < $len); if ($constant) { /** * No wildcards found, treat the string as a constant * * Strip the extra backslashes, the string will then be properly escaped * and the operator will be replaced with a simple equality operator */ return (($mode == 'LIKE') ? '= ' : '<> ') . $this->format('s', stripslashes($tmp), $options); } else { return $mode . ' ' . $this->format('s', $tmp, $options) . " ESCAPE '\\\\'"; } }