Обрезка текста в формате HTML с помощью SMARTY

У меня есть переменная, отформатированная случайным HTML-кодом. Я называю это {$text} и усекаю его.

Значение, например:

<div>Lorem <i>ipsum <b>dolor <span>sit </span>amet</b>, con</i> elit.</div>

Если я обрежу первые ~ 30 букв текста, я получу это:

<div>Lorem <i>ipsum <b>dolor <span>sit 

Проблема в том, что я не могу закрыть элементы. Итак, мне нужен скрипт, который проверяет элементы <*> в коде (где * может быть что угодно), и если у него нет закрывающего тега, закрывает их.

Пожалуйста, помогите мне в этом. Спасибо.

Решение в нерабочее время и 4 голосования @ stackoverflow:

PHP:

...
function closetags($content) {
   preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $content, $result);
    $openedtags = $result[1];
  preg_match_all('#</([a-z]+)>#iU', $content, $result);
 $closedtags = $result[1];
  $len_opened = count($openedtags);
  if (count($closedtags) == $len_opened) {
       return $content;
  }
  $openedtags = array_reverse($openedtags);
  for ($i=0; $i < $len_opened; $i++) {
        if (!in_array($openedtags[$i], $closedtags)) {
         $content .= '</'.$openedtags[$i].'>';
       } else {
           unset($closedtags[array_search($openedtags[$i], $closedtags)]);
        }
  }
  return $content;
}
...

ТПЛ:

{$pages[j].text|truncate:300|@closetags}

person Répás    schedule 19.04.2010    source источник
comment
Вы столкнетесь с другими проблемами, если будете обрезать тег в середине.   -  person Ian Clelland    schedule 20.04.2010
comment
Что, если я напишу вот так ‹‹ для акцента ›› ?   -  person SeanJA    schedule 23.04.2010
comment
баг :) но html скрипт ($pages[j].text) написан текстовым редактором - jwysiwyg. Он не генерирует <<s. После этого он мне не понадобится, но: вы можете проверить $content PHP на наличие дубликатов, и если вы видите << или <<<... и т. д., замените его на htmlspecialchars...   -  person Répás    schedule 23.04.2010
comment
возможный дубликат PHP: обрезать HTML, игнорируя теги   -  person user    schedule 18.03.2014


Ответы (3)


Вытащите все открытые теги, вставьте их в массив (array_1) один за другим.

Вытяните все закрытые теги, вставьте их в массив (array_2) один за другим (включая самозакрывающиеся теги).

Для тегов в первом массиве (массив_1), которые не найдены во втором массиве (массив_2), добавьте их в html.

[править]

Конечно, этот метод с треском провалится, если вы не напишете правильный html... но что делать?

Другой способ - просмотреть строку вперед, чтобы увидеть, какие теги закрыты, и закрыть их по мере необходимости.

person SeanJA    schedule 19.04.2010

Для упрощения, если код является допустимым XML до усечения и вы не обрезаете теги пополам, алгоритм будет примерно таким:

  • Поместить открывающие теги в стек
  • Снимите их, когда найдете закрывающий тег (который будет совпадать, если код действителен).
  • Когда вы дойдете до конца, начните хлопать, чтобы создать закрытие. Остальные теги должны быть добавлены к исходному (усеченному) тексту.

Пример:

<div>Lorem <i>ipsum <b>dolor <span>sit </span>amet</b><div>
  • Нажмите «div», «i», «b», «span»
  • Найден закрывающий тег "span"
  • Поп "пролёт"
  • Найден закрывающий тег "b"
  • Поп "б"
  • Нажмите "див"
  • Конец усеченного текста
  • Вытолкнуть "div" --> добавить </div> к тексту
  • Вытащите "b" --> добавьте </b> к тексту
  • Вставить "i" --> добавить </i> к тексту
  • Вытащите "div" --> добавьте </div> к тексту
  • Конец
person robertos    schedule 19.04.2010
comment
@SeanJA, допустимый xhtml также является допустимым xml. - person tloflin; 20.04.2010
comment
@SeanJA @tloflin верен - этот метод работает для любого XML, включая XHTML. HTML не требует закрытия тегов во многих случаях (например, ‹img›, ‹li›), поэтому алгоритму не нужно их закрывать. - person robertos; 22.04.2010
comment
Достаточно честно, лично я не считаю незакрытый html правильным. - person SeanJA; 22.04.2010

