preg_replace

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

What Is preg_replace() ? In simple terms: preg_replace() finds text that matches a regular expression and replaces it with something else. Unlike str_replace() , which replaces fixed text, it can repl

What Is preg_replace()?

In simple terms: preg_replace() finds text that matches a regular expression and replaces it with something else. Unlike str_replace(), which replaces fixed text, it can replace patterns — any number, any run of spaces, any date in a certain format — and it can reuse parts of the match in the replacement.

Technically: preg_replace() searches the subject for all matches of the pattern and replaces each with the replacement string, in which $n or ${n} refer to captured groups. It returns the new string (or array), or null if an error occurs. The original string is not modified.


Syntax

preg_replace(
    string|array $pattern,
    string|array $replacement,
    string|array $subject,
    int $limit = -1,
    int &$count = null
): string|array|null

 

ParameterMeaning
$patternPattern(s) to search for
$replacementReplacement text, with $1, ${1}, … for groups
$subjectString or array of strings to process
$limitMaximum replacements per pattern and subject (-1 \= unlimited)
$countFilled with the number of replacements made

 


Basic Replacement

Example

<?php
$text = "Too    many   spaces \t and\ttabs";

echo preg_replace('/\s+/', ' ', $text) . "\n";
echo preg_replace('/\d/', '#', 'Card 4111 1111') . "\n";

 

Output

Too many spaces and tabs
Card #### ####

 

  • \s+ matches any run of whitespace characters; each run becomes a single space.
  • \d matches a single digit; every digit becomes #.

Using Captured Groups in the Replacement

Groups captured in the pattern can be inserted into the replacement with $1, $2, … ($0 is the whole match).

Reformatting a Date

<?php
echo preg_replace('/(\d{4})-(\d{2})-(\d{2})/', '$3/$2/$1', 'Due on 2026-10-15') . "\n";

 

Output

Due on 15/10/2026

 

The pattern captures year, month, and day; the replacement writes them back in day/month/year order.

Formatting a Phone Number

<?php
echo preg_replace('/^(\d{5})(\d{5})$/', '$1 $2', '9876543210') . "\n";

 

Output

98765 43210

 

${1} When a Digit Follows

<?php
echo preg_replace('/(\d+)/', '${1}0', 'Price 25') . "\n";

 

Output

Price 250

 

Writing $10 would refer to group 10, which does not exist. Braces separate the group number from the digit that follows: ${1}0 means "group 1, then the character 0".

Note: Always write replacement strings in single quotes. In double quotes, PHP processes some of this syntax itself before preg_replace() sees it: "${1}" is treated as variable interpolation, and "\1" becomes an invisible control character.


Limiting and Counting Replacements

<?php
$result = preg_replace('/a/', 'A', 'banana', 2, $count);

echo "$result ($count replacements)\n";

 

Output

bAnAna (2 replacements)

 

Only the first two matches are replaced. $count reports how many replacements happened, which is useful for detecting whether anything changed.


Several Patterns at Once

When $pattern and $replacement are arrays, each pattern is applied in order to the result of the previous one:

<?php
$title = 'Hello World! PHP 8';

$slug = preg_replace(
    ['/\s+/', '/[^a-z0-9-]/'],
    ['-', ''],
    strtolower($title)
);

echo $slug . "\n";

 

Output

hello-world-php-8

 

  1. Spaces become hyphens: hello-world!-php-8.
  2. Every character that is not a lowercase letter, digit, or hyphen is removed: hello-world-php-8.

The order matters: running the second pattern first would remove the spaces before they could become hyphens.


Dynamic Replacements: preg_replace_callback()

When the replacement must be calculated, use preg_replace_callback(). The callback receives the matches array and returns the replacement text:

<?php
$text = 'Pen ₹20, Notebook ₹60, Bag ₹1299';

$withGst = preg_replace_callback(
    '/₹(\d+)/u',
    fn(array $m): string => '₹' . number_format($m[1] * 1.18, 2),
    $text
);

echo $withGst . "\n";

 

Output

Pen ₹23.60, Notebook ₹70.80, Bag ₹1,532.82

 

For each price, the callback calculates the amount including 18% GST and formats it. A plain replacement string could not perform arithmetic.

Several Patterns, Several Callbacks: preg_replace_callback_array()

<?php
$message = 'Hi @asha, see #php8 :)';

$html = preg_replace_callback_array([
    '/@(\w+)/' => fn(array $m): string => '<a href="/u/' . $m[1] . '">@' . $m[1] . '</a>',
    '/#(\w+)/' => fn(array $m): string => '<a href="/tag/' . $m[1] . '">#' . $m[1] . '</a>',
    '/:\)/' => fn(array $m): string => '🙂',
], htmlspecialchars($message));

