Regular expressions7 min readExamples run on PHP 8.5.10

preg_filter()

Learn how preg_filter() differs from preg_replace(): it returns only the subjects that actually matched, with examples, edge cases and common mistakes.

Signature

preg_filter(
    string|array $pattern,
    string|array $replacement,
    string|array $subject,
    int $limit = -1,
    int &$count = null
): string|array|null
Available since PHP 5.3.07.48.08.18.28.38.48.5All versions →

Purpose

preg_filter() performs a regular expression search and replace, just like preg_replace(), with one important difference: it only returns the subjects in which a match was found. With a single string, you get either the changed string or null. With an array, you get a smaller array that contains only the entries that were transformed.

That makes it handy when you want to rewrite and select in one step, for example converting only the entries that follow a given format and dropping everything else.

Signature

Parameters and defaults

  • $pattern: a pattern string with delimiters, or an array of patterns.
  • $replacement: a replacement string or an array of replacements. References such as $1 or ${1} insert captured text.
  • $subject: a string, or an array of strings to process.
  • $limit: the maximum number of replacements per pattern within each subject. The default -1 means no limit.
  • $count: an optional by-reference variable that receives the total number of replacements made.

The parameters behave as described for preg_replace(), so the same rules for pattern arrays, replacement arrays and references apply.

Return value and failures

If $subject is an array, the function returns an array. Otherwise it returns a string. When nothing matched, or when an error occurred, you get an empty array for an array subject and null for a string subject.

If a pattern does not compile into a valid regular expression, PHP emits an E_WARNING. Because a failed call and a call with no matches look alike, preg_last_error() can help you tell a runtime PCRE problem from an ordinary non-match.

Example 1: string subjects

With a string subject, the result is the new string when the pattern matches and null otherwise.

Example
<?php
$flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
$changed = preg_filter('/\d+/', '#', 'Order 42 shipped');
echo json_encode($changed, $flags), "\n";
$unchanged = preg_filter('/\d+/', '#', 'No digits here');
var_dump($unchanged);
Output
"Order # shipped"
NULL

The second call returns null, not the original text. This is the main difference from preg_replace(), which would hand back the unchanged string.

Example 2: filtering an array

With an array subject, entries that did not match are removed, and the keys of the survivors are preserved.

Example
<?php
$flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
$items = ['a' => 'item-1', 'b' => 'plain', 'c' => 'item-22'];
$filtered = preg_filter('/^item-(\d+)$/', 'ID:$1', $items);
$replaced = preg_replace('/^item-(\d+)$/', 'ID:$1', $items);
echo json_encode($filtered, $flags), "\n";
echo json_encode($replaced, $flags), "\n";
Output
{"a":"ID:1","c":"ID:22"}
{"a":"ID:1","b":"plain","c":"ID:22"}

The first line shows only a and c. The second shows that preg_replace() keeps b untouched.

Example 3: limit and count

The $limit argument applies per subject, while $count adds up the replacements across all subjects.

Example
<?php
$subjects = ['x1y2', 'abc', 'z3'];
$count = 0;
$result = preg_filter('/\d/', '_', $subjects, 1, $count);
echo json_encode($result), "\n";
echo $count, "\n";
Output
{"0":"x_y2","2":"z_"}
2

Only the first digit in x1y2 is replaced, abc is dropped, and the total count is 2. Notice that the surviving keys are 0 and 2, so the JSON encoder writes an object instead of a list.

Edge cases

  • Keys are not renumbered. If you need a list, wrap the result in array_values() when it is an array.
  • Empty results. An array subject with no matches yields [], and an empty input array also yields an empty array.
  • Several patterns. The patterns are applied in order to each subject, and a subject is kept if it was changed by any of them. In the official comparison with preg_replace(), the subject '1' becomes A:C:1 because later patterns work on the output of earlier ones.
  • Matching but identical output. A subject whose match is replaced by the same text still counts as matched, because the decision depends on a match occurring.
  • Invalid patterns. Expect a warning, plus null or an empty array as the result.

Common mistakes

  1. Expecting the original string back. For a string subject, a non-match gives null. Test with === null, not with a loose truthiness check, since a valid result such as "0" is falsy.
  2. Assuming sequential keys. After filtering, indexes can have gaps, which can surprise loops that use for ($i = 0; ...).
  3. Using it when you need every item. If unmatched entries must stay, use preg_replace().
  4. Forgetting delimiters. A pattern like \d+ without delimiters is invalid and triggers a warning.
  5. Confusing failure with no match. Both produce the same kind of empty result, so check preg_last_error() when it matters.
  • preg_replace() performs the same replacement but returns every subject.
  • preg_replace_callback() computes replacements with a callback.
  • preg_grep() filters array entries by pattern without changing them.
  • preg_quote() escapes literal text before it is inserted into a pattern.
  • preg_last_error() reports the code of the last PCRE error.

Recap

Use preg_filter() when you want to rewrite text with a regular expression and discard whatever did not match. Strings give back the changed string or null, arrays give back only the changed entries with their original keys, and $count tells you how many replacements were made.

Tested examples

String subjects: changed string or null
<?php
$flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
$changed = preg_filter('/\d+/', '#', 'Order 42 shipped');
echo json_encode($changed, $flags), "\n";
$unchanged = preg_filter('/\d+/', '#', 'No digits here');
var_dump($unchanged);
Output
"Order # shipped"
NULL
Filtering an array while preserving keys
<?php
$flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
$items = ['a' => 'item-1', 'b' => 'plain', 'c' => 'item-22'];
$filtered = preg_filter('/^item-(\d+)$/', 'ID:$1', $items);
$replaced = preg_replace('/^item-(\d+)$/', 'ID:$1', $items);
echo json_encode($filtered, $flags), "\n";
echo json_encode($replaced, $flags), "\n";
Output
{"a":"ID:1","c":"ID:22"}
{"a":"ID:1","b":"plain","c":"ID:22"}
Using limit and count
<?php
$subjects = ['x1y2', 'abc', 'z3'];
$count = 0;
$result = preg_filter('/\d/', '_', $subjects, 1, $count);
echo json_encode($result), "\n";
echo $count, "\n";
Output
{"0":"x_y2","2":"z_"}
2