50

I'm having some troubles with the PHP function str_replace when using arrays.

I have this message:

$message = strtolower("L rzzo rwldd ty esp mtdsza'd szdepw ty esp opgtw'd dple");

And I am trying to use str_replace like this:

$new_message = str_replace(
    array('l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a','b','c','d','e','f','g','h','i','j','k'),
    array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'),
    $message);

The result should be A good glass in the bishop's hostel in the devil's seat, but instead, I get p voos vlpss xn twt qxswop's wosttl xn twt stvxl's stpt.

However, when I only try replacing 2 letters it replaces them well:

$new_message = str_replace(array('l','p'), array('a','e'), $message);

the letters l and p will be replaced by a and e.

Why is it not working with the full alphabet array if they are both exactly the same size?

Ry-
  • 218,210
  • 55
  • 464
  • 476
LautaroAngelico
  • 623
  • 2
  • 6
  • 7

5 Answers5

62

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

    // Outputs F because A is replaced with B, then B is replaced with C, and so on...
    // Finally E is replaced with F, because of left to right replacements.
    $search  = array('A', 'B', 'C', 'D', 'E');
    $replace = array('B', 'C', 'D', 'E', 'F');
    $subject = 'A';
    echo str_replace($search, $replace, $subject);
  • This doesn't provide any more information than was already given in the other answer (which happens also to solve the problem). Downvote for you. – Steven Moseley Dec 05 '12 at 03:39
  • 15
    @TheSmose As I see question was 'Why is it not working...' and not what would be an alternative. So I tried to explain with an example, why was it not working. – Rakesh Tembhurne Dec 05 '12 at 03:45
  • 1
    The other answer stated the exact "why" that you posted above, **and** a "how to fix it". Your answer of the **question** in a minimal way, without attempting to solve the **problem** to me is against the grain of SO. Besides, the question was already answered and the problem solved... – Steven Moseley Dec 05 '12 at 03:53
  • 1
    This is a nice clear description of why this happens. Thanks. – Novocaine Feb 08 '17 at 10:41
  • It reframes the information in a way I find more easily understood. – alimack Jun 13 '17 at 09:43
48

str_replace with arrays just performs all the replacements sequentially. Use strtr instead to do them all at once:

$new_message = strtr($message, 'lmnopq...', 'abcdef...');
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • 2
    Note, you could make this work for upper and lower case simply by doing `$new_message = strtr('lmnopqrstuvwxyzabcdefghijkLMNOPQRSTUVWXYZABCDEFGHIJK','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',$message);` – Steven Moseley Dec 05 '12 at 03:42
27

Easy and better than str_replace:

<?php
$arr = array(
    "http://" => "http://www.",
    "w" => "W",
    "d" => "D");

    $word = "http://desiweb.ir";
    echo strtr($word,$arr);
?>

strtr PHP doc here

T30
  • 11,422
  • 7
  • 53
  • 57
desiweb.ir
  • 281
  • 3
  • 2
  • For those wondering, stristr should be used for scenarios requiring case insensitive application. – Tarquin Feb 28 '17 at 22:45
  • 1
    @Tarquin Not quite - stristr is the case insensitive version of strstr, not strtr (These names!) For those that are wondering like myself, the alternative can be seen at https://stackoverflow.com/questions/7529330/php-case-insensitive-equivalent-of-strtr – MortimerCat Jul 10 '20 at 12:02
3

Alternatively to the answer marked as correct, if you have to replace words instead of chars you can do it with this piece of code :

$query = "INSERT INTO my_table VALUES (?, ?, ?, ?);";
$values = Array("apple", "oranges", "mangos", "papayas");
foreach (array_fill(0, count($values), '?') as $key => $wildcard) {
    $query = substr_replace($query, '"'.$values[$key].'"', strpos($query, $wildcard), strlen($wildcard));
}
echo $query;

Demo here : http://sandbox.onlinephpfunctions.com/code/56de88aef7eece3d199d57a863974b84a7224fd7

TwystO
  • 2,456
  • 2
  • 22
  • 28
  • `substr_replace` with `strpos` is just a longer way of writing `str_replace` with the `$count` parameter set to 1. It has the same problem the question was asking for a solution to. Also, this example is a recipe for SQL injection. – Ry- Dec 13 '18 at 06:01
  • A better approach here is to use `preg_replace` and **regex**. ```foreach($values as $value) { $query = preg_replace('/\\?/', $value, $query, 1); }``` – Erick Maeda May 20 '21 at 23:54
0

If the text is a simple markup and has existing anchors, stage the existing anchor tags first, swap out the urls, then replace the staged markers.

$text = '
Lorem Ipsum is simply dummy text found by searching http://google.com/?q=lorem in your <a href=https://www.mozilla.org/en-US/firefox/>Firefox</a>,
<a href="https://www.apple.com/safari/">Safari</a>, or https://www.google.com/chrome/ browser.

Link replacements will first stage existing anchor tags, replace each with a marker, then swap out the remaining links.
Links should be properly encoded.  If links are not separated from surrounding content like a trailing "." period then they it will be included in the link.
Links that are not encoded properly may create a problem, so best to use this when you know the text you are processing is not mixed HTML.

Example: http://google.com/i,m,complicate--d/index.html
Example: https://www.google.com/chrome/?123&t=123
Example: http://google.com/?q='. urlencode('<a href="http://google.com">http://google.com</a>') .'
';

// Replace existing links with a marker
$linkStore = array();
$text = preg_replace_callback('/(<a.*?a>)/', function($match) use (&$linkStore){ $key = '__linkStore'.count($linkStore).'__'; $linkStore[$key] = $match[0]; return $key; }, $text);

// Replace remaining URLs with an anchor tag
$text = preg_replace_callback("/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/", function($match) use (&$linkStore){ return '<a href="'. $match[0] .'">'. $match[0] .'</a>'; }, $text);

// Replace link markers with original
$text = str_replace(array_keys($linkStore), array_values($linkStore), $text);

echo '<pre>'.$text;
David H.
  • 355
  • 2
  • 9