preg_match_all
Finds every match of a regular expression in a string and collects all captured groups.
preg_match_all(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int|false- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$pattern | string | The pattern to search for, including delimiters and modifiers. |
$subject | string | The string to search in. |
$matchesoptional | array= null | Passed by reference and filled with all matches, ordered according to the flags. |
$flagsoptional | int= 0 | PREG_PATTERN_ORDER (the default behaviour, value 1) or PREG_SET_ORDER, optionally combined with PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL. |
$offsetoptional | int= 0 | Byte 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
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.
<?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.
<?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.
<?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