<?php
class HTMLUtils {
public static function closeUnclosedTags($html) {
$aloneTags = array('area', 'base', 'basefont', 'bgsound', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'); $endNameTagSymbols = array('>', ' ', "\n", "\r", "\t"); $openedTagsStack = array(); $currentTag = '';
$insideOpenTag = false;
$insideCloseTag = false;
$waitForTagEnd = false;
$closedBrake = false;
for ($i = 0; $i < strlen($html); $i++) {
if ($html[$i] == '<') {
if (!$insideOpenTag && !$insideCloseTag) {
if ($html[$i + 1] == '/') {
$insideCloseTag = true;
$i++;
} else {
$insideOpenTag = true;
}
}
} elseif ($waitForTagEnd) {
if ($html[$i] == '>') {
$insideOpenTag = false;
$insideCloseTag = false;
$waitForTagEnd = false;
} else continue;
} elseif ($insideOpenTag || $insideCloseTag) {
if (!in_array($html[$i], $endNameTagSymbols)) {
$currentTag .= $html[$i];
} else {
if ($html[$i] == '>') {
$closedBrake = true;
}
if ($insideOpenTag) {
$openedTagsStack[] = $currentTag;
} else {
if (!in_array($currentTag, $openedTagsStack)) {
$i -= strlen($currentTag) + 3;
$closedBrake = true; // костыль
} else {
$lastOpenedTag = array_pop($openedTagsStack); $needToInsert = '';
while ($lastOpenedTag != $currentTag) {
$needToInsert .= '</' . $lastOpenedTag . '>';
$lastOpenedTag = array_pop($openedTagsStack);
}
}
}
$currentTag = '';
if ($closedBrake) {
$insideOpenTag = false;
$insideCloseTag = false;
$closedBrake = false;
} else {
$waitForTagEnd = true;
}
}
} elseif (!$insideOpenTag && !$insideCloseTag && $html[$i] == '>') {
$html = substr($html, 0, $i) . '>' . substr($html, $i + 1); $i += 3;
}
}
if ($insideOpenTag || $insideCloseTag)
if (!empty($openedTagsStack)) {
for ($i = count($openedTagsStack) - 1; $i >= 0; $i--) $html .= '</' . $openedTagsStack[$i] . '>';
}
return $html;
}
// логика - закрывает все теги, закрытые без открытых удаляет
}
$html = '<b class="baka"><!-- --><b top="kek"><i hui><img lal><stro>ng><i>k</b></strong )))></i></stro';
echo $html . "\n";
echo HTMLUtils::closeUnclosedTags($html);
PD9waHAKCiAgICBjbGFzcyBIVE1MVXRpbHMgewogICAgICAgIAogICAgICAgIHB1YmxpYyBzdGF0aWMgZnVuY3Rpb24gY2xvc2VVbmNsb3NlZFRhZ3MoJGh0bWwpIHsKICAgICAgICAgICAgCiAgICAgICAgICAgICRhbG9uZVRhZ3MgPSBhcnJheSgnYXJlYScsICdiYXNlJywgJ2Jhc2Vmb250JywgJ2Jnc291bmQnLCAnYnInLCAnY29sJywgJ2NvbW1hbmQnLCAnZW1iZWQnLCAnaHInLCAnaW1nJywgJ2lucHV0JywgJ2lzaW5kZXgnLCAna2V5Z2VuJywgJ2xpbmsnLCAnbWV0YScsICdwYXJhbScsICdzb3VyY2UnLCAndHJhY2snLCAnd2JyJyk7CiAgICAgICAgICAgICRlbmROYW1lVGFnU3ltYm9scyA9IGFycmF5KCc+JywgJyAnLCAiXG4iLCAiXHIiLCAiXHQiKTsKICAgICAgICAgICAgJG9wZW5lZFRhZ3NTdGFjayA9IGFycmF5KCk7CiAgICAgICAgICAgICRjdXJyZW50VGFnID0gJyc7CiAgICAgICAgICAgICRpbnNpZGVPcGVuVGFnID0gZmFsc2U7CiAgICAgICAgICAgICRpbnNpZGVDbG9zZVRhZyA9IGZhbHNlOwogICAgICAgICAgICAkd2FpdEZvclRhZ0VuZCA9IGZhbHNlOwogICAgICAgICAgICAkY2xvc2VkQnJha2UgPSBmYWxzZTsKICAgICAgICAgICAgCiAgICAgICAgICAgICRodG1sID0gcHJlZ19yZXBsYWNlKCclPCEtW14+XSotPiUnLCAnJywgJGh0bWwpOwogICAgICAgICAgICAKICAgICAgICAgICAgZm9yICgkaSA9IDA7ICRpIDwgc3RybGVuKCRodG1sKTsgJGkrKykgewogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICBpZiAoJGh0bWxbJGldID09ICc8JykgewogICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgIGlmICghJGluc2lkZU9wZW5UYWcgJiYgISRpbnNpZGVDbG9zZVRhZykgewogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCRodG1sWyRpICsgMV0gPT0gJy8nKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkaW5zaWRlQ2xvc2VUYWcgPSB0cnVlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgJGkrKzsKICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICRpbnNpZGVPcGVuVGFnID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICB9IGVsc2VpZiAoJHdhaXRGb3JUYWdFbmQpIHsKICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICBpZiAoJGh0bWxbJGldID09ICc+JykgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAkaW5zaWRlT3BlblRhZyA9IGZhbHNlOwogICAgICAgICAgICAgICAgICAgICAgICAkaW5zaWRlQ2xvc2VUYWcgPSBmYWxzZTsKICAgICAgICAgICAgICAgICAgICAgICAgJHdhaXRGb3JUYWdFbmQgPSBmYWxzZTsKICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgfSBlbHNlaWYgKCRpbnNpZGVPcGVuVGFnIHx8ICRpbnNpZGVDbG9zZVRhZykgewogICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgIGlmICghaW5fYXJyYXkoJGh0bWxbJGldLCAkZW5kTmFtZVRhZ1N5bWJvbHMpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAkY3VycmVudFRhZyAuPSAkaHRtbFskaV07CiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICBpZiAoJGh0bWxbJGldID09ICc+JykgewogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICRjbG9zZWRCcmFrZSA9IHRydWU7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCRpbnNpZGVPcGVuVGFnKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghaW5fYXJyYXkoJGN1cnJlbnRUYWcsICRhbG9uZVRhZ3MpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRvcGVuZWRUYWdzU3RhY2tbXSA9ICRjdXJyZW50VGFnOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghaW5fYXJyYXkoJGN1cnJlbnRUYWcsICRvcGVuZWRUYWdzU3RhY2spKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJGh0bWwgPSBzdWJzdHIoJGh0bWwsIDAsICRpIC0gc3RybGVuKCRjdXJyZW50VGFnKSAtIDIpIC4gc3Vic3RyKCRodG1sLCBzdHJwb3MoJGh0bWwsICc+JywgJGkpICsgMSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJGkgLT0gc3RybGVuKCRjdXJyZW50VGFnKSArIDM7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJGNsb3NlZEJyYWtlID0gdHJ1ZTsgLy8g0LrQvtGB0YLRi9C70YwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRsYXN0T3BlbmVkVGFnID0gYXJyYXlfcG9wKCRvcGVuZWRUYWdzU3RhY2spOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRuZWVkVG9JbnNlcnQgPSAnJzsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGlsZSAoJGxhc3RPcGVuZWRUYWcgIT0gJGN1cnJlbnRUYWcpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRuZWVkVG9JbnNlcnQgLj0gJzwvJyAuICRsYXN0T3BlbmVkVGFnIC4gJz4nOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkbGFzdE9wZW5lZFRhZyA9IGFycmF5X3BvcCgkb3BlbmVkVGFnc1N0YWNrKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRodG1sID0gc3Vic3RyKCRodG1sLCAwLCAkaSAtIHN0cmxlbigkY3VycmVudFRhZykgLSAyKSAuICRuZWVkVG9JbnNlcnQgLiBzdWJzdHIoJGh0bWwsICRpIC0gc3RybGVuKCRjdXJyZW50VGFnKSAtIDIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICRpICs9IHN0cmxlbigkbmVlZFRvSW5zZXJ0KTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAkY3VycmVudFRhZyA9ICcnOwogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCRjbG9zZWRCcmFrZSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICRpbnNpZGVPcGVuVGFnID0gZmFsc2U7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkaW5zaWRlQ2xvc2VUYWcgPSBmYWxzZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICRjbG9zZWRCcmFrZSA9IGZhbHNlOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICR3YWl0Rm9yVGFnRW5kID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIH0gZWxzZWlmICghJGluc2lkZU9wZW5UYWcgJiYgISRpbnNpZGVDbG9zZVRhZyAmJiAkaHRtbFskaV0gPT0gJz4nKSB7CiAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgJGh0bWwgPSBzdWJzdHIoJGh0bWwsIDAsICRpKSAuICcmZ3Q7JyAuIHN1YnN0cigkaHRtbCwgJGkgKyAxKTsKICAgICAgICAgICAgICAgICAgICAkaSArPSAzOwogICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgIH0KICAgICAgICAgICAgCiAgICAgICAgICAgIGlmICgkaW5zaWRlT3BlblRhZyB8fCAkaW5zaWRlQ2xvc2VUYWcpCiAgICAgICAgICAgICAgICAkaHRtbCA9IHN1YnN0cigkaHRtbCwgMCwgc3RycnBvcygkaHRtbCwgJzwnKSk7CiAgICAgICAgICAgIAogICAgICAgICAgICBpZiAoIWVtcHR5KCRvcGVuZWRUYWdzU3RhY2spKSB7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIGZvciAoJGkgPSBjb3VudCgkb3BlbmVkVGFnc1N0YWNrKSAtIDE7ICRpID49IDA7ICRpLS0pCiAgICAgICAgICAgICAgICAgICAgJGh0bWwgLj0gJzwvJyAuICRvcGVuZWRUYWdzU3RhY2tbJGldIC4gJz4nOwogICAgICAgICAgICAgICAgCiAgICAgICAgICAgIH0KICAgICAgICAgICAgCiAgICAgICAgICAgIHJldHVybiAkaHRtbDsKICAgICAgICAgICAgCiAgICAgICAgfQogICAgICAgIC8vINC70L7Qs9C40LrQsCAtINC30LDQutGA0YvQstCw0LXRgiDQstGB0LUg0YLQtdCz0LgsINC30LDQutGA0YvRgtGL0LUg0LHQtdC3INC+0YLQutGA0YvRgtGL0YUg0YPQtNCw0LvRj9C10YIKICAgICAgICAKICAgIH0KICAgIAogICAgJGh0bWwgPSAnPGIgY2xhc3M9ImJha2EiPjwhLS0gLS0+PGIgdG9wPSJrZWsiPjxpIGh1aT48aW1nIGxhbD48c3Rybz5uZz48aT5rPC9iPjwvc3Ryb25nICkpKT48L2k+PC9zdHJvJzsKICAgIAogICAgZWNobyAkaHRtbCAuICJcbiI7CiAgICAKICAgIGVjaG8gSFRNTFV0aWxzOjpjbG9zZVVuY2xvc2VkVGFncygkaHRtbCk7