If you use str_replace with an array of needles, every needle is replaced in a separate str_replace call. You can imagine that something like this happens internally:
for ($i=0, $n=min(count($pattern),count($change)); $i<$n; $i++) {
$sql = str_replace($pattern[$i], $change[$i], $sql);
}
So in the first iteration all 1 are replaced by 1, then all 2 are replaced by 3, and then all 3 is replaced by 4:
SELECT * FROM %s WHERE no_1 IN (%s) AND no_2 IN (%s) AND no_3 IN (%s)
SELECT * FROM %s WHERE no_1 IN (%s) AND no_3 IN (%s) AND no_3 IN (%s)
SELECT * FROM %s WHERE no_1 IN (%s) AND no_4 IN (%s) AND no_4 IN (%s)
To do a simultaneous replacement, you can use preg_replace_callback and call a mapping function for each matched pattern:
function str_replacep(array $search, array $replace, $subject, &$count=0) {
$combinedPattern = '/(?:'.implode('|', array_map(function($str) { return preg_quote($str, '/'); }, $search)).')/';
$map = array_combine($search, $replace);
$mapping = function($match) use ($map, $count) {
$count++;
return $map[$match[0]];
};
return preg_replace_callback($combinedPattern, $mapping, $subject);
}
I used anonymous function in this example but it will also work with create_function.
With this the order of the replacement doesn’t matter. You can even exchange two values:
var_dump(str_replacep(array(1,2), array(2,1), "12")); // string(2) "21"