Regular expressions7 min readExamples run on PHP 8.5.10

preg_grep()

Learn how preg_grep() filters array elements by a PCRE pattern, keeps original keys, inverts matches with PREG_GREP_INVERT, and what edge cases to watch.

Signature

preg_grep(string $pattern, array $array, int $flags = 0): array|false

Purpose

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 to 0. The only flag is PREG_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.

Example
<?php
$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";
Output
{"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.

Example
<?php
$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";
Output
{"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.

Example
<?php
$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";
Output
{"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 for false separately if you need to detect a bad pattern.
  • Anchors matter. '/pdf/' matches pdf_notes.txt as well, while '/\.pdf$/' matches only names ending in .pdf.
  • Modifiers such as i for case-insensitive matching go after the closing delimiter.
  • preg_grep() only reports whether an element matches. It does not return capture groups. Use preg_match() or preg_match_all() when you need the matched text.
  • null is converted to an empty string and true to "1" when matching, so be careful when an array mixes types.

Common mistakes

  1. 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].
  2. Forgetting delimiters. The pattern needs delimiters, so '^a' is invalid while '/^a/' is valid.
  3. Inserting raw user input into a pattern. Special characters like ., * or ( change the meaning. Run user text through preg_quote() and pass the delimiter as its second argument.
  4. Using it for plain substring tests. If you only need a fixed substring, str_contains() with array_filter() is simpler and clearer.
  5. Confusing the inverse flag with a negated pattern. PREG_GREP_INVERT flips the selection of elements. It does not alter how the pattern itself is interpreted.
  6. Ignoring false. A malformed pattern emits a warning and returns false. Passing that straight to count() or foreach will cause further problems.
  • 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

Filter file names and observe preserved keys
<?php
$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";
Output
{"0":"report.pdf","2":"summary.pdf"}
["report.pdf","summary.pdf"]
Invert the selection with PREG_GREP_INVERT
<?php
$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";
Output
{"a":"apple","c":"avocado"}
{"b":"banana","d":"cherry"}
Match decimal numbers in a mixed array
<?php
$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";
Output
{"1":3.5,"2":"2.74","4":".5"}