Here is my implementation based on Alain answer :
public static function mbReplace($search, $replace, $subject, $encoding = 'auto', &$count=0) { if(!is_array($subject)) { $searches = is_array($search) ? array_values($search) : [$search]; $replacements = is_array($replace) ? array_values($replace) : [$replace]; $replacements = array_pad($replacements, count($searches), ''); foreach($searches as $key => $search) { $replace = $replacements[$key]; $search_len = mb_strlen($search, $encoding); $sb = []; while(($offset = mb_strpos($subject, $search, 0, $encoding)) !== false) { $sb[] = mb_substr($subject, 0, $offset, $encoding); $subject = mb_substr($subject, $offset + $search_len, null, $encoding); ++$count; } $sb[] = $subject; $subject = implode($replace, $sb); } } else { foreach($subject as $key => $value) { $subject[$key] = self::mbReplace($search, $replace, $value, $encoding, $count); } } return $subject; }
It does not accept character encoding, although I assume you can set it through mb_regex_encoding .
My unit tests pass:
function testMbReplace() { $this->assertSame('bbb',Str::mbReplace('a','b','aaa','auto',$count1)); $this->assertSame(3,$count1); $this->assertSame('ccc',Str::mbReplace(['a','b'],['b','c'],'aaa','auto',$count2)); $this->assertSame(6,$count2); $this->assertSame("\xbf\x5c\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'iso-8859-1')); $this->assertSame("\xbf\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'gbk')); }