preg_match_all

Finds every match of a regular expression in a string and collects all captured groups.

Signature
preg_match_all(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int|false
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$patternstringThe pattern to search for, including delimiters and modifiers.
$subjectstringThe string to search in.
$matchesoptionalarray= nullPassed by reference and filled with all matches, ordered according to the flags.
$flagsoptionalint= 0PREG_PATTERN_ORDER (the default behaviour, value 1) or PREG_SET_ORDER, optionally combined with PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL.
$offsetoptionalint= 0Byte offset in the subject at which to start searching.

Return value

int|falseThe number of full matches found (0 if none), or false on failure.

Errors

Returns false and emits an E_WARNING when the pattern fails to compile. A ValueError is thrown if PREG_PATTERN_ORDER and PREG_SET_ORDER are combined.

Changelog

  • PHP 7.2.0PREG_UNMATCHED_AS_NULL is now supported in the flags parameter.

preg_match_all() scans the whole subject and returns how many times the pattern matched, filling $matches with everything it found. The shape of that array is decided by one flag, and choosing the wrong one is the single biggest source of confusion with this function.

Pattern order vs set order

The default, PREG_PATTERN_ORDER, groups by capture group: $matches[0] is a list of every full match, $matches[1] is a list of every first group. That is ideal when you want one column, such as all the hrefs. PREG_SET_ORDER groups by match instead: $matches[0] is the first match with all its groups, so you can foreach over it row by row. That is what you want when the groups belong together.

What trips people up

Two things bite. A pattern that can match an empty string will not loop forever — PCRE forces progress — but it will produce a match at every position, which is rarely what you meant. And the return value counts full matches, not captures, so a pattern with three groups matching twice returns 2, not 6.

Example

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

When to use it

  • Collect every URL or image src out of a block of HTML
  • Extract all key=value pairs from a configuration line
  • Count how many times a word appears in a document, with word boundaries

Frequently asked questions

What is the difference between PREG_PATTERN_ORDER and PREG_SET_ORDER?

PREG_PATTERN_ORDER groups results by capture group ($matches[1] is every first-group capture), while PREG_SET_ORDER groups them by match ($matches[0] is the first match with all its groups). Use set order when you want to foreach over matches.

Does preg_match_all return the number of capture groups?

No, it returns the number of full matches. A pattern with three groups that matches twice returns 2.

How do I get all matches with their positions?

Add PREG_OFFSET_CAPTURE to the flags; every entry then becomes a [text, byte offset] pair. You can combine it with PREG_SET_ORDER.

More examples

PREG_SET_ORDER for row-by-row iteration

Each element of $m is one complete match: [0] the whole 'a=1', [1] the key, [2] the value. With the default ordering you would instead have to walk two parallel arrays by index.

Example · php
<?php
$s = 'a=1;b=22;c=333';

preg_match_all('/(\w+)=(\d+)/', $s, $m, PREG_SET_ORDER);

foreach ($m as $set) {
    echo $set[1], ' => ', $set[2], "\n";
}

Counting occurrences

The $matches argument is optional, so you can use the function as a counter. The second call returns 2, not 4 — the return value counts full matches, not the capture groups inside them.

Example · php
<?php
$text = 'the cat sat on the mat, the end';

$count = preg_match_all('/\bthe\b/i', $text);

echo $count, "\n";
var_dump(preg_match_all('/(\w)(\w)/', 'abcd'));

Building an associative array from two columns

Pattern order pays off here: $m[1] is the list of header names and $m[2] the list of values, so array_combine() zips them straight into a map. The m modifier is what makes ^ and $ match at each line.

Example · php
<?php
$headers = "Host: example.com\nAccept: text/html\nX-Id: 42";

preg_match_all('/^([\w-]+):\s*(.+)$/m', $headers, $m);

print_r(array_combine($m[1], $m[2]));

Discussion

  • Be the first to comment on this lesson.