01 · THE IDEAREAD → TRY → LEARN
What a regular expression does
A regular expression describes the shape of a piece of text instead of spelling out the text itself. Instead of asking "is this string exactly AB-123?", you ask "is this two capital letters, a hyphen and three digits?". In PHP, the usual tool for that question is preg_match().
The call looks like preg_match($pattern, $subject). The pattern is a string with delimiters around it, most often slashes, as in '/^\d+$/'. The subject is the text you are testing. An optional third argument receives the captured pieces.
Anchors make a pattern strict
Without anchors, a pattern matches anywhere inside the subject. The pattern /\d{3}/ finds three digits in abc123xyz, which is rarely what you want when validating input. Two anchors fix this:
^means the match must begin at the start of the subject.$means the match must end at the end of the subject.
Together they say "the whole string must look like this". Inside the pattern, [A-Z] is a character class for one capital ASCII letter, \d is one digit, and {3} repeats the previous item exactly three times.
Three outcomes, not two
The return type of preg_match() is int|false. That gives three outcomes:
1means the pattern matched.0means it ran correctly but did not match.falsemeans something went wrong, for example the pattern could not be compiled.
Because 0 and false are both falsy, a plain if (preg_match(...)) treats a broken pattern like an ordinary non-match. When the difference matters, compare with ===.
Worked example: product codes
The first example checks codes made of two capital letters, a hyphen and three digits. Only the first subject fits the whole pattern. The second has an extra digit, so the $ anchor rejects it. The third uses lowercase letters, and the pattern is case-sensitive.
$pattern = '/^[A-Z]{2}-\d{3}$/';
echo json_encode([
preg_match($pattern, 'AB-123'),
preg_match($pattern, 'AB-1234'),
preg_match($pattern, 'ab-123'),
]);[1,0,0]
A subtle point: by default $ also matches just before a newline at the very end of the subject. So /^\d+$/ accepts "42\n". Adding the D modifier after the closing delimiter, as in /^\d+$/D, makes $ mean the true end only.
Capturing the parts
Parentheses create capture groups, and a group can have a name with (?<name>...). When you pass a variable as the third argument, PHP fills it with the matched text. Index 0 holds the whole match, and each group is stored under both its number and, if named, its name. The array is only meaningful after a successful match.
$result = preg_match('/^(?<prefix>[A-Z]{2})-(?<number>\d{3})$/', 'AB-123', $matches);
echo json_encode([$result, $matches['prefix'], $matches['number']]);[1,"AB","123"]
When the pattern itself is wrong
If a pattern does not compile, PHP emits an E_WARNING and preg_match() returns false. The function also returns false if internal limits such as the backtrack limit are hit on a long subject; preg_last_error() then reports the reason. In the next example the third pattern has an unclosed parenthesis. The @ operator hides the warning only so the demonstration output stays clean; in real code it is better to fix the pattern than to silence it.
$results = [
@preg_match('/^\d+$/', '2024'),
@preg_match('/^\d+$/', 'year'),
@preg_match('/^(\d+$/', '2024'),
];
echo json_encode($results);
echo "\n";
echo $results[2] === false ? "pattern error" : "ok";[1,0,false] pattern error
Common mistakes
- Forgetting the anchors and accidentally accepting strings that merely contain a valid piece.
- Testing the result with a loose check and so confusing
falsewith0. - Using
preg_match()to see whether one fixed string contains another;str_contains()is simpler and faster for that. - Reading
$matchesafter a failed match and assuming it holds useful data. - Forgetting that offsets and lengths are measured in bytes, not characters.
Exercise
Write matchStatus(string $pattern, string $subject): string. It must return "match" when preg_match() returns 1, "no match" when it returns 0, and "error" when it returns false. The tests include an empty subject, an invalid pattern and a trailing newline checked with the D modifier.
Recap
Use anchors to validate a whole string, remember that preg_match() has three outcomes, and compare with === whenever you must separate a clean non-match from a failure.
Strings · Functions
- Match a clear anchored pattern
- Distinguish a match, no match and a pattern error