← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

preg_match() / preg_replace()

PHP PHP 5.0+ Intermediate
debt(d7/e3/b3/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7) — phpstan/psalm can flag some loose return checks but the false-vs-0 distinction and ReDoS risk typically require code review; semgrep rules can catch patterns but aren't default.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3) — quick_fix is to replace if (preg_match(...)) with === 1 checks and add backtrack_limit; repeated pattern fix across call sites but each is small.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3) — regex usage tends to cluster in validation/parsing code; doesn't shape the whole system but each call site carries the return-value gotcha.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7) — misconception is explicit: three return values (1/0/false) where the falsy check conflates no-match with error, contradicting most other languages' regex APIs that throw on error.

About DEBT scoring →

Also Known As

preg_match() PHP regex PCRE PHP

TL;DR

PHP's PCRE regex functions — powerful but prone to ReDoS if patterns are not carefully constructed.

Explanation

preg_match() and preg_replace() are PHP's primary regular expression functions using the PCRE library. Common pitfalls: catastrophic backtracking (ReDoS) occurs when a pattern with nested quantifiers matches a long string that ultimately fails — the engine retries exponentially many paths. Always set a backtrack limit and use pcre.backtrack_limit in php.ini. For untrusted patterns, validate the regex itself before executing it. preg_replace() with the /e modifier (deprecated PHP 5.5, removed 7.0) executed replacement strings as PHP code — a critical vulnerability.

Common Misconception

preg_match() returns false only when the pattern does not match. preg_match() returns 1 on match, 0 on no match, and false on regex error — these must all be handled separately. Always use === 1 for a positive match check rather than if (preg_match(...)).

Why It Matters

preg_match() is PHP's primary regex function — misuse causes ReDoS vulnerabilities, false positives, and subtle bugs from incorrect return value checking.

Common Mistakes

  • Checking if (preg_match(...)) where a pattern error returns false — same check for match and error.
  • Not limiting input length before matching complex patterns — ReDoS on unbounded input.
  • Using preg_match when str_contains or str_starts_with is sufficient — regex has overhead.
  • Not using the PREG_OFFSET_CAPTURE flag when needing match position — re-running the regex to find position.

Code Examples

✗ Vulnerable
// Return value not checked properly:
if (preg_match('/^[a-z]+$/i', $input)) {
    // preg_match returns 1 (match), 0 (no match), or FALSE (error)
    // if() treats 0 and false the same — pattern errors silently fail
}

// Correct:
$result = preg_match('/^[a-z]+$/i', $input);
if ($result === false) throw new RuntimeException('Regex error: ' . preg_last_error_msg());
if ($result === 1) { /* matched */ }
✓ Fixed
// Basic match — returns 1 (match), 0 (no match), false (error)
if (preg_match('/^[a-z0-9_-]{3,20}$/i', $username)) {
    // valid username
}

// Capture groups
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $date, $m)) {
    [$full, $year, $month, $day] = $m;
}

// Named captures
preg_match('/^(?P<year>\d{4})-(?P<month>\d{2})$/', $input, $m);
$year = $m['year'];

// All matches
preg_match_all('/<a href="([^"]+)"/', $html, $matches);
$urls = $matches[1];

// Replace
$slug = preg_replace('/[^a-z0-9]+/', '-', strtolower($title));

// Check for regex errors
if (preg_last_error() !== PREG_NO_ERROR) { /* handle */ }

Added 15 Mar 2026
Edited 22 Mar 2026
Views 110
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 2 pings M 0 pings T 1 ping W 0 pings T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 0 pings S 1 ping M
Perplexity 1
No pings yesterday
PetalBot 12 Scrapy 11 Ahrefs 7 ChatGPT 7 SEMrush 7 Perplexity 6 Google 4 Unknown AI 2 Applebot 2 Bing 2 Meta AI 1 Twitter/X 1 Brave Search 1
crawler 59 crawler_json 4
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
PHP php A server-side scripting language that generates web pages and APIs — the code runs on the server, and only its output (usually HTML or JSON) reaches the browser.

PHP is often the first server-side language people meet, and understanding its execution model — script starts fresh on every request, no memory between requests — explains most of how the web backend works: sessions, databases, and caching all exist to bridge that per-request amnesia.

💡 Start with PHP 8.x, declare(strict_types=1), and PDO — skip any tutorial that mentions mysql_query().

Ask Codex about PHP →
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Low
⚡ Quick Fix
Always check preg_match() return value — it returns false on PCRE error (not just 0 for no match); use preg_last_error() to diagnose failures and set pcre.backtrack_limit to catch ReDoS
📦 Applies To
PHP 5.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
preg_match result used without checking for false; no preg_last_error() after failed match; regex applied to user input without backtrack limit awareness
Auto-detectable: ✓ Yes phpstan psalm semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Low Context: Line


✓ schema.org compliant