metadige adlı üyeden alıntı: mesajı görüntüle
Arkadaşlar mantık geliştirme açısından güzel ancak öyle substr vs. ile olacak iş değil. Bir üstteki mesajımda belirttiğim gibi preg_match_all ile yapılması en kısa yolu, bir örnek ekliyorum, kendinize göre geliştirirsiniz:

$text = '<h3>başlık3</h3>öylesine yazı<b>bold yazı <p>paragraf';
if(preg_match_all('/(<[^\>\/]*\>)?([^<\>]+)(<\/[^\>\/]*\>)?/smi',$text,$al)){
  $say = count($al[1]);  
  for($i=0;$i<$say;$i++){
    $yeni .= ($al[1][$i] != '') ? $al[1][$i].$al[2][$i].str_replace('<','</',$al[1][$i]) : $al[2][$i];
  }
}  
echo $yeni;
Sanırım benim mesajım arada kaynadı, işe yaradı dediğiniz fonksiyon yukarda örnek verdiğim texti doğru kapatmıyor, <p> tagını <b> içinde gösteriyor. Oysa benim yazdığımda sorun yok. Atladığım bir yer varsa söylersiniz düzeltiriz, çünkü farklı ihtimaller için kafa yormadım. Bu arada belirttiğin gibi alıntı sonunda </ ile başlayan yer kapanacaksa şu şekile çevirmek yeterlidir sanırım:
$text = '<h3>başlık3</h3>öylesine yazı<b>bold yazı <p>paragraf</str';
if(preg_match_all('/(<[^\>\/]*\>)?([^<\>]+)(<\/[^\>\/]*\>)?/smi',$text,$al)){
  $say = count($al[1]);  
  for($i=0;$i<$say;$i++){
    $yeni .= ($al[1][$i] != '') ? $al[1][$i].$al[2][$i].str_replace('<','</',$al[1][$i]) : ((substr($al[2][$i],0,1) != '/') ? $al[2][$i] : '');
  }
}  
echo $yeni;