Code SnippetsClose HTML Tags Syndicate content

Wed, 07/19/2006 - 14:26

If you cut a html-formatted string at some random position (e.g. with my truncate() function) you might mess up the html. To circumvent that, this function will close all open tags at the end of the string:

The original function was written by connum at DONOTSPAMME dot googlemail dot com

  1. /**
  2.  * close all open xhtml tags at the end of the string
  3.  *
  4.  * @param string $html
  5.  * @return string
  6.  * @author Milian Wolff <mail@milianw.de>
  7.  */
  8. function closetags($html) {
  9. #put all opened tags into an array
  10. preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
  11. $openedtags = $result[1];
  12.  
  13. #put all closed tags into an array
  14. preg_match_all('#</([a-z]+)>#iU', $html, $result);
  15. $closedtags = $result[1];
  16. $len_opened = count($openedtags);
  17. # all tags are closed
  18. if (count($closedtags) == $len_opened) {
  19. return $html;
  20. }
  21. $openedtags = array_reverse($openedtags);
  22. # close tags
  23. for ($i=0; $i < $len_opened; $i++) {
  24. if (!in_array($openedtags[$i], $closedtags)){
  25. $html .= '</'.$openedtags[$i].'>';
  26. } else {
  27. unset($closedtags[array_search($openedtags[$i], $closedtags)]);
  28. }
  29. }
  30. return $html;
  31. }

Comments

Hi, thanks all for Fri, 03/18/2011 - 14:11 — Volo Kulpa (not verified)

  1. Hi, thanks all for comments
  2. Here are some my fixes:
  3. * in_array is case sensitive so convert opened tag to lower case
  4. * checking if next_tag exists to prevent offset error

<code>geshi:code|&#10;# close tags&#10;for ($i=0; $i &lt; $len_opened; $i++) {&#10; $openedtags[$i] = strtolower($openedtags[$i]);&#10; if (!in_array($openedtags[$i],$arr_single_tags)) {&#10; if (!in_array($openedtags[$i], $closedtags)) {&#10; $next_tag = (($i + 1) &lt; count($openedtags) ? strtolower($openedtags[$i+1]) : null);&#10; if ($next_tag &amp;&amp; !in_array($next_tag, $arr_single_tags)) {&#10; $html = preg_replace(&#039;#&lt;/&#039;.$next_tag.&#039;#iU&#039;,&#039;&lt;/&#039;.$openedtags[$i].&#039;&gt;&lt;/&#039;.$next_tag,$html);&#10; } else {&#10; $html .= &#039;&lt;/&#039;.$openedtags[$i].&#039;&gt;&#039;;&#10; }&#10; }&#10; }&#10;}&#10;|code</code>

Ich denke, falls alle Mon, 11/19/2012 - 01:03 — San Andreas Multiplayer (not verified)

Ich denke, falls alle Schreiber und Blogger so einen super Inhalt wie du in das Blog stellen würden, gaebe es so viel mehr Sinnvolles zu finden.

A small fix: I tried to Wed, 01/05/2011 - 23:25 — Plasmodino (not verified)

A small fix: I tried to ignore closing tags which do not have an opening tag, but it does not work the way I did it. The following code only works if the html syntax in the truncated string is correct:

  1. static function closeTags($html) {
  2. preg_match_all("/<\/?(\w+)((\s+(\w|\w[\w-]*\w)(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/i",$html,$result);
  3. $tags = &$result[0]; $closeCnt = 0;
  4. for ($i=count($tags)-1;$i>=0;$i--) {
  5. if ($tags[$i]{strlen($tags[$i])-2}!='/') {
  6. if ($tags[$i]{1}!='/') {
  7. if (!$closeCnt) $html .= '</'.$result[1][$i].'>'; else $closeCnt--;
  8. } else $closeCnt++;
  9. }
  10. }
  11. return $html;
  12. }

I suggest the following code Wed, 01/05/2011 - 22:47 — Plasmodino (not verified)

I suggest the following code for closing open tags:

  1. static function closeTags($html) {
  2. preg_match_all("/<\/?(\w+)((\s+(\w|\w[\w-]*\w)(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/i",$html,$result);
  3. $tags = &$result[0]; $closeCnt = 0;
  4. for ($i=count($tags)-1;$i>=0;$i--) {
  5. if ($tags[$i]{strlen($tags[$i])-2}!='/') {
  6. if ($tags[$i]{1}!='/') {
  7. if (!$closeCnt) $html .= '</'.$result[1][$i].'>'; else $closeCnt--;
  8. } elseif ($i>0&&$result[1][$i]==$result[1][$i-1]) $closeCnt++;
  9. }
  10. }
  11. return $html;
  12. }
I did not test it much, though.

I suggest the following code Wed, 01/05/2011 - 22:29 — Plasmodino (not verified)

