29
$string = "<tag>i dont know what is here</tag>"
$string = str_replace("???", "<tag></tag>", $string);
echo $string; // <tag></tag>

So what code am i looking for?

Tom Walters
  • 15,366
  • 7
  • 57
  • 74
faq
  • 2,965
  • 5
  • 27
  • 35

8 Answers8

34

A generic function:

function replace_between($str, $needle_start, $needle_end, $replacement) {
    $pos = strpos($str, $needle_start);
    $start = $pos === false ? 0 : $pos + strlen($needle_start);

    $pos = strpos($str, $needle_end, $start);
    $end = $pos === false ? strlen($str) : $pos;

    return substr_replace($str, $replacement, $start, $end - $start);
}

DEMO

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 3
    better than the accepted answer. no need to use a regex replace unless you need one. – user428517 Jun 22 '15 at 21:58
  • @sgroves Regex is a reasonable tool when it avoids code convolution or provides another sensible advantage. – mickmackusa Jun 27 '18 at 02:50
  • 1
    I tried this in PHP 7 but no result was echoed, it seems it is also stripping out the tags - also I wonder if there is an option for all occurrences, ie more than 1 set of tags with text between – kerry Apr 16 '19 at 02:50
26
$search = "/[^<tag>](.*)[^<\/tag>]/";
$replace = "your new inner text";
$string = "<tag>i dont know what is here</tag>";
echo preg_replace($search,$replace,$string);

outputs:

<tag>your new inner text</tag>
rlemon
  • 17,518
  • 14
  • 92
  • 123
  • I'm assuming you want to replace the contents of the tags, and not just replace the entire string. – rlemon Jul 29 '11 at 16:11
  • Should use lookaround instead, to be honest. – Steve Wang Jul 29 '11 at 16:59
  • Running this code as is I get `Warning: preg_replace() [function.preg-replace]: Unknown modifier 't' on line 1` – anthonygore Apr 18 '14 at 04:01
  • I'd add a `?` to the capture group, `(.*?)` to make sure the wildcard operator doesn't get to greedy and stops and the end tag next end tag. – mfink Oct 06 '17 at 18:43
  • 3
    This answer is 100% incorrect. It is only happenstance that it provides the desired output. Watch this answer fail when the input changes slightly. https://3v4l.org/p507L This is an incorrect use of negated character classes and it is misinforming researchers. @faq – mickmackusa Nov 19 '19 at 20:27
  • 4
    If you feel there is a better answer provide it, but I would be far from saying this answer is "100% incorrect" when it does exactly what it needs to do at the time it was written (and OP clearly agreed). – rlemon Nov 19 '19 at 21:44
  • The accepted answer is incorrect because it works only for the given example. For an input like this: $string = "BEGIN i dont know what is here MIDDLE i dont know what is here END"; you get a wrong result. For a correct solution you should use this code: $search = "/[^](.*)[^<\/tag>]/"; $search = '#().*?()#'; $replace = "new text"; $string = "BEGIN inside tag MIDDLE second tag END"; echo preg_replace($search, '$1'.$replace.'$2', $string); Link to code: https://3v4l.org/pD61r – Sergiu Sandrean Apr 13 '20 at 12:20
  • How to handle it for case insensitive? For example and – DSP Apr 22 '20 at 07:28
14
$string = "<tag>I do not know what is here</tag>";
$new_text = 'I know now'; 
echo preg_replace('#(<tag.*?>).*?(</tag>)#', '$1'.$new_text.'$2' , $string); //<tag>I know now</tag>
Fivell
  • 11,829
  • 3
  • 61
  • 99
4

A generic and non-regex solution:

I've modified @felix-kling's answer. Now it only replaces text if it finds the needles.

Also, I've added parameters for replacing the needles, starting position and replacing all the matches.

I've used the mb_ functions for making the function multi-byte safe. If you need a case insensitive solution then replace mb_strpos calls with mb_stripos.

