Reputation: 15949
I am a bit confused on the use of preg_replace_callback()
I have a $content
with some URLs inside .
Previously I used
$content = preg_match_all( '/(http[s]?:[^\s]*)/i', $content, $links );
foreach ($links[1] as $link ) {
// we have the link. find image , download, replace the content with image
// echo '</br>LINK : '. $link;
$url = esc_url_raw( $link );
$url_name = parse_url($url);
$url_name = $description = $url_name['host'];// get rid of http://..
$url = 'http://somescriptonsite/v1/' . urlencode($url) . '?w=' . $width ;
}
return $url;
But what I really need is to REPLACE the original URL with my parsed URL...
So I tried the preg_replace_callback:
function o99_simple_parse($content){
$content = preg_replace_callback( '/(http[s]?:[^\s]*)/i', 'o99_simple_callback', $content );
return $content;
}
and :
function o99_simple_callback($url){
// how to get the URL which is actually the match? and width ??
$url = esc_url_raw( $link );
$url_name = parse_url($url);
$url_name = $description = $url_name['host'];// get rid of http://..
$url = 'http://something' . urlencode($url) . '?w=' . $width ;
return $url; // what i really need to replace
}
I assumed that the callback will work in a way that EVERY match will call the callback (recursively ?) and get back results , thus allowing for to replace on-the-fly the URLS in $content with the parsed $url
from o99_simple_callbac()
.
But another question here (and especially this comment) triggered my doubts .
If the preg_replace_callback()
actually pass the whole array of matches , then what is actually the difference between what I used previously ( preg_match_all()
in first example ) and the callback example ?
What am I missing / misunderstanding ??
What would be the correct way of replacing the URLS found in $content
with the parsed urls ?
Upvotes: 3
Views: 4669
Reputation: 37
Upvotes: 2
Reputation: 30273
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.
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
.
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];
// ...
}
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.
Upvotes: 4
Reputation: 145482
It does not pass all matches, but invokes the callback for each match. The callback won't receive a single string parameter, but a list of strings. $match[0]
is the whole match, and $match[1]
the first capture group (what's in your regex between the first parens).
So this is how your callback should look:
function o99_simple_callback($match){
$url = $match[1];
//$url = esc_url_raw( $link );
$url_name = parse_url($url);
$url_name = $description = $url_name['host'];// get rid of http://..
$url = 'http://something' . urlencode($url) . '?w=' . $width ;
return $url; // what i really need to replace
}
Please also see the manual examples on preg_replace_callback
Upvotes: 3