Using str_replace so that it only acts on the first match?

There's no version of it, but the solution isn't hacky at all.

$pos = strpos($haystack, $needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
}

Pretty easy, and saves the performance penalty of regular expressions.


Bonus: If you want to replace last occurrence, just use strrpos in place of strpos.


Can be done with preg_replace:

function str_replace_first($search, $replace, $subject)
{
    $search = '/'.preg_quote($search, '/').'/';
    return preg_replace($search, $replace, $subject, 1);
}

echo str_replace_first('abc', '123', 'abcdef abcdef abcdef'); 
// outputs '123def abcdef abcdef'

The magic is in the optional fourth parameter [Limit]. From the documentation:

[Limit] - The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).


Though, see zombat's answer for a more efficient method (roughly, 3-4x faster).


Edit: both answers have been updated and are now correct. I'll leave the answer since the function timings are still useful.

The answers by 'zombat' and 'too much php' are unfortunately not correct. This is a revision to the answer zombat posted (as I don't have enough reputation to post a comment):

$pos = strpos($haystack,$needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack,$replace,$pos,strlen($needle));
}

Note the strlen($needle), instead of strlen($replace). Zombat's example will only work correctly if needle and replace are the same length.

Here's the same functionality in a function with the same signature as PHP's own str_replace:

function str_replace_first($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos !== false) {
        return substr_replace($subject, $replace, $pos, strlen($search));
    }
    return $subject;
}

This is the revised answer of 'too much php':

implode($replace, explode($search, $subject, 2));

Note the 2 at the end instead of 1. Or in function format:

function str_replace_first($search, $replace, $subject) {
    return implode($replace, explode($search, $subject, 2));
}

I timed the two functions and the first one is twice as fast when no match is found. They are the same speed when a match is found.