preg_replace_callback
Replaces regex matches using the string returned by a callback for each match.
preg_replace_callback(string|array $pattern, callable $callback, string|array $subject, int $limit = -1, int &$count = null, int $flags = 0): string|array|null- Added in PHP 4.0.5
Parameters
| Parameter | Type | Description |
|---|---|---|
$pattern | string|array | One pattern, or an array of patterns applied in order. |
$callback | callable | Called once per match with the matches array as its only argument; its return value is used as the replacement. |
$subject | string|array | The string, or array of strings, to run the replacement on. |
$limitoptional | int= -1 | Maximum replacements per pattern per subject; -1 means unlimited. |
$countoptional | int= null | Passed by reference and filled with the total number of replacements performed. |
$flagsoptional | int= 0 | PREG_OFFSET_CAPTURE and/or PREG_UNMATCHED_AS_NULL, changing the shape of the array handed to the callback. |
Return value
string|array|nullAn array if subject is an array, otherwise a string. Returns null if an error occurred.
Errors
A TypeError is thrown if callback is not callable. Returns null and emits an E_WARNING if a pattern fails to compile; an exception thrown inside the callback propagates out of the call.
Changelog
- PHP 7.4.0The flags parameter was added, allowing PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL for the matches passed to the callback.
The callback should return a string, but the return value is coerced rather than rejected: returning null silently splices in an empty string, and returning an array splices in Array with an "Array to string conversion" warning. Only an object without __toString() actually throws (an Error).
Example
When to use it
- Mask all but the last four digits of any card-like number in a log line
- Fill {name}-style placeholders in a template from an array of values
- Convert every measurement in a document from inches to centimetres
Frequently asked questions
What is the difference between preg_replace and preg_replace_callback?
preg_replace uses a fixed replacement string with $1-style backreferences; preg_replace_callback calls your function for each match and uses what it returns. Reach for the callback whenever the replacement has to be computed from the matched text.
What does the callback receive?
A single array shaped like the $matches array of preg_match: index 0 is the whole match, index 1 onwards are the capture groups, and named groups are also available by name. Passing PREG_OFFSET_CAPTURE turns each entry into a [text, offset] pair.
How do I use a variable from outside the callback?
Import it with use ($var) on a closure, use &$var to modify it, or use an arrow function (fn), which captures outer variables by value automatically.
More examples
Masking sensitive numbers
Each match is replaced by asterisks plus its own last four digits, which no static replacement string could produce. $count reports 2, one per card number found.
<?php
$line = 'card 4111111111111111 and card 5500000000000004';
echo preg_replace_callback('/\b\d{13,16}\b/', function (array $m) {
return str_repeat('*', strlen($m[0]) - 4) . substr($m[0], -4);
}, $line, -1, $count), "\n";
echo "masked: $count\n";Using PREG_OFFSET_CAPTURE in the callback
With the flag (PHP 7.4+) every entry in $m becomes [text, byte offset], so $m[0][1] is where the match started. The output is 'a 12@2 b 345@7' and the count is 2.
<?php
echo preg_replace_callback(
'/\d+/',
fn(array $m) => $m[0][0] . '@' . $m[0][1],
'a 12 b 345',
-1,
$count,
PREG_OFFSET_CAPTURE
), "\n";
echo $count, "\n";Named groups and stateful callbacks
The callback reads the match by name and keeps a counter across calls by capturing $n by reference, producing h1 and h2 ids. A plain replacement string could not number the headings.
<?php
$n = 0;
$md = "# Title\n# Another";
echo preg_replace_callback(
'/^#\s*(?<heading>.+)$/m',
function (array $m) use (&$n) {
$n++;
return "<h1 id=\"h$n\">{$m['heading']}</h1>";
},
$md
), "\n";
Discussion