PHP将第一次出现替换为x,将第二次出现的字符串替换为y

This is a tricky question here is what I am trying to do:

$str="abc def abc";

I want to replace first occurrence of abc with 123 and second occurrence of abc with 456 Now with preg_match it is possible that I replace first occurrence of abc with 123 like this

preg_replace('/abc/', '123', $str, 1);

But how to replace the second occurrence with 456 and most importantly how to do it at once and generate the required string (i.e 123 def 456).

Note: Strings are generated from external source.

Thanks Ahmar

You were very close:

$str = preg_replace('/abc/', '123', $str, 1); // will replace first 'abc'
$str = preg_replace('/abc/', '456', $str);    // will replace all others

The reason this will work, is because the 2nd regex's first occurence, is actually the 2nd item.
Pro: Very readable, very easy to understand and implement
Con: the string will be regexed twice (bad for large strings), options are limited


If this is not what you want, you can use preg_replace_callback()

$firstHasBeenFound = false;
function magicFunction($matches){
    global $firstHasBeenFound;
    print_r($matches);
    if( !$firstHasBeenFound ){
        /* do something for the first time */ ;
        $firstHasBeenFound = true; // save for next round
    }
    else{
         /* do something for the test */ ;
    }
}

$str = preg_replace_callback('/abc/', 'magicFunction', $str);

Pro: A lot more variants can be made, more control over code, only one parse of the string
Con: More difficult to read/implement

In this example I use $firstHasBeenFound, but you could use a increment to do something each 2nd, or something when you find match 7, etc

How about this one?:

$str = preg_replace('/abc(.*?)abc/', '123${1}456', $str, 1);
$str="abc def abc ghi abc";
$replacements = array('123', '456');

$str = preg_replace_callback(
    '/abc/',
    function ($matches) use ($replacements) {
        static $counter = 0;
        $returnValue = $matches[0];
        if (isset($replacements[$counter]))
            $returnValue = $replacements[$counter];
        $counter++;
        return $returnValue;
    },
    $str
);

var_dump($str);

You may use preg_replace_callback with closure as callback (works in PHP 5.3+).

$str = "abc def abc abc def abc";
$a_replc = array('123', '456');
$n = 0;
$str = preg_replace_callback('/abc/', function () use (&$n, $a_replc) {
    return $a_replc[$n++ % count($a_replc)];
}, $str);