I suggest the following code for closing open tags:

  1. static function closeTags($html) {
  2. preg_match_all("/<\/?(\w+)((\s+(\w|\w[\w-]*\w)(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/i",$html,$result);
  3. $tags = &$result[0]; $closeCnt = 0;
  4. for ($i=count($tags)-1;$i>=0;$i--) {
  5. if ($tags[$i]{strlen($tags[$i])-2}!='/') {
  6. if ($tags[$i]{1}!='/') {
  7. if (!$closeCnt) $html .= '</'.$result[1][$i].'>'; else $closeCnt--;
  8. } elseif ($i>0&&$result[1][$i]==$result[1][$i-1]) $closeCnt++;
  9. }
  10. }
  11. return $html;
  12. }

I did not test it much yet, though.

This code (including all Tue, 10/19/2010 - 18:49 — Anonymous (not verified)

This code (including all following postings) has still a lot of problems. For example, it can not close that - the 2nd span will not be closed. <span>fjlfsdlkf <span>123 </span>

Little modified for closing Fri, 09/24/2010 - 12:37 — Anonymous (not verified)

Little modified for closing all the opened tags

Like incase if we have “<i><i>rr<b>rrr <i> dddd</i> <i> dddddd</i> <i>ssss”

Output would be : <i><i>rr<b>rrr <i> dddd</i> <i> dddddd</i> <i>ssss</i></i></b></i>

  1. /**
  2. * closetags
  3. * used to close html tags incase not closed properly
  4. * @param string $html - html string
  5. * @access public
  6. */
  7. function closetags($html){
  8. $arr_single_tags = array('meta','img','br','link','area');
  9. preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])\s*>#iU', $html, $result);
  10. $openedtags = $result[1];
  11. preg_match_all('#</([a-z]+)>#iU', $html, $result);
  12. $closedtags = $result[1];
  13. $len_opened = count($openedtags);
  14. if (count($closedtags) == $len_opened){
  15. return $html;
  16. }
  17. $openedtags = array_reverse($openedtags);
  18. //re arrange open tags and closed tags for count
  19. $aOpenedtagsCnt=Array();
  20. $aClosedtagsCnt=Array();
  21. if(is_array($openedtags)){
  22. foreach($openedtags as $iK =>$sTag){
  23. if(!isset($aOpenedtagsCnt[$sTag])){
  24. $aOpenedtagsCnt[$sTag]=1;
  25. }else{
  26. $aOpenedtagsCnt[$sTag]++;
  27. }
  28. }
  29. }
  30. if(is_array($closedtags)){
  31. foreach($closedtags as $iK =>$sTag){
  32. if(!isset($aClosedtagsCnt[$sTag])){
  33. $aClosedtagsCnt[$sTag]=1;
  34. }else{
  35. $aClosedtagsCnt[$sTag]++;
  36. }
  37. }
  38. }
  39. for ($i=0; $i < $len_opened; $i++){
  40. if (!in_array($openedtags[$i],$arr_single_tags)){
  41. if ($aOpenedtagsCnt[$openedtags[$i]]!=$aClosedtagsCnt[$openedtags[$i]]){
  42. $html .= '</'.$openedtags[$i].'>';
  43. if(!isset($aClosedtagsCnt[$openedtags[$i]])){
  44. $aClosedtagsCnt[$openedtags[$i]]=1;
  45. }else{
  46. $aClosedtagsCnt[$openedtags[$i]]++;
  47. }
  48. }
  49. }
  50. }
  51. return $html;
  52. }

I would just like to point Thu, 07/08/2010 - 15:12 — Kim Steinhaug (not verified)

I would just like to point out that all of theese snippets for some reasons forgets to account for the most obvious error of them all, closing the H[1-6] tags! Breaking a page in the middle of a H1 tags has enormous impact of the rest of the page!

This simple code:

<h1>h1</h1> <p>p</p> <h1>

Does not work! Both the original and the updated versions for some reason does not fix this simple code. The second h1 will not get closed since its already closed the first time…

regards, Kim Steinhaug http://www.steinhaug.com/

this is quite good, and work Sun, 06/06/2010 - 12:41 — Web Dev (not verified)

this is quite good, and work for general tag closing, although I have managed to trip it in complex XHTML - thanks for sharing, I think this is good foundation for future work.

Try this! function Sat, 03/20/2010 - 00:44 — Berto (not verified)

Try this!

  1. function closetags($html) {
  2. preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
  3. $openedtags = $result[1];
  4. preg_match_all('#</([a-z]+)>#iU', $html, $result);
  5. $closedtags = $result[1];
  6. $len_opened = count($openedtags);
  7. if (count($closedtags) == $len_opened) {
  8. return $html;
  9. }
  10. $openedtags = array_reverse($openedtags);
  11. for ($i=0; $i < $len_opened; $i++) {
  12. if (!in_array($openedtags[$i], $closedtags)) {
  13. $html .= '</'.$openedtags[$i].'>';
  14. } else {
  15. unset($closedtags[array_search($openedtags[$i], $closedtags)]);
  16. }
  17. }
  18. return $html;
  19. }

