Regular Expressions (PCRE)
A regular expression is a compact pattern language for describing the shape of text — "a sequence of digits," "something that looks like an email address," "a word starting with a capital letter" — rather than an exact string to match literally. PHP's regex engine is PCRE (Perl-Compatible Regular Expressions), a mature implementation that follows the same conventions as Perl's regex syntax, which is also close to what JavaScript, Python, and most other mainstream languages use.
Delimiters: patterns need a wrapper
Unlike some languages that treat regex as its own literal syntax, PHP represents a regular expression as a plain string, and that string needs delimiters so PHP knows where the pattern ends and where optional flags begin. The forward slash is the conventional choice, but any non-alphanumeric character works — #, ~, and % are common alternatives, especially useful when the pattern itself contains a lot of literal slashes.
Equivalent patterns with different delimiters
<?php $pattern1 = '/hello/'; $pattern2 = '#hello#'; $pattern3 = '~hello~'; // Useful when matching against paths, which are full of slashes $pathPattern = '#^/api/v1/users/\d+$#';
Flags: modifying how the pattern is applied
After the closing delimiter, a pattern can carry one or more single -letter flags that change matching behavior without changing the pattern text itself.
Flag | Effect |
|---|---|
i | Case-insensitive matching |
m |
|
s |
|
u | Treats the pattern and subject as UTF-8, enabling proper multibyte matching |
x | Ignores whitespace and |
Using the i flag for case-insensitive matching
<?php
var_dump(preg_match('/hello/', 'Hello, world')); // 0 — case matters
var_dump(preg_match('/hello/i', 'Hello, world')); // 1 — case ignoredint(0) int(1)
The building blocks: anchors, classes, and quantifiers
Most patterns are built from three kinds of ingredients. Anchors pin a match to a position rather than matching characters themselves: ^ means "start of the string" and $ means "end of the string." Character classes, written in square brackets, match any one character from a set — [a-z] matches any lowercase letter, [0-9] matches any digit, and [^0-9] (a caret right after the bracket) means "anything except a digit." PHP also provides shorthand classes: \\d for a digit, \\w for a "word" character (letters, digits, underscore), and \\s for whitespace, each with an uppercase inverse (\\D, \\W, \\S).
Quantifiers control how many times the preceding element may
repeat: * means zero or more, + means one or more, ? means
zero or one (making the preceding element optional), and {n,m}
means "between n and m times."
Anchors, classes, and quantifiers together
<?php
// Matches strings that are entirely 3 to 5 digits, nothing else
var_dump(preg_match('/^\d{3,5}$/', '4821')); // 1
var_dump(preg_match('/^\d{3,5}$/', '48')); // 0 (too short)
var_dump(preg_match('/^\d{3,5}$/', '482199')); // 0 (too long)int(1) int(0) int(0)
Worked example: an email-shape check
Real, fully correct email validation according to the RFC spec is notoriously complex, but a practical "does this look roughly like an email address" pattern is a good illustration of combining classes and quantifiers.
A practical (not RFC-complete) email shape check
<?php
$pattern = '/^[\w.+-]+@[a-zA-Z\d.-]+\.[a-zA-Z]{2,}$/';
var_dump(preg_match($pattern, 'firoz@example.com')); // 1
var_dump(preg_match($pattern, 'not-an-email')); // 0
var_dump(preg_match($pattern, 'missing@domain')); // 0 (no dot)int(1) int(0) int(0)
[\w.+-]+ matches the local part before the @ (letters, digits,
underscore, dot, plus, hyphen), @ matches itself literally,
[a-zA-Z\d.-]+ matches the domain, and \.[a-zA-Z]{2,} requires a
literal dot followed by at least two letters for the top-level
domain.
Worked example: extracting digits
Combining \\d with + is enough to pull consecutive runs of digits out of a larger string, which is useful for stripping formatting characters from things like phone numbers.
Pulling every digit out of formatted text
<?php
$phone = '(555) 123-4567';
$digitsOnly = preg_replace('/\D+/', '', $phone);
echo $digitsOnly, "\n";5551234567
\\D+ matches one or more characters that are not digits, and replacing every such run with an empty string leaves only the digits behind — an approach that's covered in more depth, alongside preg_match and its capture groups, on the next page.
A PHP regex is a string with matching delimiters on both ends —
/pattern/,#pattern#, or any other non-alphanumeric character.Flags follow the closing delimiter:
ifor case-insensitive,mfor multiline anchors,sfor dot-matches-newline,ufor UTF-8 mode.^and$anchor to the start and end of the string (or line, with themflag).Character classes
[...]match one character from a set;\d,\w,\sare shorthand classes with uppercase inverses.Quantifiers
*,+,?, and{n,m}control how many times the preceding element repeats.