PCRE in PHP
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7), because while phpstan/psalm can flag some issues, the core trap (treating preg_match 0 and false the same) typically slips past default static analysis and surfaces only in review or when malformed patterns hit production.
Closest to 'simple parameterised fix' (e3), since the fix is replacing `if (!preg_match(...))` with `=== false` checks and adding flags like /u or preg_quote() calls — small pattern-level changes per call site.
Closest to 'localised tax' (b3), as regex usage tends to be scattered but each usage is self-contained; the choice doesn't shape system architecture but does impose a recurring small tax wherever regex is used across web/cli/queue contexts.
Closest to 'serious trap' (t7), because preg_match's tri-state return (1/0/false) directly contradicts the naming convention of typical boolean-returning match functions in other languages, exactly as the misconception field describes.
Also Known As
TL;DR
Explanation
PHP regex functions: preg_match() returns 1/0/false. preg_match_all() returns count or false. preg_replace() returns string or false. preg_replace_callback() for callback replacement. preg_split() splits by pattern. preg_grep() filters arrays. preg_quote() escapes metacharacters for literal matching. All return false on error — use preg_last_error_msg(). Use $1 not \1 in replacement strings. PCRE caches compiled patterns.
Common Misconception
Why It Matters
Common Mistakes
- if (!preg_match()) treating error same as no-match — use === false
- Not using preg_quote() for user-supplied literal strings
- \1 instead of $1 in preg_replace replacement — use $1
- Building regex from unescaped user input — regex injection
Code Examples
// Silent error — pattern error looks like no-match:
$result = preg_match('/(?P<n>[a-z]+/i', $subject); // Missing )
if (!$result) {
echo 'No match'; // Actually: broken pattern!
}
// User input without escaping — regex injection:
$search = $_GET['q']; // User enters: a+b
preg_match("/{$search}/", $text); // + is a metachar!
// Detect errors explicitly:
$result = preg_match('/^[a-z]+$/', $subject);
if ($result === false) {
throw new RuntimeException('Regex error: ' . preg_last_error_msg());
}
if ($result === 0) { /* no match */ }
// Safe user input as literal:
$escaped = preg_quote($_GET['q'], '/');
$found = preg_match("/{$escaped}/i", $text); // Metacharacters escaped