W3docs

PHP Regular Expressions

PHP is a powerful server-side scripting language that allows developers to create dynamic and interactive web pages. One of its most useful features is its

A regular expression (regex) is a pattern that describes a set of strings. Instead of looking for one fixed piece of text, you describe its shape — "an email address", "a sequence of digits", "a word ending in .php" — and PHP finds, validates, replaces, or splits text that matches.

This chapter covers how PHP's preg_ functions work, the syntax of a pattern (delimiters, character classes, quantifiers, groups, anchors), and the practical tasks you'll reach for most often: validating input, extracting data, and replacing text.

What are regular expressions

A regular expression is a sequence of characters that defines a search pattern. Most characters match themselves — the pattern cat matches the literal text cat. The power comes from metacharacters that stand for categories of characters or repetition:

TokenMeaningExample match
.Any single character (except newline)c.tcat, cut
\dA digit 0-9\d\d42
\wA "word" character (a-z, A-Z, 0-9, _)\w+hello_1
\sWhitespace (space, tab, newline)
[abc]Any one of a, b, c[aeiou]e
[^abc]Any character except a, b, c
a*Zero or more a"", aaa
a+One or more aa, aaa
a?Zero or one a (optional)"", a
a{2,4}Between 2 and 4 of aaa, aaaa
^ / $Start / end of the string
|Alternation ("or")cat|dogcat or dog
()Grouping / capture(ab)+abab

How regular expressions work in PHP

PHP uses PCRE (Perl-Compatible Regular Expressions) through the preg_ family of functions. The ones you'll use most:

FunctionWhat it does
preg_match()Tests whether a pattern matches; captures the first match
preg_match_all()Finds all matches in a string
preg_replace()Replaces every match with new text
preg_split()Splits a string on a pattern
preg_quote()Escapes regex metacharacters in a literal string

The syntax of a PHP pattern

A PHP pattern is a string built from three parts: a delimiter, the pattern, and optional modifiers.

/pattern/modifiers

The first character is the delimiter — almost always /, but any non-alphanumeric character works (#, ~, !). Choosing a delimiter that doesn't appear in your pattern saves you from escaping. For example, matching a URL path is cleaner with #:

"#^/users/\d+$#"   // no need to escape the slashes

modifiers are optional letters after the closing delimiter that change how the engine behaves:

  • i — case-insensitive: /php/i matches PHP, Php, php.
  • mmultiline: ^ and $ match at the start/end of each line, not just the whole string.
  • ssingle line ("dotall"): . also matches newlines.
  • u — treat the pattern and subject as UTF-8. Use this for any text with non-ASCII characters.
  • x — extended: whitespace in the pattern is ignored, so you can space out and comment complex patterns.

Testing for a match with preg_match

preg_match() returns 1 if the pattern is found, 0 if not, and false on error. It's the right choice for a yes/no question like "does this string contain a digit?"

php— editable, runs on the server

The optional third argument, $matches, is filled with the results: $matches[0] is the whole match, and $matches[1], $matches[2], … are the capturing groups in order — the substrings captured by each pair of parentheses.

Validating user input

A common use of regex is validating user input — checking a value has the right shape before you store or trust it. Here is a basic email check:

<?php

$email = "[email protected]";

if (preg_match("/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/", $email)) {
    echo "Valid email address";
} else {
    echo "Invalid email address";
}

?>

The anchors ^ and $ are important here: without them the pattern would match an email anywhere inside a larger string, so "junk [email protected] junk" would pass. With them, the entire string must be a single email.

For email specifically, PHP's built-in filter_var() with FILTER_VALIDATE_EMAIL is more robust than a hand-written regex. Reach for regex when you need a format the filters don't cover — postal codes, custom IDs, phone formats. See PHP form validation for a full input-checking example.

Finding every match with preg_match_all

Where preg_match() stops at the first hit, preg_match_all() collects them all — useful for extracting every hashtag, price, or link from a block of text.

<?php

$text = "Prices: $12, $7 and $349";

preg_match_all("/\\\$(\d+)/", $text, $matches);

print_r($matches[1]); // the captured numbers

?>

$matches[1] holds an array of every captured group — here, ["12", "7", "349"].

Replacing text with preg_replace

preg_replace() substitutes every match. In the replacement string, $1, $2, … refer back to captured groups, so you can rewrite as well as remove:

<?php

$date = "2026-06-21";

// Reformat YYYY-MM-DD into DD/MM/YYYY
echo preg_replace("/(\d{4})-(\d{2})-(\d{2})/", "$3/$2/$1", $date);
// 21/06/2026

?>

To break a string into pieces on a pattern instead of replacing, use preg_split() — for example splitting on any run of whitespace with /\s+/.

Advanced techniques

Once you're comfortable with the basics, these features handle harder patterns:

  • Capturing groups (...) — extract specific parts of a match, as shown above with $matches[1].
  • Non-capturing groups (?:...) — group without filling a $matches slot, when you only need the grouping for a quantifier or alternation.
  • Named groups (?<year>\d{4}) — reference a match by name ($matches['year']) instead of by number.
  • Lookahead (?=...) and lookbehind (?<=...) — match based on what comes after or before, without consuming it. Useful for "a number followed by px" without capturing px.
  • Lazy quantifiers *?, +? — match as few characters as possible. By default .* is greedy and grabs everything it can.

A note on escaping: characters like ., +, *, ?, (, ), [, \ have special meaning. To match them literally, prefix with a backslash (\. matches a literal dot). When the literal text comes from a variable, use preg_quote() to escape it safely.

Common gotchas

  • Forgetting the delimiters. PHP patterns are strings and must include delimiters: "/\d+/", not "\d+". Omitting them triggers a warning and the match fails.
  • Double-escaping in double-quoted strings. Inside "...", a backslash is also a PHP escape. "/\d/" works because \d isn't a PHP escape, but to match a literal $ you need "/\\$/". Single-quoted strings ('/\d/') avoid this surprise.
  • Greedy matching grabs too much. <.*> on <a><b> matches the whole <a><b>, not just <a>. Use a lazy <.*?> or a negated class <[^>]*>.
  • Skipping the u modifier on UTF-8. Without /u, . and \w operate on bytes, mangling multi-byte characters.

Conclusion

Regular expressions let you describe the shape of text rather than its exact content, which makes them indispensable for validating input, extracting data, and transforming strings. In PHP you reach for them through the preg_ functions: preg_match() and preg_match_all() to search, preg_replace() to rewrite, and preg_split() to break strings apart. Start with anchors, character classes, and quantifiers, then layer in groups and lookarounds as your patterns grow. To go deeper on the strings you'll be matching against, see PHP Strings.

Practice

Practice
What is the main purpose of PHP regex?
What is the main purpose of PHP regex?
Was this page helpful?