Android Tablet PC на DHgate.com


PHP – автоматическое закрытие всех открытых HTML тегов


Все знают, что в административной панели сайта хорошо править контент страниц в визуальном редакторе, который имеет название WYSIWYG. Вот и наш редактор как-то странно себя начал вести, перестал закрывать HTML теги (не все, а те, которые ему хочется). Т.к. JS программист в это время был в отпуске нужно было срочно исправить проблему. Пришлось вставлять костыль в виде скрипта на PHP, которые будет автоматом закрыть все незакрытые теги.

Например корявый код:

<a href="http://artkiev.com">Разработка интернет-магазинов</a> <div><br />Лучшие цены <div><div> на </div> раскрутку <b>и оптимизацию <p>сайтов</b>

После обработки получаем:

<a href="http://artkiev.com">Разработка интернет-магазинов</a> <div><br />Лучшие цены <div><div> на </div> раскрутку <b>и оптимизацию <p>сайтов</b></div></div></p>

Функция:

<?
   function close_tags($content)
    {
        $position = 0;
        $open_tags = array();
        //теги для игнорирования
        $ignored_tags = array('br', 'hr', 'img');

        while (($position = strpos($content, '<', $position)) !== FALSE)
        {
            //забираем все теги из контента
            if (preg_match("|^<(/?)([a-z\d]+)\b[^>]*>|i", substr($content, $position), $match))
            {
                $tag = strtolower($match[2]);
                //игнорируем все одиночные теги
                if (in_array($tag, $ignored_tags) == FALSE)
                {
                    //тег открыт
                    if (isset($match[1]) AND $match[1] == '')
                    {
                        if (isset($open_tags[$tag]))
                            $open_tags[$tag]++;
                        else
                            $open_tags[$tag] = 1;
                    }
                    //тег закрыт
                    if (isset($match[1]) AND $match[1] == '/')
                    {
                        if (isset($open_tags[$tag]))
                            $open_tags[$tag]--;
                    }
                }
                $position += strlen($match[0]);
            }
            else
                $position++;
        }
        //закрываем все теги
        foreach ($open_tags as $tag => $count_not_closed)
        {
            $content .= str_repeat("</{$tag}>", $count_not_closed);
        }

        return $content;
    }
?>

Пример использования:

$text = close_tags($_POST['body_page']);

Другие публикации:

Написать комментарий через:

 
               
  • Локальный блог
  •  
 

Ваш отзыв

Имя *

Почта (скрыта) *

Сайт

Напишите цифрами двa вoceмь двa *

Сообщение

