Regular Expressions (Regex)
Learn how to match, extract, and replace text patterns in PHP using the preg_ family of functions and PCRE syntax.
Introduction
Filters like FILTER_VALIDATE_EMAIL are great for common, well-defined patterns, but sooner or later you will need to match or extract text that follows a custom pattern of your own — a product code, a hashtag, a specific date format. That is exactly what regular expressions are for.
PHP uses the PCRE (Perl Compatible Regular Expressions) engine through a family of functions prefixed with preg_. This lesson covers the core functions and enough pattern syntax to start writing useful, real-world regular expressions.
- What a regular expression is and why PHP calls them "preg" functions.
- The building blocks of pattern syntax: character classes, quantifiers, and anchors.
- How to use preg_match(), preg_match_all(), and preg_replace().
- How to validate an email's format and mask phone numbers using regex.
What Is a Regular Expression?
A regular expression (or "regex") is a small, specialized pattern language for describing text. Instead of writing loops and character-by-character comparisons, you describe the shape of what you are looking for, and the regex engine finds it for you.
In PHP, regex patterns are written as strings wrapped in delimiters — most commonly forward slashes, like "/pattern/". Everything between the delimiters is PCRE syntax; anything after the closing delimiter (like "i" for case-insensitive) is a modifier.
<?php
$pattern = "/hello/i"; // "i" modifier makes the match case-insensitive
var_dump(preg_match($pattern, "Hello World")); // 1 (match found)int(1)Basic Pattern Syntax
A handful of symbols cover the vast majority of everyday regex work.
| Symbol | Meaning |
|---|---|
| . | Matches any single character except a newline |
| \d | Matches any digit (0-9) |
| \w | Matches any "word" character: letters, digits, underscore |
| \s | Matches any whitespace character (space, tab, newline) |
| * | Zero or more of the previous item |
| + | One or more of the previous item |
| ? | Zero or one of the previous item (makes it optional) |
| ^ | Anchors the match to the start of the string |
| $ | Anchors the match to the end of the string |
| [...] | A character class — matches any one character inside the brackets |
| (...) | A capturing group — extracts the matched portion separately |
<?php
var_dump(preg_match("/^\d+$/", "12345")); // 1: only digits, start to end
var_dump(preg_match("/^\d+$/", "123a5")); // 0: contains a letter
var_dump(preg_match("/colou?r/", "color")); // 1: "u" is optional
var_dump(preg_match("/colou?r/", "colour")); // 1: matches with "u" tooint(1)
int(0)
int(1)
int(1)preg_match(): Checking and Extracting
preg_match() checks whether a pattern matches a string, and optionally fills a $matches array with the matched text (including any capturing groups). It returns 1 if a match is found, 0 if not, or false on error.
<?php
$text = "Order #48213 shipped today.";
if (preg_match("/#(\d+)/", $text, $matches)) {
echo "Full match: {$matches[0]}\n";
echo "Order number: {$matches[1]}\n";
} else {
echo "No order number found.";
}Full match: #48213
Order number: 48213$matches[0] always holds the entire matched text, while $matches[1], $matches[2], and so on hold whatever each parenthesized group captured, in order.
preg_match_all(): Finding Every Match
preg_match() stops at the first match. When you need every occurrence in a string, use preg_match_all(), which returns the total number of matches and fills $matches with arrays of results.
<?php
$text = "Contact us at sales@example.com or support@example.com.";
$count = preg_match_all("/[\w.]+@[\w.]+/", $text, $matches);
echo "Found $count email(s):\n";
foreach ($matches[0] as $email) {
echo "- $email\n";
}Found 2 email(s):
- sales@example.com
- support@example.compreg_replace(): Find and Replace
preg_replace() finds every match of a pattern and swaps it out for a replacement string. Captured groups can be reused in the replacement using $1, $2, and so on.
<?php
$text = "The event is on 2026-07-26.";
// Rearrange YYYY-MM-DD into DD/MM/YYYY using capturing groups
$formatted = preg_replace("/(\d{4})-(\d{2})-(\d{2})/", "$3/$2/$1", $text);
echo $formatted;The event is on 26/07/2026.Example: Validating Email Format
While filter_var(FILTER_VALIDATE_EMAIL) is usually the better tool for real email validation, this example shows how you would express a simplified email pattern yourself using regex, which is a common interview and learning exercise.
<?php
function looksLikeEmail(string $value): bool {
return (bool) preg_match("/^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$/", $value);
}
var_dump(looksLikeEmail("jane.doe@example.com")); // true
var_dump(looksLikeEmail("not-an-email")); // false
var_dump(looksLikeEmail("missing@tld")); // falsebool(true)
bool(false)
bool(false)Hand-written email regexes are notoriously easy to get subtly wrong. Use this pattern for learning purposes, but reach for FILTER_VALIDATE_EMAIL in real applications.
Example: Masking Phone Numbers
A common privacy task is displaying only part of sensitive data, like showing just the last four digits of a phone number in a log or receipt.
<?php
$log = "Customer called from 555-201-4478 regarding order #48213.";
// Replace all but the last 4 digits of any phone-like pattern with X
$masked = preg_replace_callback(
"/(\d{3})-(\d{3})-(\d{4})/",
function ($m) {
return "XXX-XXX-{$m[3]}";
},
$log
);
echo $masked;Customer called from XXX-XXX-4478 regarding order #48213.preg_replace_callback() works like preg_replace(), but instead of a static replacement string, it calls a function for every match, giving you full control over how each one is transformed.
Common Mistakes
- Forgetting the delimiters (like /pattern/) around a PCRE pattern string.
- Confusing preg_match() (stops at the first match) with preg_match_all() (finds every match).
- Not escaping special regex characters (like . or $) when you mean them literally — use preg_quote() for user-supplied text.
- Writing an overly complex, hard-to-read pattern when a simpler PHP string function like str_contains() or str_starts_with() would do the job.
Best Practices
- Reach for regex when the pattern is genuinely variable; use plain string functions for simple, fixed checks.
- Always check preg_match()'s return value explicitly rather than assuming it succeeded.
- Use preg_quote() when inserting user-supplied text into a dynamically built pattern.
- Add comments above complex patterns explaining what they match — regex is easy to write and hard to re-read later.
- Test tricky patterns against a range of sample inputs, including edge cases, before relying on them.
Frequently Asked Questions
What does PCRE stand for?
Perl Compatible Regular Expressions — the regex engine PHP uses internally for all of its preg_ functions.
What is the difference between preg_match and preg_match_all?
preg_match() stops after finding the first match; preg_match_all() continues through the whole string and reports every match it finds.
Can preg_replace use a function instead of a fixed string?
Yes — use preg_replace_callback(), which calls a function you provide for each match so you can compute the replacement dynamically.
Why do I need delimiters like slashes around a pattern?
Delimiters mark where the pattern starts and ends, and let you attach modifiers (like i for case-insensitive) right after the closing delimiter.
Key Takeaways
- PHP's preg_ functions use the PCRE regex engine and require patterns wrapped in delimiters.
- preg_match() checks for and extracts the first match; preg_match_all() finds every match.
- preg_replace() finds and replaces text, and can reuse captured groups with $1, $2, and so on.
- . \d \w \s * + ? ^ $ are the core building blocks of most everyday patterns.
- preg_replace_callback() lets you compute each replacement dynamically with a function.
Summary
Regular expressions give you precise control over matching, extracting, and transforming text that goes beyond what simple string functions or filters can do. The preg_ family — preg_match(), preg_match_all(), and preg_replace() — covers the vast majority of real-world text-processing needs in PHP.
Next, you will move from raw text patterns to structured data by learning how PHP encodes and decodes JSON, the format used by nearly every modern web API.
- You understand basic PCRE pattern syntax.
- You can check and extract matches with preg_match().
- You can find every match in a string with preg_match_all().
- You can find-and-replace text, including with dynamic callbacks.