PHP RegEx Functions

Ma Mariya Vinciya Updated 21 Oct 2026
7 min read ·Lesson 163 of 174
In this post

PHP's regular expression support is provided by the PCRE extension, which is always enabled. This lesson is a complete reference to its functions, flags, error codes, and settings, with examples for t

PHP's regular expression support is provided by the PCRE extension, which is always enabled. This lesson is a complete reference to its functions, flags, error codes, and settings, with examples for the functions not covered in the previous lessons (preg_grep(), preg_filter(), and preg_quote()), plus a reusable approach to error handling.


Function Reference

FunctionPurposeReturns
preg_match($pattern, $subject, &$matches, $flags, $offset)First match1, 0, or false
preg_match_all($pattern, $subject, &$matches, $flags, $offset)All matchesNumber of matches, or false
preg_replace($pattern, $replacement, $subject, $limit, &$count)Replace matchesString/array, or null on error
preg_replace_callback($pattern, $callback, $subject, $limit, &$count, $flags)Replace using a callbackString/array, or null
preg_replace_callback_array($patternsToCallbacks, $subject, $limit, &$count, $flags)Several patterns, each with its callbackString/array, or null
preg_filter($pattern, $replacement, $subject, $limit, &$count)Like preg_replace(), but returns only subjects that matchedString/array, or null
preg_split($pattern, $subject, $limit, $flags)Split by a patternArray, or false
preg_grep($pattern, $array, $flags)Array elements that matchArray, or false
preg_quote($string, $delimiter)Escape regex special charactersEscaped string
preg_last_error()Error code of the last PCRE callInteger constant
preg_last_error_msg()Error message of the last PCRE call (PHP 8.0+)String

 

The core functions are explained in preg_match, preg_replace, and preg_split.


preg_grep() — Filter an Array by Pattern

<?php
$values = ['10', 'abc', '25', '3.5', '007'];

print_r(preg_grep('/^\d+$/', $values));
print_r(preg_grep('/^\d+$/', $values, PREG_GREP_INVERT));

 

Output

Array
(
    [0] => 10
    [2] => 25
    [4] => 007
)
Array
(
    [1] => abc
    [3] => 3.5
)

 

  • preg_grep() returns the elements that match, preserving their keys.
  • PREG_GREP_INVERT returns the elements that do not match.

It is a shortcut for array_filter($values, fn($v) => preg_match($pattern, $v) === 1).


preg_filter() — Replace Only Where Something Matched

<?php
$items = ['item1', 'misc', 'item22'];

print_r(preg_replace('/(\d+)/', '#$1', $items));
print_r(preg_filter('/(\d+)/', '#$1', $items));

 

Output

Array
(
    [0] => item#1
    [1] => misc
    [2] => item#22
)
Array
(
    [0] => item#1
    [2] => item#22
)

 

preg_replace() returns every subject, changed or not. preg_filter() returns only the subjects in which at least one replacement happened — combining filtering and replacement in one step. For a single string subject, it returns null when nothing matched.


preg_quote() — Escape Text for Use in a Pattern

<?php
$userSearch = 'C++ (v2.0) #1';

$pattern = '/' . preg_quote($userSearch, '/') . '/i';

echo $pattern . "\n";
var_dump(preg_match($pattern, 'Learn c++ (V2.0) #1 today'));

 

Output

/C\+\+ \(v2\.0\) \#1/i
int(1)

 

preg_quote() adds a backslash before every character with special meaning in a pattern:

.  \  +  *  ?  [  ^  ]  $  (  )  {  }  =  !  <  >  |  :  -  #

 

plus the NUL character and, if given, the delimiter (the second argument). The # character has been escaped since PHP 7.3, which makes quoted text safe with # delimiters and in extended (x) mode. Always quote user input and other dynamic text before inserting it into a pattern.


Flags Reference

FlagUsed WithEffect
PREG_PATTERN_ORDERpreg_match_all()Results grouped by group number (default)
PREG_SET_ORDERpreg_match_all()Results grouped by match
PREG_OFFSET_CAPTUREpreg_match(), preg_match_all(), callbacksAdds the byte offset to each match
PREG_UNMATCHED_AS_NULLpreg_match(), preg_match_all(), callbacksUnmatched groups become null
PREG_SPLIT_NO_EMPTYpreg_split()Removes empty pieces
PREG_SPLIT_DELIM_CAPTUREpreg_split()Includes captured separators
PREG_SPLIT_OFFSET_CAPTUREpreg_split()Adds offsets to pieces
PREG_GREP_INVERTpreg_grep()Returns non-matching elements

 

Flags can be combined with |, for example PREG_SET_ORDER | PREG_UNMATCHED_AS_NULL.


Errors

How Errors Are Reported

PCRE functions do not throw exceptions. Instead:

ProblemWhat Happens
Invalid pattern (syntax error, missing delimiter)A warning is emitted, and the function returns false (or null for replace functions)
Runtime error (invalid UTF-8 with u, limits exceeded)No warning; the function returns false/null, and the error is available from preg_last_error()

 

Error Codes

ConstantMeaning
PREG_NO_ERRORNo error
PREG_INTERNAL_ERRORInternal error (including pattern compilation failures)
PREG_BACKTRACK_LIMIT_ERRORpcre.backtrack_limit exceeded
PREG_RECURSION_LIMIT_ERRORpcre.recursion_limit exceeded
PREG_BAD_UTF8_ERRORThe subject contains invalid UTF-8 (with u)
PREG_BAD_UTF8_OFFSET_ERRORThe offset points into the middle of a UTF-8 character
PREG_JIT_STACKLIMIT_ERRORThe JIT compiler ran out of stack space

 

