Как закрыть незакрытые теги HTML? - PullRequest
34 голосов
/ 17 июня 2010

Всякий раз, когда мы выбираем какой-либо пользовательский контент с некоторым редактированием из базы данных или аналогичных источников, мы можем извлечь часть, которая содержит только открывающий тег, но не закрывающий.

Это может помешать текущему макету сайта.

Есть ли способ исправить это на стороне клиента или на стороне сервера?

Ответы [ 8 ]

24 голосов
/ 12 июня 2012

Нашел отличный ответ на этот вопрос:

Используйте PHP 5 и используйте метод loadHTML () объекта DOMDocument.Это автоматически анализирует неправильно сформированный HTML, и последующий вызов saveXML () выведет действительный HTML.Функции DOM можно найти здесь:

http://www.php.net/dom

Использование этого:

$doc = new DOMDocument();
$doc->loadHTML($yourText);
$yourText = $doc->saveHTML();
15 голосов
/ 17 июня 2010

У меня есть решение для php

<?php
    // close opened html tags
    function closetags ( $html )
        {
        #put all opened tags into an array
        preg_match_all ( "#<([a-z]+)( .*)?(?!/)>#iU", $html, $result );
        $openedtags = $result[1];

        #put all closed tags into an array
        preg_match_all ( "#</([a-z]+)>#iU", $html, $result );
        $closedtags = $result[1];
        $len_opened = count ( $openedtags );

        # all tags are closed
        if( count ( $closedtags ) == $len_opened )
        {
            return $html;
        }
        $openedtags = array_reverse ( $openedtags );

        # close tags
        for( $i = 0; $i < $len_opened; $i++ )
        {
            if ( !in_array ( $openedtags[$i], $closedtags ) )
            {
                $html .= "</" . $openedtags[$i] . ">";
            }
            else
            {
                unset ( $closedtags[array_search ( $openedtags[$i], $closedtags)] );
            }
        }
        return $html;
    }
    // close opened html tags
?>

Вы можете использовать эту функцию как

   <?php echo closetags("your content <p>test test"); ?>
15 голосов
/ 17 июня 2010

Вы можете использовать Tidy :

Tidy - это привязка для утилиты очистки и восстановления HTML Tidy, которая позволяет не только чистить и иным образом манипулировать HTML-документами, но ипройти через дерево документа.

или HTMLPurifier

HTML Purifier - это стандартная библиотека HTML-фильтров, написанная на PHP.HTML Purifier не только удалит весь вредоносный код (более известный как XSS) с тщательно проверенным, безопасным, но разрешающим белым списком, но также обеспечит соответствие ваших документов стандартам, чего можно достичь только при полном знании спецификаций W3C.

7 голосов
/ 28 апреля 2016

Для фрагментов HTML и работы с Ответ KJS Я добился успеха со следующим, когда фрагмент имеет один корневой элемент:

$dom = new DOMDocument();
$dom->loadHTML($string);
$body = $dom->documentElement->firstChild->firstChild;
$string = $dom->saveHTML($body);

Без корневого элемента это возможно (но, по-видимому, оболочка только первого текстового дочернего узла в тегах p в text <p>para</p> text):

$dom = new DOMDocument();
$dom->loadHTML($string);
$bodyChildNodes = $dom->documentElement->firstChild->childNodes;

$string = '';
foreach ($bodyChildNodes as $node){
   $string .= $dom->saveHTML($node);
}

Или, еще лучше, из PHP> = 5.4 и libxml> = 2.7.8 (2.7.7 для LIBXML_HTML_NOIMPLIED):

$dom = new DOMDocument();