function replaceBetween($string, $needleStart, $needleEnd, $replacement,
                        $replaceNeedles = false, $startPos = 0, $replaceAll = false) {
    $posStart = mb_strpos($string, $needleStart, $startPos);

    if ($posStart === false) {
        return $string;
    }

    $start = $posStart + ($replaceNeedles ? 0 : mb_strlen($needleStart));
    $posEnd = mb_strpos($string, $needleEnd, $start);

    if ($posEnd === false) {
        return $string;
    }

    $length = $posEnd - $start + ($replaceNeedles ? mb_strlen($needleEnd) : 0);

    $result = substr_replace($string, $replacement, $start, $length);

    if ($replaceAll) {
        $nextStartPos = $start + mb_strlen($replacement) + mb_strlen($needleEnd);

        if ($nextStartPos >= mb_strlen($string)) {
            return $result;
        }

        return replaceBetween($result, $needleStart, $needleEnd, $replacement, $replaceNeedles, $nextStartPos, true);
    }

    return $result;
}
$string = "{ Some} how it {is} here{";

echo replaceBetween($string, '{', '}', '(hey)', true, 0, true); // (hey) how it (hey) here{
Caner
  • 329
  • 3
  • 4
4

If "tag" changes:

$string = "<tag>i dont know what is here</tag>";
$string = preg_replace('|^<([a-z]*).*|', '<$1></$1>', $string)
echo $string; // <tag></tag>
labue
  • 2,605
  • 3
  • 26
  • 35
3

If you don't know what's inside the <tag> tag, it's possible there is another <tag> tag in there e.g.

<tag>something<tag>something else</tag></tag>

And so a generic string replace function won't do the job.

A more robust solution is to treat the string as XML and manipulate it with DOMDocument. Admittedly this only works if the string is valid as XML, but I still think it's a better solution than a string replace.

$string = "<tag>i don't know what is here</tag>";
$replacement = "replacement";

$doc = new DOMDocument();
$doc->loadXML($str1);
$node = $doc->getElementsByTagName('tag')->item(0);
$newNode = $doc->createElement("tag", $replacement); 
$node->parentNode->replaceChild($newNode, $node);
echo $str1 = $doc->saveHTML($node); //output: <tag>replacement</tag>
anthonygore
  • 4,722
  • 4
  • 31
  • 30
  • This almost a working answer. I made some adjustments. https://3v4l.org/phEOf Overall, if DomDocument can be used, it should be used for stability. – mickmackusa Nov 19 '19 at 12:07
3
$string = "<tag>i dont know what is here</tag>"
$string = "<tag></tag>";
echo $string; // <tag></tag>

or just?

$string = str_replace($string, "<tag></tag>", $string);

Sorry, could not resist. Maybe you update your question with a few more details. ;)

Phil
  • 10,948
  • 17
  • 69
  • 101
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Totally unnecessary to put such words into an answer. A comment would have done it too. –  Oct 05 '22 at 04:11
2

If you need to replace the portion too then this function is helpful:

$var = "Nate";

$body = "Hey there {firstName} have you already completed your purchase?";

$newBody = replaceVariable($body,"{","}",$var);

echo $newBody;

function replaceVariable($body,$needleStart,$needleEnd,$replacement){
  while(strpos($body,$needleStart){
      $start = strpos($body,$needleStart);
      $end = strpos($body,$needleEnd);
      $body = substr_replace($body,$replacement,$start,$end-$start+1);
  }
  return $body;
}

I had to replace a variable put into a textarea that was submitted. So I replaced firstName with Nate (including the curly braces).

Nate S
  • 163
  • 3
  • 13
  • This works great, without regular expressions. But only if you want to replace only one occurence of the string. If you need to replace multiple occurence, you must do a loop until strpos will return FALSE (which means there isn't any occurence to replace any more. – Julien Fastré Jan 23 '19 at 22:00
  • **Note**: this expression `strpos($body,$needleEnd);` would start the search from the beginning of the string. To search from the start needle you will need an offset: `strpos($body,$needleEnd, $needleStart);`. – AlexSp3 Sep 27 '21 at 21:30