A Wrapper That Throws Exceptions

Because silent false results are easy to miss, applications often wrap PCRE calls:

<?php
function regexMatch(string $pattern, string $subject, ?array &$matches = null): bool
{
    $result = preg_match($pattern, $subject, $matches);

    if ($result === false) {
        throw new RuntimeException('Regex error: ' . preg_last_error_msg());
    }

    return $result === 1;
}

var_dump(regexMatch('/^\d+$/', '12345'));

try {
    regexMatch('/\w+/u', "abc\xFF");
} catch (RuntimeException $e) {
    echo $e->getMessage() . "\n";
}

 

Output

bool(true)
Regex error: Malformed UTF-8 characters, possibly incorrectly encoded

 

The wrapper returns a clean boolean for "matched / not matched" and turns failures into exceptions, so errors cannot be mistaken for "no match". Libraries such as composer/pcre provide complete, well-tested wrappers of this kind for all PCRE functions.


PCRE Configuration

php.ini SettingDefaultPurpose
pcre.backtrack_limit1000000Maximum backtracking steps per call
pcre.recursion_limit100000Maximum recursion depth
pcre.jit1Enables the just-in-time compiler for faster matching

 

These limits protect the server from patterns that would otherwise run for a very long time. Raising them hides the problem; rewriting an inefficient pattern solves it.

PHP also caches compiled patterns, so using the same pattern repeatedly in a loop is efficient. Building a different pattern for every iteration (for example, from user input) cannot benefit from the cache as much.


Choosing the Right Function

TaskFunction
Validate a formatpreg_match()
Extract parts of one matchpreg_match() with groups
Extract all occurrencespreg_match_all()
Replace with a fixed patternpreg_replace()
Replace with calculated textpreg_replace_callback()
Replace and keep only changed itemspreg_filter()
Split by varying separatorspreg_split()
Filter an array by patternpreg_grep()
Insert dynamic text into a patternpreg_quote()

 


Practical Example: A Reusable Format Validator

<?php
const FORMATS = [
    'pincode' => '/^[1-9]\d{5}\z/',
    'mobile' => '/^[6-9]\d{9}\z/',
    'ifsc' => '/^[A-Z]{4}0[A-Z0-9]{6}\z/',
    'vehicle' => '/^[A-Z]{2}\d{2}[A-Z]{1,2}\d{4}\z/',
];

function validateFormats(array $input): array
{
    $results = [];

    foreach ($input as $field => $value) {
        if (!array_key_exists($field, FORMATS)) {
            $results[$field] = 'unknown field';
            continue;
        }

        $normalized = strtoupper(preg_replace('/\s+/', '', (string) $value) ?? '');
        $results[$field] = preg_match(FORMATS[$field], $normalized) === 1 ? 'valid' : 'invalid';
    }

    return $results;
}

$input = [
    'pincode' => '600 042',
    'mobile' => '98765',
    'ifsc' => 'sbin0001234',
    'vehicle' => 'TN 09 AB 1234',
    'aadhaar' => '1234 5678 9012',
];

foreach (validateFormats($input) as $field => $result) {
    echo str_pad($field, 8) . " $result\n";
}

 

Output

pincode  valid
mobile   invalid
ifsc     valid
vehicle  valid
aadhaar  unknown field

 

Step-by-Step Explanation

  1. All patterns live in one constant array, so formats are defined in a single place and reused across forms.
  2. array_key_exists() checks whether a format is defined. (isset() cannot be used on a constant expression such as FORMATS[$field]; that is a compile-time error.)
  3. Input is normalized before validation: whitespace is removed with preg_replace() and letters are uppercased. Users can type 600 042 or tn 09 ab 1234 and still pass.
  4. \z anchors ensure nothing follows the valid part — not even a newline.
  5. Fields without a known format are reported instead of silently accepted.

An IFSC code (Indian bank branch code) has four letters for the bank, a 0, and six characters for the branch. As with all format checks, a valid format does not prove that the code actually exists.


Common Mistakes

Treating false/null as "No Match"

Effect: Pattern and encoding errors go unnoticed.

Fix: Check === false / === null, or use a throwing wrapper.

Not Escaping Dynamic Text

Effect: User input breaks the pattern or changes its meaning.

Fix: preg_quote($text, $delimiter).

Raising PCRE Limits to Fix Slow Patterns

Effect: The server spends more time on inefficient matches, which can be exploited for denial of service.

Fix: Rewrite the pattern (avoid nested quantifiers; use specific classes).

Re-implementing preg_grep() and preg_filter()

Effect: More code than necessary.

Fix: Use the built-in functions.


Best Practices

  • Pick the most specific function for each task.
  • Check for errors and convert them to exceptions in application code.
  • Escape dynamic text with preg_quote().
  • Keep reusable patterns in one place, and normalize input before validating it.
  • Keep PCRE limits at their defaults and optimize patterns instead.

Official reference: https\://www.php.net/manual/en/ref.pcre.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer
Ask about this post
AI Ask about this post

Ask questions about PHP RegEx Functions and get answers drawn from it.

Signed-in readers only.