// Load with no html/body tags and do not add a default dtd
$dom->loadHTML($string, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

$string = $dom->saveHTML();    
6 голосов
/ 17 июня 2010

В дополнение к серверным инструментам, таким как Tidy, вы также можете использовать браузер пользователя, чтобы выполнить некоторые действия для вас.* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * '* *' * '*' * '*' * '' * '' 'В' '1001 *] - это то же самое, что он применяет такое же быстрое восстановление к динамическому контенту, как и к HTML-страницам.Этот код работает довольно хорошо (с двумя оговорками), и на самом деле ничего не записывается на страницу:

var divTemp = document.createElement('div');
divTemp.innerHTML = '<p id="myPara">these <i>tags aren\'t <strong> closed';
console.log(divTemp.innerHTML); 

Предупреждения:

  1. Различные браузеры будут возвращать разные строки,Это не так уж и плохо, за исключением случая IE, который возвращает заглавные теги и удаляет кавычки из атрибутов тегов, которые не проходят проверку.Решением здесь является простая очистка на стороне сервера.Но, по крайней мере, документ будет иметь правильно структурированный XML.

  2. Я подозреваю, что вам может потребоваться задержка перед чтением innerHTML - дайте браузеру возможность переварить строку -- или вы рискуете получить обратно именно то, что было вставлено. Я только что попробовал IE8, и похоже, что строка анализируется сразу, но я не уверен в IE6.Вероятно, было бы лучше прочитать innerHTML после задержки (или бросить его в setTimeout (), чтобы принудительно перевести его в конец очереди).

Я бы порекомендовал вам воспользоваться советом @ Gordon и использовать Tidy, если у вас есть доступ к нему (для его реализации требуется меньше усилий), и если вам это не удастся, используйте innerHTML и напишите свою собственную функцию tidy вPHP.

И хотя это не является частью вашего вопроса, поскольку это касается CMS, рассмотрите возможность использования YUI 2 Rich Text Editor для подобных вещей.Он довольно прост в реализации, несколько прост в настройке, интерфейс очень знаком большинству пользователей и выдает совершенно корректный код.Существует несколько других готовых текстовых редакторов, но у YUI самая лучшая лицензия и самая мощная из всех, что я видел.

3 голосов
/ 14 июля 2014

Лучшая функция PHP для удаления не открытых / не закрытых тегов из webmaster-glossar.de (me)

function closetag($html){
    $html_new = $html;
    preg_match_all ( "#<([a-z]+)( .*)?(?!/)>#iU", $html, $result1);
    preg_match_all ( "#</([a-z]+)>#iU", $html, $result2);
    $results_start = $result1[1];
    $results_end = $result2[1];
    foreach($results_start AS $startag){
        if(!in_array($startag, $results_end)){
            $html_new = str_replace('<'.$startag.'>', '', $html_new);
        }
    }
    foreach($results_end AS $endtag){
        if(!in_array($endtag, $results_start)){
            $html_new = str_replace('</'.$endtag.'>', '', $html_new);
        }
    }
    return $html_new;
}

используйте эту функцию следующим образом:

closetag('i <b>love</b> my <strike>cat'); 
#output: i <b>love</b> my cat

closetag('i <b>love</b> my cat</strike>'); 
#output: i <b>love</b> my cat
0 голосов
/ 08 января 2016

Я использовал собственный метод DOMDocument, но с некоторыми улучшениями по безопасности.

Обратите внимание, что другие ответы, использующие DOMDocument, не учитывают нити html, такие как

This is a <em>HTML</em> strand

. Вышеприведет к

<p>This is a <em>HTML</em> strand

Мое решение ниже

function closeDanglingTags($html) {
    if (strpos($html, '<') || strpos($html, '>')) {
        // There are definitiley HTML tags
        $wrapped = false;
        if (strpos(trim($html), '<') !== 0) {
            // The HTML starts with a text node. Wrap it in an element with an id to prevent the software wrapping it with a <p>
            //  that we know nothing about and cannot safely retrieve
            $html = cHE::getDivHtml($html, null, 'closedanglingtagswrapper');
            $wrapped = true;
        }
        $doc = new DOMDocument();
        $doc->encoding = 'utf-8';
        @$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
        if ($doc->firstChild) {
            // Test whether the firstchild is definitely a DOMDocumentType
            if ($doc->firstChild instanceof DOMDocumentType) {
                // Remove the added doctype
                $doc->removeChild($doc->firstChild);
            }
        }
        if ($wrapped) {
            // The contents originally started with a text node and was wrapped in a div#plasmappclibtextwrap. Take the contents
            //  out of that div
            $node = $doc->getElementById('closedanglingtagswrapper');
            $children = $node->childNodes;  // The contents of the div. Equivalent to $('selector').children()
            $doc = new DOMDocument();   // Create a new document to add the contents to, equiv. to "var doc = $('<html></html>');"
            foreach ($children as $childnode) {
                $doc->appendChild($doc->importNode($childnode, true)); // E.g. doc.append()
            }
        }
        // Remove the added html,body tags
        return trim(str_replace(array('<html><body>', '</body></html>'), '', html_entity_decode($doc->saveHTML())));
    } else {
        return $html;
    }
}
0 голосов
/ 31 марта 2012

Эрик Арвидссон написал хороший парсер HTML SAX в 2004 году. http://erik.eae.net/archives/2004/11/20/12.18.31/

Он отслеживает открытые теги, поэтому с помощью минималистического обработчика SAX можно вставлять закрывающие теги в правильную позицию:

function tidyHTML(html) {
    var output = '';
    HTMLParser(html, {
        comment: function(text) {
            // filter html comments
        },
        chars: function(text) {
            output += text;
        },
        start: function(tagName, attrs, unary) {
            output += '<' + tagName;
            for (var i = 0; i < attrs.length; i++) {
                output += ' ' + attrs[i].name + '=';
                if (attrs[i].value.indexOf('"') === -1) {
                    output += '"' + attrs[i].value + '"';
                } else if (attrs[i].value.indexOf('\'') === -1) {
                    output += '\'' + attrs[i].value + '\'';
                } else { // value contains " and ' so it cannot contain spaces
                    output += attrs[i].value;
                }
            }
            output += '>';
        },
        end: function(tagName) {
            output += '</' + tagName + '>';
        }
    });
    return output;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...