preg_grep
Returns the array elements that match a regular expression, keeping their original keys.
preg_grep(string $pattern, array $array, int $flags = 0): array|false- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$pattern | string | The pattern to test each element against, including delimiters. |
$array | array | The input array. Its values are tested as strings. |
$flagsoptional | int= 0 | 0, or PREG_GREP_INVERT to return the elements that do not match. |
Return value
array|falseAn array of the elements that matched, indexed with the keys they had in the input array. Returns false on failure.
Errors
Emits an E_WARNING and returns false when the pattern fails to compile. No ValueError is thrown for an invalid flags value: php_pcre_grep_impl only does `invert = flags & PREG_GREP_INVERT`, so any other bits are silently ignored. Verified on PHP 8.3: preg_grep('/a/', ['a'], 99) returns an array with no exception.
preg_grep() is the regex version of array_filter(): it runs one pattern against every value in an array and hands back the ones that matched, keys intact. Pass PREG_GREP_INVERT and you get the complement instead.
When to reach for it
Reach for it whenever you would otherwise write array_filter($a, fn($v) => preg_match($p, $v)) — it is shorter, does the same thing, and avoids a userland call per element. Typical jobs: pick out the log lines that mention a request ID, keep only the filenames with an image extension, or strip every key-looking string out of a config dump with the invert flag.
What trips people up
Two things. First, it filters on values, not keys; there is no built-in way to grep the keys, so filter array_keys($a) and intersect if that is what you need. Second, the result keeps the original numeric keys, so $result[0] may not exist and json_encode() will emit an object. Non-string values such as integers are coerced to strings before matching; objects without __toString() will raise an error.
Example
When to use it
- Keep only the uploaded filenames whose extension is on an allow-list
- Pull every log line that contains a trace ID out of an array of lines
- Drop all comment and blank lines from a config file read with file()
Frequently asked questions
How do I filter an array by a regular expression in PHP?
Call preg_grep() with the pattern and the array; it returns the matching elements with their keys preserved. Add PREG_GREP_INVERT as the third argument to get the non-matching ones instead.
What is the difference between preg_grep and array_filter?
preg_grep tests each value against one regex, while array_filter runs an arbitrary callback. preg_grep is faster for a plain pattern test because it does not call back into PHP for every element.
Does preg_grep search array keys?
No, it only tests values. To filter by key, run preg_grep on array_keys() and combine the result with array_intersect_key() and array_flip().
More examples
Stripping comments and blank lines
The pattern matches lines that are blank or start with a hash; PREG_GREP_INVERT flips it so those lines are removed. array_values() renumbers the gaps left by keys 0, 2 and 4.
<?php
$config = ['# database', 'host=localhost', '', 'port=5432', ' # end'];
$clean = preg_grep('/^\s*(#|$)/', $config, PREG_GREP_INVERT);
print_r(array_values($clean));Grepping the keys instead of the values
preg_grep only looks at values, so the keys are grepped separately and then intersected back. The output keeps user_id and user_name and drops created_at.
<?php
$row = ['user_id' => 3, 'user_name' => 'ada', 'created_at' => '2026-08-30'];
$userKeys = preg_grep('/^user_/', array_keys($row));
$subset = array_intersect_key($row, array_flip($userKeys));
print_r($subset);Numbers are matched as strings
Values are cast to string before matching, so the integer 10 and the string '20' both match. 3.5 becomes '3.5' and fails on the dot, and 'x9' fails on the anchor — both are dropped.
<?php
$mixed = [10, '20', 3.5, 'x9'];
print_r(preg_grep('/^\d+$/', $mixed));
Discussion