для других людей, таких как я и вы, я нашел этот код, я думаю, что это лучшее решение

добавьте этот файл с именем "modifier.html_substr.php"

        <?php
    /*
    * Smarty plugin
    *
    -------------------------------------------------------------
    * File: modifier.html_substr.php
    * Type: modifier
    * Name: html_substr
    * Version: 1.0
    * Date: June 19th, 2003
    * Purpose: Cut a string preserving any tag nesting and matching.
    * Install: Drop into the plugin directory.
    * Author: Original Javascript Code: Benjamin Lupu <[email protected]>
    * Translation to PHP & Smarty: Edward Dale <[email protected]>
    * Modification to add a string: Sebastian Kuhlmann <[email protected]>
    * Modification to put the added string before closing <p> or <li> tags by Peter Carter http://www.podhawk.com
    -------------------------------------------------------------
    */
    function smarty_modifier_html_substr($string, $length, $addstring="")
    {

    //some nice italics for the add-string
    if (!empty($addstring)) $addstring = "<i> " . $addstring . "</i>";

    if (strlen($string) > $length) {
    if( !empty( $string ) && $length>0 ) {
    $isText = true;
    $ret = "";
    $i = 0;

    $currentChar = "";
    $lastSpacePosition = -1;
    $lastChar = "";

    $tagsArray = array();
    $currentTag = "";
    $tagLevel = 0;

    $addstringAdded = false;

    $noTagLength = strlen( strip_tags( $string ) );

    // Parser loop
    for( $j=0; $j<strlen( $string ); $j++ ) {

    $currentChar = substr( $string, $j, 1 );
    $ret .= $currentChar;

    // Lesser than event
    if( $currentChar == "<") $isText = false;

      // Character handler
      if( $isText ) {

          // Memorize last space position
          if( $currentChar == " " ) { $lastSpacePosition = $j; }
          else { $lastChar = $currentChar; }

          $i++;
          } else {
          $currentTag .= $currentChar;
          }

          // Greater than event
          if( $currentChar == ">" ) {
              $isText = true;

              // Opening tag handler
              if( ( strpos( $currentTag, "<" ) !== FALSE ) &&
              ( strpos( $currentTag, "/>" ) === FALSE ) &&
              ( strpos( $currentTag, "</") === FALSE ) ) {

              // Tag has attribute(s)
              if( strpos( $currentTag, " " ) !== FALSE ) {
                  $currentTag = substr( $currentTag, 1, strpos( $currentTag, " " ) - 1 );
                  } else {
                  // Tag doesn't have attribute(s)
                  $currentTag = substr( $currentTag, 1, -1 );
                  }

                  array_push( $tagsArray, $currentTag );

                  } else if( strpos( $currentTag, "</" ) !== FALSE ) {
                  array_pop( $tagsArray );
                  }

              $currentTag = "";
              }

          if( $i >= $length) {
          break;
          }
      }

    // Cut HTML string at last space position
    if( $length < $noTagLength ) {
    if( $lastSpacePosition != -1 ) {
    $ret = substr( $string, 0, $lastSpacePosition );
    } else {
    $ret = substr( $string, $j );
    }
    }

    // Close broken XHTML elements
    while( sizeof( $tagsArray ) != 0 ) {
    $aTag = array_pop( $tagsArray );
      // if a <p> or <li> tag needs to be closed, put the add-string in first
      if (($aTag == "p" || $aTag == "li") && strlen($string) > $length) {
      $ret .= $addstring;
      $addstringAdded = true;
      }
    $ret .= "</" . $aTag . ">\n";
    }

    } else {
    $ret = "";
    }

    // if we have not added the add-string already 
    if ( strlen($string) > $length && $addstringAdded == false) {
    return( $ret.$addstring );
    }
    else {
    return ( $ret );
    }
    }
    else {
    return ( $string );
    }
    }
    ?>

Применение

{$yourdata|html_substr:300:' ...'}
person bakal    schedule 04.03.2018