elfuego1
elfuego1

Reputation: 10348

PHP: Problem with str_replace

I have noticed a strange behaviour of a str_replace function. Can you tell me why instead of number 3 in no_2 i get no_4 ?

Here is the case:

$pattern = array(1,2,3);
$change = array(1,3,4); 
$sql = "SELECT * FROM %s WHERE no_1 IN (%s) AND no_2 IN (%s) AND no_3 IN (%s)";

$test_multiply[] = str_replace($pattern, $change, $sql);

Which gives:

Array ( [0] => SELECT * FROM %s WHERE no_1 IN (%s) AND no_4 IN (%s) AND no_4 IN (%s) ) 

Can you tell me what should I do to receive no_3 instead of no_2?

Upvotes: 1

Views: 915

Answers (4)

Gumbo
Gumbo

Reputation: 655129

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:

  1. SELECT * FROM %s WHERE no_1 IN (%s) AND no_2 IN (%s) AND no_3 IN (%s)
  2. SELECT * FROM %s WHERE no_1 IN (%s) AND no_3 IN (%s) AND no_3 IN (%s)
  3. 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"

Upvotes: 3

dynamic
dynamic

Reputation: 48091

Because once you replace 2 with 3 the next replace will always replace 3 with 4

Upvotes: 0

Josh
Josh

Reputation: 1281

First, you're replacing 1 with 1, so thats fine

SELECT * FROM %s WHERE no_1 IN (%s) AND no_2 IN (%s) AND no_3 IN (%s)

Then, replacing 2 with 3, to get

SELECT * FROM %s WHERE no_1 IN (%s) AND no_3 IN (%s) AND no_3 IN (%s)

Then replacing 3 with 4, resulting in SELECT * FROM %s WHERE no_1 IN (%s) AND no_4 IN (%s) AND no_4 IN (%s)

Maybe you should replace 2 with another value (not 3), which you can replace again later.

Upvotes: 1

Pascal MARTIN
Pascal MARTIN

Reputation: 400922

The documentation for str_replace() says (quoting) :

Because str_replace() replaces left to right, it might replace a previously inserted value when doing multiple replacements.

I believe you are precisely in this situation :

  • Your no_2 gets replaced to no_3 -- because of the second item in your $pattern and $change arrays
  • But, then, that no_3 gets replaced to no_4 -- because of the thid item in your $pattern and $change arrays

To avoid that specific situation, you might try reversing the order of the items in those two arrays :
$pattern = array(3, 2, 1);
$change = array(4, 3, 1); 

And you'd get the following result :

SELECT * FROM %s WHERE no_1 IN (%s) AND no_3 IN (%s) AND no_4 IN (%s)

Upvotes: 7

Related Questions