1

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?

4 Answers 4

7

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)
Sign up to request clarification or add additional context in comments.

2 Comments

OK. Then what's the solution to this situation?
That explenation was exactly what I needed. Thank you very much Martin!
3

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"

Comments

1

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.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.