preg_replace_callback
In PHP, regular expressions are an essential tool for manipulating and searching strings. The preg_replace_callback() function is one of the many functions that
Introduction
preg_replace_callback() performs a regular-expression search-and-replace, but instead of supplying a fixed replacement string, you hand it a callback function that is run for each match. The callback receives the match (and any capturing groups) and returns the text to substitute in. This lets you compute replacements with real PHP logic — uppercase a word, add 1 to a number, look a value up in a table — which the plain preg_replace() function cannot do.
Use it whenever the replacement depends on what was matched. If your replacement is a constant or a simple backreference like $1, reach for preg_replace() instead; if you need a different callback per pattern, see preg_replace_callback_array().
Syntax
preg_replace_callback(
string|array $pattern,
callable $callback,
string|array $subject,
int $limit = -1,
int &$count = null,
int $flags = 0
): string|array|null| Parameter | Description |
|---|---|
$pattern | The regular expression (a delimited string) to match, or an array of patterns. |
$callback | A callable run for each match. It receives the matches array and returns the replacement string. |
$subject | The string (or array of strings) to search and modify. |
$limit | Maximum replacements per subject string. -1 (the default) means no limit. |
$count | Passed by reference; filled with the number of replacements made. |
$flags | PREG_OFFSET_CAPTURE and/or PREG_UNMATCHED_AS_NULL, mirroring preg_match(). |
It returns the modified subject, or null if a regex error occurs. The first argument to the callback is the $matches array: $matches[0] is the whole match and $matches[1], $matches[2], … are the captured groups — exactly like the array preg_match() fills in.
Basic example: uppercase every word
The pattern \w+ matches each run of word characters. For every match the callback receives $matches[0] (the word) and returns its uppercase form, which is spliced back into the string.
Working with capturing groups
Parentheses in the pattern create capturing groups that show up at $matches[1], $matches[2], and so on. Here we add one to every number in a string:
<?php
$subject = 'Room 12, floor 3, building 7';
$result = preg_replace_callback('/(\d+)/', function ($m) {
return (string) ((int) $m[1] + 1);
}, $subject);
echo $result;
// Room 13, floor 4, building 8Because the callback runs real code, the increment is computed per match — something a static replacement string can never express.
A practical use case: masking sensitive data
A common job is partially hiding emails or card numbers in logs. The callback can decide how much of each match to reveal:
<?php
$text = 'Contact: [email protected] or [email protected]';
$result = preg_replace_callback('/([\w.]+)@([\w.]+)/', function ($m) {
$name = $m[1];
$masked = $name[0] . str_repeat('*', max(strlen($name) - 1, 1));
return $masked . '@' . $m[2];
}, $text);
echo $result;
// Contact: a****@example.com or b**@test.orgCounting and limiting replacements
The $limit and $count parameters let you cap how many matches are processed and learn how many actually were:
<?php
$subject = 'a a a a a';
$result = preg_replace_callback('/a/', function ($m) {
return 'b';
}, $subject, 2, $count);
echo $result, "\n"; // b b a a a
echo $count; // 2Only the first two as are replaced because $limit is 2, and $count reports the number of replacements made.
preg_replace vs. preg_replace_callback
| Use | Function |
|---|---|
Fixed text or a backreference like $1 | preg_replace() |
| Replacement computed from the match | preg_replace_callback() |
| A different callback per pattern | preg_replace_callback_array() |
For matching without replacing, see preg_match() and preg_match_all(), and the PHP regular expressions chapter for pattern syntax.
Common gotchas
- Always return a string. The callback's return value is cast to a string and inserted. Returning
nullremoves the match; forgetting toreturninserts an empty string. - Reference the right index.
$matches[0]is the full match; group 1 lives at$matches[1]. Off-by-one here is the most frequent bug. - Escape with care. Unlike
preg_replace(), you do not write$1/\1backreferences in the result — you build the string yourself, so there is nothing to escape in the replacement. - A
nullreturn value from the function itself (not the callback) signals a regex error, such as an unmatched delimiter.
Conclusion
preg_replace_callback() is the right tool whenever a replacement must be computed rather than stated. It pairs the matching power of regular expressions with arbitrary PHP logic in the callback, making it ideal for transforming, masking, or recalculating matched text. For static replacements stick with preg_replace(), and for several patterns at once use preg_replace_callback_array().