preg_filter

Runs a regex replacement over subjects and returns only the ones that actually matched.

Signature
preg_filter(string|array $pattern, string|array $replacement, string|array $subject, int $limit = -1, int &$count = null): string|array|null
  • Added in PHP 5.3.0

Parameters

ParameterTypeDescription
$patternstring|arrayOne pattern, or an array of patterns, applied in order.
$replacementstring|arrayReplacement string, or an array of replacements paired positionally with the patterns. Backreferences are written as $1 or \1.
$subjectstring|arrayThe string or array of strings to search and replace in.
$limitoptionalint= -1Maximum replacements per pattern per subject; -1 means no limit.
$countoptionalint= nullPassed by reference and filled with the total number of replacements performed.

Return value

string|array|nullAn array when subject is an array, otherwise a string. Only subjects that matched at least one pattern are returned; if nothing matched you get an empty array (array subject) or null (string subject).

Errors

Emits an E_WARNING when a pattern fails to compile; it does not throw. The failure value follows the subject type, exactly as for 'no match': null when subject is a string, an empty array when subject is an array (verified on PHP 8.3: @preg_filter('/[/', 'x', ['a','b']) returns array(0) {}).

preg_filter() is preg_replace() with the non-matching subjects dropped. Given an array, it returns only the elements that at least one pattern touched, already transformed. Given a plain string, it returns the transformed string or null.

When to reach for it

Use it when you want to filter and reshape in a single pass: pull the numeric ID out of every well-formed row and silently discard the junk lines, normalise the phone numbers that look like phone numbers, or turn a mixed list of paths into just the ones under a prefix. Doing the same with preg_grep() followed by preg_replace() means compiling and running the pattern twice.

What trips people up

The returned array keeps the original keys, so the result is usually not a list any more — index 0 may be missing entirely. That matters the moment you json_encode() it, because a gapped array encodes as a JSON object, not an array.

Also remember that "matched" means matched by any of the patterns, not that the value changed. A replacement that produces the same text still counts as a match and is kept.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Pull the numeric order ID out of a mixed log dump and throw away the lines that have none
  • Normalise the entries of a CSV column that match a date format and drop the rest
  • Turn a list of file paths into just the basenames of the ones with a given extension

Frequently asked questions

What is the difference between preg_filter and preg_replace?

They perform the same replacement, but preg_filter returns only the subjects that matched at least one pattern, while preg_replace returns every subject whether it matched or not. For a string subject, preg_filter returns null on no match where preg_replace returns the original string.

Does preg_filter preserve array keys?

Yes. The surviving elements keep the keys they had in the input array, so the result is usually not a sequential list. Wrap it in array_values() if you need a list.

Can I use preg_filter to just filter without replacing?

You can, by passing '$0' as the replacement so each match is replaced with itself, but preg_grep() is clearer and cheaper for pure filtering.

More examples

preg_filter vs preg_replace

preg_replace returns all three elements ('a#', 'bb', 'c#') because it always returns the subject. preg_filter returns only keys 0 and 2, because 'bb' contained no digit. This is the entire difference between the two functions.

Example · php
<?php
$in = ['a1', 'bb', 'c3'];

print_r(preg_replace('/\d/', '#', $in));
print_r(preg_filter('/\d/', '#', $in));

Counting the replacements

$count receives 3 — one per subject that matched. The returned array has keys 0, 2 and 3, so the count tells you how many rows survived without having to call count() on a re-indexed copy.

Example · php
<?php
$lines = ['id=7', 'junk', 'id=8', 'id=9'];

$out = preg_filter('/^id=(\d+)$/', 'ID $1', $lines, -1, $count);

print_r($out);
echo "replacements: $count\n";

Several patterns at once

Patterns and replacements are paired by position and every pattern is tried against every subject. 'hello' matches neither, so it is dropped; the other two are rewritten and keep keys 0 and 1.

Example · php
<?php
$values = ['12:30', '2026-08-30', 'hello'];

$out = preg_filter(
    ['/^(\d{2}):(\d{2})$/', '/^(\d{4})-(\d{2})-(\d{2})$/'],
    ['time $1h$2',          'date $3\/$2\/$1'],
    $values
);

print_r($out);

Discussion

  • Be the first to comment on this lesson.