Signature
preg_grep(string $pattern, array $array, int $flags = 0): array|falsePurpose
preg_grep() filters an array using a regular expression. It tests each element against a PCRE pattern and returns a new array holding only the elements that match. If you have ever wanted array_filter() with a regex callback already built in, this is that function. It is a compact choice for narrowing a list of names, codes or tokens by shape.
Signature
Parameters and defaults
$pattern: a full PCRE pattern as a string, including delimiters and any modifiers, for example'/^a/i'.$array: the input array. Each element is tested in turn. Scalar values that are not strings are converted to strings before matching.$flags: defaults to0. The only flag isPREG_GREP_INVERT, which returns the elements that do not match the pattern.
Return value and failure cases
The function returns an array that uses the keys of the input array. That means a filtered result is not re-indexed: if the elements at positions 0 and 2 match, the result has keys 0 and 2.
On failure it returns false. The manual states that a pattern which does not compile to a valid regular expression causes an E_WARNING. In practice, a missing delimiter or an unbalanced group are typical causes. If the failure may come from the matching engine rather than the pattern, preg_last_error() can tell you more.
Example 1: keeping keys
The first example selects PDF names from a list. The second line of output shows array_values() re-indexing the result.
$files = ['report.pdf', 'notes.txt', 'summary.pdf', 'image.png'];
$pdfs = preg_grep('/\.pdf$/', $files);
echo json_encode($pdfs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode(array_values($pdfs), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"0":"report.pdf","2":"summary.pdf"}
["report.pdf","summary.pdf"]
Because the surviving keys are 0 and 2, JSON encodes the first result as an object. The second result is a plain list.
Example 2: inverting the match
With PREG_GREP_INVERT you get the complement. String keys are preserved too.
$items = ['a' => 'apple', 'b' => 'banana', 'c' => 'avocado', 'd' => 'cherry'];
$matches = preg_grep('/^a/', $items);
$others = preg_grep('/^a/', $items, PREG_GREP_INVERT);
echo json_encode($matches, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode($others, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"a":"apple","c":"avocado"}
{"b":"banana","d":"cherry"}
Example 3: mixed values
Elements do not have to be strings. Here the pattern accepts decimal numbers such as 2.74 or .5, and it ignores whole numbers. The float 3.5 is tested as the string 3.5, yet it appears in the result with its original type.
$values = ["4", 3.5, "2.74", 42, ".5"];
$decimals = preg_grep('/^(\d+)?\.\d+$/', $values);
echo json_encode($decimals, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"1":3.5,"2":"2.74","4":".5"}
The returned elements are the original values, not the converted strings. Only the matching step uses the string form.
Edge cases
- An empty input array gives an empty array back.
- If nothing matches, the result is an empty array, not
false. Do not treat an empty result as an error. Check forfalseseparately if you need to detect a bad pattern. - Anchors matter.
'/pdf/'matchespdf_notes.txtas well, while'/\.pdf$/'matches only names ending in.pdf. - Modifiers such as
ifor case-insensitive matching go after the closing delimiter. preg_grep()only reports whether an element matches. It does not return capture groups. Usepreg_match()orpreg_match_all()when you need the matched text.nullis converted to an empty string andtrueto"1"when matching, so be careful when an array mixes types.
Common mistakes
- Expecting a re-indexed list. The keys are preserved. Call
array_values()when you need sequential keys, for example before encoding to a JSON array or indexing with[0]. - Forgetting delimiters. The pattern needs delimiters, so
'^a'is invalid while'/^a/'is valid. - Inserting raw user input into a pattern. Special characters like
.,*or(change the meaning. Run user text throughpreg_quote()and pass the delimiter as its second argument. - Using it for plain substring tests. If you only need a fixed substring,
str_contains()witharray_filter()is simpler and clearer. - Confusing the inverse flag with a negated pattern.
PREG_GREP_INVERTflips the selection of elements. It does not alter how the pattern itself is interpreted. - Ignoring
false. A malformed pattern emits a warning and returnsfalse. Passing that straight tocount()orforeachwill cause further problems.
Related functions
preg_match()tests one string and can return capture groups.preg_match_all()performs a global match on one string.preg_filter()performs a search and replace and returns only the changed subjects.preg_quote()escapes text for safe use inside a pattern.preg_last_error()reports the error code of the last PCRE operation.array_filter()filters with an arbitrary callback.
Recap
preg_grep() takes a pattern and an array and returns the matching elements, keeping their original keys. Add PREG_GREP_INVERT to get the elements that do not match. No matches produce an empty array, while an invalid pattern produces a warning and false. Re-index with array_values() when you need a list.
Tested examples
$files = ['report.pdf', 'notes.txt', 'summary.pdf', 'image.png'];
$pdfs = preg_grep('/\.pdf$/', $files);
echo json_encode($pdfs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode(array_values($pdfs), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"0":"report.pdf","2":"summary.pdf"}
["report.pdf","summary.pdf"]
$items = ['a' => 'apple', 'b' => 'banana', 'c' => 'avocado', 'd' => 'cherry'];
$matches = preg_grep('/^a/', $items);
$others = preg_grep('/^a/', $items, PREG_GREP_INVERT);
echo json_encode($matches, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo json_encode($others, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"a":"apple","c":"avocado"}
{"b":"banana","d":"cherry"}
$values = ["4", 3.5, "2.74", 42, ".5"];
$decimals = preg_grep('/^(\d+)?\.\d+$/', $values);
echo json_encode($decimals, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"1":3.5,"2":"2.74","4":".5"}