Signature
preg_quote(string $str, ?string $delimiter = null): stringPurpose
A regular expression pattern is a small program, not plain text. If a string comes from a variable, such as a product code, a search term or a configured keyword, characters like ., + or ( can change what the pattern means. preg_quote() solves this. It puts a backslash in front of every character that has special meaning in regex syntax, so the result can be embedded in a pattern and matched literally.
Signature
Parameters and defaults
$stris the input string to be quoted.$delimiteris optional and defaults tonull. When you pass one, that character is escaped as well. Pass the same delimiter you use around the pattern./is the most common one. Because the delimiter is not part of regex syntax itself,preg_quote()does not escape/unless you ask it to.
The characters escaped by default are:
. \ + * ? [ ^ ] $ ( ) { } = ! < > | : - #
The # character is part of this list since PHP 7.3.0. A nullable $delimiter has been accepted since PHP 7.2.0.
Return value and failure cases
The function returns the quoted string. The manual documents no failure return value for it: any string can be quoted, and the function is binary-safe. The function only escapes text. It never adds delimiters or modifiers, and it does not check whether the final pattern is valid. If you assemble a broken pattern elsewhere, the error will appear when you call preg_match() or a similar function.
Example 1: quoting a delimiter
The manual's classic case contains a dollar sign and a slash. The dollar sign is always escaped. The slash is escaped here only because '/' is passed as the delimiter. Spaces and letters stay unchanged.
$keywords = '$40 for a g3/400';
echo preg_quote($keywords, '/'), "\n";\$40 for a g3\/400
Example 2: matching user text literally
The needle 3.5+tax contains two metacharacters. Without quoting, . means any character and + repeats the previous item, so the unquoted pattern does not match the text it came from.
$text = 'Price: 3.5+tax (approx)';
$needle = '3.5+tax';
$pattern = '/' . preg_quote($needle, '/') . '/';
echo json_encode([
'pattern' => $pattern,
'matched' => preg_match($pattern, $text) === 1,
'unquotedMatched' => preg_match('/' . $needle . '/', $text) === 1,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"pattern":"/3\\.5\\+tax/","matched":true,"unquotedMatched":false}
Example 3: building an alternation with another delimiter
When you combine several literal strings, quote each one separately before joining them with |. Joining first would let a | inside a word split the alternation. This example also uses ~ as the pattern delimiter and passes the same character to preg_quote().
$words = ['a.b', 'c|d', 'e*'];
$parts = array_map(fn(string $word): string => preg_quote($word, '~'), $words);
$pattern = '~^(?:' . implode('|', $parts) . ')$~';
$results = [];
foreach (['a.b', 'axb', 'c|d', 'c'] as $candidate) {
$results[$candidate] = preg_match($pattern, $candidate) === 1;
}
echo json_encode(['pattern' => $pattern, 'results' => $results], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"pattern":"~^(?:a\\.b|c\\|d|e\\*)$~","results":{"a.b":true,"axb":false,"c|d":true,"c":false}}
The JSON output shows each backslash doubled, because JSON escapes backslashes. The real pattern contains single backslashes.
Edge cases
/is not special in regex syntax. If your pattern uses/as its delimiter, you must pass'/'as the second argument.- Whitespace is not on the escaped list. That matters under the
x(extended) modifier, where unescaped spaces in a pattern are ignored. If you use that modifier, handle spaces yourself. - The hyphen is escaped, so quoted text is safe even if you place it inside a character class.
- The function works on bytes, so multibyte text passes through unchanged apart from the listed ASCII characters.
- An empty string returns an empty string.
Common mistakes
- Forgetting the delimiter argument. The default call leaves
/alone, which can end the pattern early. - Quoting the replacement string. The manual notes that
preg_quote()is not meant for the replacement argument ofpreg_replace()and related functions. Replacement strings follow different rules. - Quoting a whole pattern. Quote only the literal fragments. Quoting a pattern that already contains regex syntax turns that syntax into plain text.
- Using it as a general security filter. It only prepares literal text for a regex. It does not sanitize input for HTML, SQL or shell use.
- Joining before quoting. Quote each piece first, then combine them.
Related functions
preg_match()tests a pattern against a subject string.preg_replace()replaces matches; quote the search fragments, not the replacement.preg_split()splits on a pattern, which can be built from quoted text.str_contains()is simpler if you only need a plain substring check with no regex features.escapeshellcmd()is listed by the manual as related, but it escapes shell metacharacters, not regex syntax.
Recap
Use preg_quote() whenever a runtime string should be matched as plain text inside a regex. Pass the pattern delimiter as the second argument, quote each fragment separately, and do not apply it to replacement strings. It returns a string and has no documented failure return.
Tested examples
$keywords = '$40 for a g3/400';
echo preg_quote($keywords, '/'), "\n";
\$40 for a g3\/400
$text = 'Price: 3.5+tax (approx)';
$needle = '3.5+tax';
$pattern = '/' . preg_quote($needle, '/') . '/';
echo json_encode([
'pattern' => $pattern,
'matched' => preg_match($pattern, $text) === 1,
'unquotedMatched' => preg_match('/' . $needle . '/', $text) === 1,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"pattern":"/3\\.5\\+tax/","matched":true,"unquotedMatched":false}
$words = ['a.b', 'c|d', 'e*'];
$parts = array_map(fn(string $word): string => preg_quote($word, '~'), $words);
$pattern = '~^(?:' . implode('|', $parts) . ')$~';
$results = [];
foreach (['a.b', 'axb', 'c|d', 'c'] as $candidate) {
$results[$candidate] = preg_match($pattern, $candidate) === 1;
}
echo json_encode(['pattern' => $pattern, 'results' => $results], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"pattern":"~^(?:a\\.b|c\\|d|e\\*)$~","results":{"a.b":true,"axb":false,"c|d":true,"c":false}}