fork download
  1. <?php
  2.  
  3. class HTMLUtils {
  4.  
  5. public static function closeUnclosedTags($html) {
  6.  
  7. $aloneTags = array('area', 'base', 'basefont', 'bgsound', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr');
  8. $endNameTagSymbols = array('>', ' ', "\n", "\r", "\t");
  9. $openedTagsStack = array();
  10. $currentTag = '';
  11. $insideOpenTag = false;
  12. $insideCloseTag = false;
  13. $waitForTagEnd = false;
  14. $closedBrake = false;
  15.  
  16. $html = preg_replace('%<!-[^>]*->%', '', $html);
  17.  
  18. for ($i = 0; $i < strlen($html); $i++) {
  19.  
  20. if ($html[$i] == '<') {
  21.  
  22. if (!$insideOpenTag && !$insideCloseTag) {
  23.  
  24. if ($html[$i + 1] == '/') {
  25. $insideCloseTag = true;
  26. $i++;
  27. } else {
  28. $insideOpenTag = true;
  29. }
  30.  
  31. }
  32.  
  33. } elseif ($waitForTagEnd) {
  34.  
  35. if ($html[$i] == '>') {
  36.  
  37. $insideOpenTag = false;
  38. $insideCloseTag = false;
  39. $waitForTagEnd = false;
  40.  
  41. } else continue;
  42.  
  43. } elseif ($insideOpenTag || $insideCloseTag) {
  44.  
  45. if (!in_array($html[$i], $endNameTagSymbols)) {
  46.  
  47. $currentTag .= $html[$i];
  48.  
  49. } else {
  50.  
  51. if ($html[$i] == '>') {
  52.  
  53. $closedBrake = true;
  54.  
  55. }
  56.  
  57. if ($insideOpenTag) {
  58.  
  59. if (!in_array($currentTag, $aloneTags))
  60. $openedTagsStack[] = $currentTag;
  61.  
  62. } else {
  63.  
  64. if (!in_array($currentTag, $openedTagsStack)) {
  65.  
  66. $html = substr($html, 0, $i - strlen($currentTag) - 2) . substr($html, strpos($html, '>', $i) + 1);
  67. $i -= strlen($currentTag) + 3;
  68.  
  69. $closedBrake = true; // костыль
  70.  
  71. } else {
  72.  
  73. $lastOpenedTag = array_pop($openedTagsStack);
  74. $needToInsert = '';
  75.  
  76. while ($lastOpenedTag != $currentTag) {
  77.  
  78. $needToInsert .= '</' . $lastOpenedTag . '>';
  79. $lastOpenedTag = array_pop($openedTagsStack);
  80.  
  81. }
  82.  
  83. $html = substr($html, 0, $i - strlen($currentTag) - 2) . $needToInsert . substr($html, $i - strlen($currentTag) - 2);
  84.  
  85. $i += strlen($needToInsert);
  86.  
  87. }
  88.  
  89. }
  90.  
  91. $currentTag = '';
  92.  
  93. if ($closedBrake) {
  94.  
  95. $insideOpenTag = false;
  96. $insideCloseTag = false;
  97. $closedBrake = false;
  98.  
  99. } else {
  100.  
  101. $waitForTagEnd = true;
  102.  
  103. }
  104.  
  105. }
  106.  
  107.  
  108.  
  109. } elseif (!$insideOpenTag && !$insideCloseTag && $html[$i] == '>') {
  110.  
  111. $html = substr($html, 0, $i) . '&gt;' . substr($html, $i + 1);
  112. $i += 3;
  113.  
  114. }
  115.  
  116. }
  117.  
  118. if ($insideOpenTag || $insideCloseTag)
  119. $html = substr($html, 0, strrpos($html, '<'));
  120.  
  121. if (!empty($openedTagsStack)) {
  122.  
  123. for ($i = count($openedTagsStack) - 1; $i >= 0; $i--)
  124. $html .= '</' . $openedTagsStack[$i] . '>';
  125.  
  126. }
  127.  
  128. return $html;
  129.  
  130. }
  131. // логика - закрывает все теги, закрытые без открытых удаляет
  132.  
  133. }
  134.  
  135. $html = '<b class="baka"><!-- --><b top="kek"><i hui><img lal><stro>ng><i>k</b></strong )))></i></stro';
  136.  
  137. echo $html . "\n";
  138.  
  139. echo HTMLUtils::closeUnclosedTags($html);
Success #stdin #stdout 0.02s 52432KB
stdin
Standard input is empty
stdout
<b class="baka"><!-- --><b top="kek"><i hui><img lal><stro>ng><i>k</b></strong )))></i></stro
<b class="baka"><b top="kek"><i hui><img lal><stro>ng&gt;<i>k</i></stro></i></b></b>