preg_match()
In PHP, regular expressions are an essential tool for manipulating and searching strings. The preg_match() function is one of the many functions that PHP
Introduction
preg_match() performs a regular expression match against a string. It tells you whether a PCRE pattern occurs in a subject and, optionally, captures what it matched. It stops at the first match — if you need every occurrence, use preg_match_all() instead.
This chapter covers the signature and return values, how the $matches array is populated, the most useful flags, and the gotchas (the 0 vs false trap, anchoring, delimiters) that trip people up.
Syntax
preg_match(
string $pattern,
string $subject,
array &$matches = null,
int $flags = 0,
int $offset = 0
): int|false| Parameter | Description |
|---|---|
$pattern | The pattern, including delimiters and optional modifiers, e.g. '/colou?r/i'. |
$subject | The string to search. |
&$matches | Filled by reference: $matches[0] is the full match, $matches[1], $matches[2]… are the capturing groups. |
$flags | Bit flags such as PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL. |
$offset | Byte offset at which to start searching. |
Return value: 1 if the pattern matched, 0 if it did not, or false on error (an invalid pattern). Because preg_match() returns at most 1, it never tells you how many matches exist — only that there was one.
Basic example
The pattern captures one alphabetic word followed by whitespace and another word. On a match, $matches[0] holds the full match (This is), and $matches[1] / $matches[2] hold the two captured groups (This and is).
The 0 vs false trap
A very common bug is using == to test the result. preg_match() returns 0 for "no match" and false only for an error, and 0 == false is true in PHP. Always compare with the strict operator:
<?php
$result = preg_match('/[0-9]+/', 'abc');
// Wrong: treats "no match" and "error" the same
if ($result == false) {
echo "ambiguous\n";
}
// Right: distinguish the three outcomes
if ($result === false) {
echo "Error in the pattern\n";
} elseif ($result === 0) {
echo "No match\n";
} else {
echo "Matched\n";
}This prints ambiguous and then No match.
Named groups
Add (?<name>...) to your pattern and $matches will contain the captures under both the numeric index and the name, which keeps code readable when the group order changes:
<?php
$date = '2026-06-21';
preg_match('/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/', $date, $m);
echo $m['year'] . "\n"; // 2026
echo $m['month'] . "\n"; // 06
echo $m['day']; // 21Capturing offsets with PREG_OFFSET_CAPTURE
Pass the PREG_OFFSET_CAPTURE flag and each entry in $matches becomes a [matched_text, byte_offset] pair, so you can tell where the match occurred:
<?php
preg_match('/world/', 'hello world', $m, PREG_OFFSET_CAPTURE);
echo $m[0][0] . "\n"; // world
echo $m[0][1]; // 6Case-insensitive and anchored patterns
Modifiers go after the closing delimiter. The i modifier ignores case; ^ and $ anchor the match to the start and end of the string so the whole subject must match the pattern:
<?php
var_dump(preg_match('/^hello$/i', 'HELLO')); // int(1)
var_dump(preg_match('/^hello$/i', 'hello!')); // int(0)When to reach for something else
- Every occurrence, not just the first →
preg_match_all(). - Find and replace →
preg_replace(). - Split a string on a pattern →
preg_split(); for a fixed delimiter, plainexplode()is faster. - Escape user input before embedding it in a pattern →
preg_quote(). - A refresher on PCRE syntax → the PHP regular expressions chapter.
Conclusion
preg_match() is the go-to function for testing whether a string matches a pattern and pulling out captured groups. Remember its three return values, compare with === to avoid the 0/false trap, and switch to preg_match_all() when one match is not enough.