Отзывов (2) на «PHP – автоматическое закрытие всех открытых HTML тегов»

  1. Денис пишет:

    Хм.. Странно, но почему-то нигде не нашёл функции, которая открывает неоткрытые теги.. Пришлось модифицировать эту. Ловите, кому надо:
    function open_close_tags($content){
    $position=0;
    $open_tags=array();
    $ignored_tags=array(‘br’, ‘hr’, ‘img’);//теги для игнорирования
    while(($position=strpos($content, ‘<', $position)) !== false){
    if(preg_match("|^]*>|i”, substr($content, $position), $match)){//забираем все теги из контента
    $tag=strtolower($match[2]);
    if(!in_array($tag, $ignored_tags)){//игнорируем все одиночные теги
    if(!isset($open_tags[$tag])){
    $open_tags[$tag]=0;
    }
    if(isset($match[1]) AND $match[1] == ”){//тег открыт
    $open_tags[$tag]++;
    }
    if(isset($match[1]) AND $match[1] == ‘/’)//тег закрыт
    {
    $open_tags[$tag]–;
    }
    }
    $position += strlen($match[0]);
    }else{
    $position++;
    }
    }

    foreach($open_tags as $tag => $count){
    if($count>0){
    $content.=str_repeat(“”, $count);//закрываем незакрытые
    }
    if($count<0){
    $content=str_repeat("”, -$count).$content;//открываем неоткрытые
    }
    }

    return $content;
    }

  2. Дмитрий пишет:

    Классная функция, лучшая из тех что нашёл, tidy для этой задачи уж больно сложен в установке, другие библиотеки, вроде HTMLPurifier уж слишком громоздкие.
    Жаль что она закрывает теги не совсем верно – не по порядку, в итоге блочный элемент перекроет строчный и может поехать вёрстка.
    Также не хватает открытия не открытых тегов, например если есть но нет его открытия, также может поехать вёрстка…
    Для больших текстов оказывается очень медленной из-за работы через str_pos с отступом, большоего числа нарезок текста и частого вызова регулярки.
    Скорость работы можно существенно ускорить, если немного заменить логику работы.

    Немного доработал, возможно кому-то функция окажется полезной:

    // Функция закрывает все не закрытые HTML теги
    function close_tags($content) {

    // Открытые теги
    $open_tags = array();
    // Закрытые теги
    $closed_tags = array();
    // Битые теги (нет закрывающего символа >)
    $broken_tags = array();
    //теги для игнорирования
    $ignored_tags = array(‘br’ => 1, ‘hr’ => 1, ‘img’ => 1);
    // Массив преобразовынных тегов из верхнего регистра в нижний
    $cleaned_tags = array();

    // Ищем сразу все теги в контенте
    preg_match_all(“|?)|ius”, $content, $tag_matches, PREG_OFFSET_CAPTURE);

    // Если найдены теги
    if(!empty($tag_matches)) {
    // Проходим по всем тегам, которые мы нашли
    foreach ($tag_matches[2] as $k => $v) {

    // Если мы ещё не преобразовывали данный тег к нижнему регистру
    if(!isset($cleaned_tags[$tag_matches[2][$k][0]])) {
    // Уменьшаем тег
    $cleaned_tags[$tag_matches[2][$k][0]] = mb_strtolower($tag_matches[2][$k][0]);
    }
    // Используем уменьшенную версию тега из кэша, для ускорения работы функции
    $tag = $cleaned_tags[$tag_matches[2][$k][0]];

    // Игнорируем все одиночные теги
    if(!isset($ignored_tags[$tag])) {
    // Если регулярка нашла тег
    if (isset($tag_matches[1][$k][0])) {
    // Если нет закрывающего тега, значит он содержит ошибку и его нужно полностью удалить из контента.
    if($tag_matches[3][$k][0] == ”) {
    // Дописываем задачу на удаление тега из контента (Странно что мультибайт строки не поддерживаются опцией PREG_OFFSET_CAPTURE TODO проверить в ближайшем будущем не пофиксили ли такое поведение разработчики)
    $broken_tags[] = array($tag_matches[0][$k][0], mb_strlen(substr($content, 0, $tag_matches[0][$k][1])) , mb_strlen($tag_matches[0][$k][0]));
    // Если у тега всего хватает и он похож на валидный TODO сделать проверку на открытие и закрытие кавычек у атрибутов внутри тега
    } else {
    //тег открывается
    if($tag_matches[1][$k][0] == ”) {
    $open_tags[] = $tag;
    // тег закрывается
    } else {
    // По умолчанию считаем, что среди уже найденных открытых тегов не удалось найти тот, который закрывается текущим
    $is_found = 0;
    // Если есть открытые теги
    if(!empty($open_tags)) {
    // Получаем последний открытый тег
    $last = end($open_tags);
    // Если открытый тег был иным, надо понять какой тег закрыли…
    if($tag !== $last) {
    // Определяем количество текущих открытых тегов
    $c = count($open_tags);
    // Проходим по всем открытым тегам в обратном порядке
    for($i=$c-2; $i >= 0; $i–) {
    // Если текущий тег тот же что и закрываемый
    if($open_tags[$i] == $tag) {
    // Удаляем открытый тег из массива
    unset($open_tags[$i]);
    // Проставляем ключи для массива заново, чтобы открытые теги шли по порядку 1,2,3…
    $open_tags = array_values($open_tags);
    // Запоминаем, что тег нашёлся, не нужно будет его дописывать перед контентом
    $is_found = 1;
    break;
    }
    }
    unset($c, $i);
    // Если закрывается последний открытый тег
    } else {
    // Убираем информацию о том, что тег открывался
    array_pop($open_tags);
    // Запоминаем, что тег нашёлся, не нужно будет его дописывать перед контентом
    $is_found = 1;
    }
    }

    // Если не удалось найти тег, который был закрыт, похоже что он не был открыт ранее, нужно будет дописать его в начале контента
    if(!$is_found) {
    $closed_tags[] = ”;
    }
    unset($is_found);
    }
    }
    }
    }
    }
    unset($k, $v);
    }

    // Если есть битые теги
    if(!empty($broken_tags)) {
    // Определяем количество битых тегов
    $c = count($broken_tags);
    // Проходим по всем битым тегам в обратном порядке
    for($i=$c-1; $i >= 0; $i–) {
    // Удаляем из контента битый тег, соединяя 2 части контента до битого тега и после битого тега
    $content = mb_substr($content, 0, $broken_tags[$i][1]) . mb_substr($content, $broken_tags[$i][1] + $broken_tags[$i][2]);
    }
    unset($c, $i);
    }
    unset($broken_tags);

    // Если есть не открытые теги
    if(!empty($closed_tags)) {
    // Теги нужно будет склеивать в обратном порядке, поэтому переворачиваем массив
    $closed_tags = array_reverse($closed_tags);
    // Дописываем открывающие теги в начале контента
    $content = implode(”, $closed_tags) . $content;
    }
    unset($closed_tags);

    // Если есть не закрытые теги
    if(!empty($open_tags)) {
    // Определяем количество текущих открытых тегов
    $c = count($open_tags);
    // Проходим по всем открытым тегам в обратном порядке
    for($i=$c-1; $i >= 0; $i–) {
    // Дописываем закрывающий тег на каждый открытый
    $content .= ”;
    }
    unset($c, $i);
    }
    unset($open_tags);

    return $content;
    }

    Тесты:
    before:<b>тест
    after:<b>тест</b>

    before:<b>тест<span test=’
    after:<b>тест</b>

    before:<b>тест<table></table <span test=’
    after:<b>тест<table></table></b>

    before:<td></td></tr></table <span test=’
    after:<tr><td></td></tr>

    before:<b>тест<table><tr><table><tr><td></td></tr></table <span test=’
    after:<b>тест<table><tr><table><tr><td></td></tr></table></tr></table></b>

    before:<B>тест<tabLe><TR><tableattr=’
    ><tr><td></td></tr></table <span test=’</div>
    after:<div><B>тест<tabLe><TR><tableattr=’
    ><tr><td></td></tr></div></table></tr></table></b>

    before:Интере<b>сный<div> тек<ст
    after:Интере<b>сный<div> тек<ст</div></b>

    before:Интере<b>сный<div> тек<dd attr=’<div>
    after:Интере<b>сный<div> тек<div></div></div></b>

 
Возврат % от покупок
Статусы для соц.сетей на ArtKiev Design Studio