The other answers may have been sufficient, but let me give you one more take using a simpler example.
Let's say we have the following data in $subject
,
RECORD Male 1987-11-29 New York
RECORD Female 1987-07-13 Tennessee
RECORD Female 1990-04-14 New York
and the following regular expression in $pattern
,
/RECORD (Male|Female) (\d\d\d\d)-(\d\d)-(\d\d) ([\w ]+)/
Let's compare three approaches.
preg_match_all
First, the vanilla preg_match_all
:
preg_match_all($pattern, $subject, $matches);
Here's what $matches
comes out to be:
Array
(
[0] => Array
(
[0] => RECORD Male 1987-11-29 New York
[1] => RECORD Female 1987-07-13 Tennessee
[2] => RECORD Female 1990-04-14 New York
)
[1] => Array
(
[0] => Male
[1] => Female
[2] => Female
)
[2] => Array
(
[0] => 1987
[1] => 1987
[2] => 1990
)
[3] => Array
(
[0] => 11
[1] => 07
[2] => 04
)
[4] => Array
(
[0] => 29
[1] => 13
[2] => 14
)
[5] => Array
(
[0] => New York
[1] => Tennessee
[2] => New York
)
)
Whether we're talking about the gender field in my example with the URL field in your example, it's clear that looping through $matches[1]
iterates through just that field:
foreach ($matches[1] as $match)
{
$gender = $match;
// ...
}
However, as you noticed, changes you make to $matches[1]
, even if you iterated through its subarrays by reference, do not reflect in $subject
, i.e. you cannot perform replacements via preg_match_all
.
preg_match_all with PREG_SET_ORDER
Before we jump into preg_replace_callback
though, let's take a look at one of preg_match_all
's commonly used flags, PREG_SET_ORDER
.
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
This outputs something (seemingly) completely different!
Array
(
[0] => Array
(
[0] => RECORD Male 1987-11-29 New York
[1] => Male
[2] => 1987
[3] => 11
[4] => 29
[5] => New York
)
[1] => Array
(
[0] => RECORD Female 1987-07-13 Tennessee
[1] => Female
[2] => 1987
[3] => 07
[4] => 13
[5] => Tennessee
)
[2] => Array
(
[0] => RECORD Female 1990-04-14 New York
[1] => Female
[2] => 1990
[3] => 04
[4] => 14
[5] => New York
)
)
Now, each subarray contains the set of capture groups per match, as opposed to the set of matches, per capture group. (In yet other words, this is the transpose of the other array.) If you wanted to play with the gender (or URL) of each match, you'd now have to write this:
foreach ($matches as $match)
{
$gender = $match[1];
// ...
}
preg_replace_callback
And that's what preg_replace_callback
is like. It calls the callback for each set of matches (that is, including all its capture groups, at once), as if you were using the PREG_SET_ORDER
flag. That is, contrast the way preg_replace_callback
is used,
preg_replace_callback($pattern, $subject, 'my_callback');
function my_callback($matches)
{
$gender = $match[1];
// ...
return $gender;
}
to the PREG_SET_ORDER
example. Note how the two examples iterate through matches in exactly the same way, the only difference being that preg_replace_callback
gives you an opportunity to return a value for replacement.