echo $html . "\n";

 

Output

Hi <a href="/u/asha">@asha</a>, see <a href="/tag/php8">#php8</a> 🙂

 

  • Each pattern has its own callback; they are applied in the order listed.
  • The message is escaped with htmlspecialchars() before links are added, so any HTML typed by the user is neutralized while the generated links stay intact. The captured names contain only word characters (\w), so they are safe to insert.

Masking Sensitive Data

Lookaheads let you replace characters based on what follows them:

<?php
echo preg_replace('/\d(?=\d{4})/', '*', '9876543210') . "\n";
echo preg_replace('/(?<=.).(?=[^@]*@)/', '*', 'asha.k@example.com') . "\n";

 

Output

******3210
a*****@example.com

 

  • \d(?=\d{4}) matches a digit only if at least four more digits follow, so every digit except the last four is replaced.
  • The second pattern replaces every character of the email's local part except the first: (?<=.) requires a character before it, and (?=[^@]*@) requires that an @ still follows.

Error Handling

preg_replace() returns null if an error occurs:

<?php
$result = preg_replace('/\w+/u', 'x', "abc\xFF");

var_dump($result);
echo preg_last_error_msg() . "\n";

 

Output

NULL
Malformed UTF-8 characters, possibly incorrectly encoded

 

Passing null on to string functions later causes deprecation notices or type errors. Check the result when the input may be invalid:

$clean = preg_replace($pattern, $replacement, $input)
    ?? throw new RuntimeException('Regex failed: ' . preg_last_error_msg());

 

Replacement Strings From Users

In a replacement string, $1 and \1 are interpreted as group references. If the replacement comes from user input, use preg_replace_callback() and return the user's text from the callback — the callback's return value is inserted literally, with no special characters.


Practical Example: Creating URL Slugs

<?php
function slugify(string $title): string
{
    $slug = mb_strtolower(trim($title));
    $slug = preg_replace("/['’]/u", '', $slug);
    $slug = preg_replace('/[^\p{L}\p{M}\p{N}]+/u', '-', $slug);

    return trim($slug, '-');
}

echo slugify("PHP 8.5: What's New?") . "\n";
echo slugify('  Café — Résumé Tips  ') . "\n";
echo slugify('தமிழ் Tutorial') . "\n";

 

Output

php-8-5-whats-new
café-résumé-tips
தமிழ்-tutorial

 

Step-by-Step Explanation

  1. mb_strtolower() lowercases text correctly for all languages, and trim() removes outer spaces.
  2. Apostrophes are removed first, so What's becomes whats instead of what-s.
  3. [^\p{L}\p{M}\p{N}]+ matches every run of characters that are not letters, combining marks, or numbers, and replaces it with one hyphen. Including \p{M} keeps Tamil vowel signs attached to their letters.
  4. trim($slug, '-') removes hyphens at the start and end (from the ? and surrounding spaces).

Modern browsers display Unicode slugs correctly. If your system requires ASCII-only URLs, transliterate first (for example, with the intl extension's Transliterator).


preg_replace() vs str_replace()

str_replace()preg_replace()
Searches forFixed textPatterns
SpeedFasterSlower
Reuse parts of the matchNoYes ($1, …)
Calculated replacementsNoWith preg_replace_callback()
Use whenThe text to replace is known exactlyThe text follows a pattern

 


Common Mistakes

Double-Quoted Patterns and Replacements

Effect: PHP interprets ${1} as a variable and \1 or \d-style escapes in its own way before the regex engine sees the string, producing empty or wrong results.

Fix: Use single quotes for both patterns and replacements.

$10 When You Mean Group 1 Followed by 0

Effect: The wrong group (or nothing) is inserted.

Fix: ${1}0.

Ignoring null Results

Effect: Errors spread as null values.

Fix: Check for null and use preg_last_error_msg().

Using Regex for Fixed Text

Effect: Slower, harder-to-read code.

Fix: Use str_replace().

Building HTML From Unescaped Input

Effect: XSS through replaced content.

Fix: Escape the input first, then add generated HTML with safe captured values.


Best Practices

  • Use single-quoted patterns and replacement strings.
  • Use ${n} when a group reference is followed by a digit.
  • Use preg_replace_callback() for calculated or user-provided replacements.
  • Apply multiple patterns in a deliberate order with arrays.
  • Add the u modifier for text that may contain non-ASCII characters, and handle null results.

Official reference: https\://www.php.net/manual/en/function.preg-replace.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 preg_replace and get answers drawn from it.

Signed-in readers only.