sssssssssssssssssssssssssssss Thu, 04/15/2010 - 21:09 — ssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfaaa (not verified)

ssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgssssssssssssssssssssssssssssssssssssssssssfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfgfg

hey, nice snipe thax for Sat, 03/13/2010 - 07:20 — Hack MLx (not verified)

hey, nice snipe thax for sharing.. but how do i make it work, where do i put because i need it for a script that a i added to a post body…

stripslashes(substr($info[‘post_body’],0,600));

i added that to make like a lil “continue reading” script i could found a plug in, i found some just for wordpress, so yea where do i need to add the function, thanks…

i wait for ur answer… thank you

Hi. Can you help me with such Thu, 02/12/2009 - 02:22 — Anonymous (not verified)

Hi. Can you help me with such problem - I truncate the html (to make a teaser from it) and your code helps to close all the tags, but I ecoutered a problem when a tag is cut in the middle (for example - instead of “<p>” on the end I have “<p” only. How can I add the “>” when it’s needed?

The easiest would be to check Thu, 02/12/2009 - 15:52 — Milian Wolff

The easiest would be to check that by an additional regexp. Something like:

  1. ...
  2. $html = preg_replace('#<[a-z]+[^>]*?$#', '', $html);
  3. return $html;

Note: I did not test this, but the idea should be clear.

Thanks, Milian, for the code. Wed, 01/07/2009 - 00:29 — Alexandre de Oliveira (not verified)

Thanks, Milian, for the code. The code posted by Martin, however, doesn’t work well.

In the following string, it will not work:

  1. <div><p>Some text

It will close div but not p. That’s why the code tries to replace the $next-tag from the string, which doesn’t exist because it’s the end of the string and the div tag hasn’t been closed yet.

I made a work around, but have not checked extensively, I just need this quickly, so I have no time. Replace this

  1. $html = preg_replace('#</'.$next_tag.'#iU','</'.$openedtags[$i].'></'.$next_tag,$html);

by this

  1. $tmp_html = $html;
  2. $html = preg_replace('#</'.$next_tag.'#iU','</'.$openedtags[$i].'></'.$next_tag,$html);
  3. if($html == $tmp_html){ //if it did not replace, do it now
  4. $html .= '</'.$openedtags[$i].'>';
  5. }

I hope someone improves the code.

See you.

Hi Milian, nice snippet, but Wed, 04/02/2008 - 16:05 — Martin (not verified)

Hi Milian,

nice snippet, but it does not work always… e.g. if you got the following construct: <table><tr><td>asdasd</tr> I also added the standalone tag option for e.g. <img /> or <br /> I modified your code a little like this:

  1. function closetags($html)
  2. {
  3. $arr_single_tags = array('meta','img','br','link','area');
  4.  
  5. preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
  6. $openedtags = $result[1];
  7.  
  8. preg_match_all('#</([a-z]+)>#iU', $html, $result);
  9.  
  10. $closedtags = $result[1];
  11.  
  12. $len_opened = count($openedtags);
  13.  
  14. if (count($closedtags) == $len_opened)
  15. {
  16. return $html;
  17. }
  18.  
  19. $openedtags = array_reverse($openedtags);
  20.  
  21. for ($i=0; $i < $len_opened; $i++)
  22. {
  23. if (!in_array($openedtags[$i],$arr_single_tags))
  24. {
  25. if (!in_array($openedtags[$i], $closedtags))
  26. {
  27. if ($next_tag = $openedtags[$i+1])
  28. {
  29. $html = preg_replace('#</'.$next_tag.'#iU','</'.$openedtags[$i].'></'.$next_tag,$html);
  30. }
  31. else
  32. {
  33. $html .= '</'.$openedtags[$i].'>';
  34. }
  35. }
  36. }
  37. }
  38.  
  39. return $html;
  40. }

Now it returns: <table><tr><td>asdasd</td></tr></table>

Hi Martin! Great that my Sat, 04/05/2008 - 12:54 — Milian Wolff

Hi Martin! Great that my script is of at least some help.

I just wanted to add that you are missing hr and input in your $arr_single_tags array.

Hi Milian, thanks for your Sat, 06/16/2007 - 12:12 — Michael (not verified)

Hi Milian,

thanks for your code snippet. However, there are two issues:

  1. The function fails on self-closing tags, i. e. <img />. For example, <img src="foo.png" />, will become <img src="foo.png">...more.<em>html</em>...</img>. I solved this by adding a switch in the FOR loop (case "img":...break; default: your original code).

  2. Is it possible to change the actual closing behavior? For example: Not close at the end of $html, but close directly after the “false” opening tag occurs; or: remove the “false” opening tag from $html? I couldn’t build the necessary preg_replace myself, but maybe the for-loop can be used via strpos and friend?

Schöne Grüße aus dem Süden der Republik!

Post new comment

  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <pre>.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options