Domanda

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 ?

È stato utile?

Soluzione 2

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

Altri suggerimenti

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.

preg_replace_callback

  1. Using preg_replace_callback() to Replace Patterns
  2. Generating replacement strings with a callback function
  3. Generating replacement strings with an anonymous function
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top