Lesson 28 of 100
Regular expressions12 min5 checksPHP 8.5

Matching a Simple Pattern with preg_match()

Learn to test a string against an anchored regular expression with preg_match() and tell a match, a non-match and a pattern error apart.

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:

  • 1 means the pattern matched.
  • 0 means it ran correctly but did not match.
  • false means 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.

Example
<?php
$pattern = '/^[A-Z]{2}-\d{3}$/';
echo json_encode([
    preg_match($pattern, 'AB-123'),
    preg_match($pattern, 'AB-1234'),
    preg_match($pattern, 'ab-123'),
]);
Output
[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.

Example
<?php
$result = preg_match('/^(?<prefix>[A-Z]{2})-(?<number>\d{3})$/', 'AB-123', $matches);
echo json_encode([$result, $matches['prefix'], $matches['number']]);
Output
[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.

Example
<?php
$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";
Output
[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 false with 0.
  • Using preg_match() to see whether one fixed string contains another; str_contains() is simpler and faster for that.
  • Reading $matches after 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.

Before you start

Strings · Functions

You’ll practice
  • Match a clear anchored pattern
  • Distinguish a match, no match and a pattern error
Your task

Make matchStatus() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.