Ir para conteúdo
Fórum Script Brasil
  • 0

Manipular strings com tags HTML?


LucasConstantino

Pergunta

Então pessoal, vou explicar minha situação pois dessa forma se evitam perguntas desnecessária depois:

Estou desenvolvendo uma área administrativa em PHP, mais precisamente para a manipulação de notícias. No formulário de envio das notícias, o usuário tem a opção de customizar seus textos com tags HTML (com a utilização do programa TinyMCE, o que não vem ao caso). Porém, na página principal do site, haverá uma prévia da notícia mais recente, e é na criação dessa prévia que estou quebrando a cabeça já faz alguns dias. A prévia seria, por exemplo, os 200 primeiros caracteres da notícia em questão.

Meu problema está com as tags HTML. Se elas não existissem, um simples "substr( $noticia, 0, 200)" resolveria o problema, porém não posso cortar um texto em qualquer ponto se este conter tags. Caso contrário, se alguma tag fosse cortada ao meio ou ficasse sem fechamento, minha página quebraria inteira.

Descobri uma função chamada "strip_tags( $texto, tags permitidas )" que simplemente limpa do texto todas as tags HTML. Mas também não posso utilizar esta saída, pois se alguém escreve na notícia um nome em negrito, esse nome deve aparecer em negrito na prévia também!

Bom, o que preciso é basicamente entrar com um texto que contenha tags e retorne o mesmo texto, diminuído, contendo, se necessário, as mesmas tags. Exemplo

Texto: "Hello World!"

Html do texto: "Hello <strong>World</strong>!"

Caso eu pedir, no código PHP, que ele apresente apenas os 8 primeiros caracteres do texto, por exemplo, ele me retornará assim:

"Hello <s"

Porém, o que eu gostaria que ele me retornasse é:

"Hello Wo" ou, em HTML: "Hello <strong>Wo</strong>"

Veja que a conta seria feita em base no texto sem o Html, ou seja, o texto resultante. Porém, as tags estariam ainda presentes e, mais ainda, antecipadas de acordo com o fim do texto.

Fiz um código simples que cobre parte do que pretendo realizar, porém está incompleto, pois trata apenas à uma específica tag pré-definida. Eis o script:

<?php
    
    // texto a ser diminuído
    $texto = "Hello <strong>World</strong>!";
    // tamanho final requerido
    $tamanho_final = 8;
    // tag a ser utilizada
    $tag_html = "strong";
    
    // função que reduzira o texto mantendo suas tags Html
function reduce_tagged_text( $text, $max, $tag )
{
    // abertura da tag Html ( para casos como a tag "a", que contém mais informações antes do fechamento )
    $tag_open                         = "<" . $tag;
    // posição onde se inicia a tag
    $tag_open_start             = stripos( $text, $tag_open );
    // apenas para seguir um padrão que virá abaixo é criada essa variável
    $tag_open_start_pos     = $tag_open_start;
    // tamanho da tag até o fechamento
    $tag_open_end                 = stripos( substr( $text, $tag_open_start ), ">" ) + 1;
    /* posição do fechamento. Repare que indica em relação ao início do texto,
    quanto que a de cima indica apartir do início da tag */
    $tag_open_end_pos         = $tag_open_start + $tag_open_end;
    
    // fechamento da tag
    $tag_close                         = "</" . $tag . ">";
    // posição do fechamento, em relação a abertura
    $tag_close_start             = stripos( substr( $text, $tag_open_end_pos ), $tag_close );
    // posição do fechamento, em relação ao início do texto
    $tag_close_start_pos     = $tag_open_end_pos + $tag_close_start;
    // posição do fim do fechamento, em relação ao início do mesmo
    $tag_close_end                 = strlen( $tag_close );
    // posição do fim do fechamento, em relação ao início do texto
    $tag_close_end_pos         = $tag_close_start_pos + $tag_close_end;
    
    $text_part = array();
    
    // subdivisão do texto, contendo "Hello "
    $text_part[0]         = substr( $text, 0, $tag_open_start_pos );
    // subdivisão do texto, contendo "<strong>"
    $text_part[1]         = substr( $text, $tag_open_start_pos, $tag_open_end );
    // subdivisão do texto, contendo "World"
    $text_part[2]         = substr( $text, $tag_open_end_pos, $tag_close_start );
    // subdivisão do texto, contendo "</strong>"
    $text_part[3]        = substr( $text, $tag_close_start_pos, $tag_close_end );
    // subdivisão do texto, contendo "!"
    $text_part[4]         = substr( $text, $tag_close_end_pos );
    
    /*
    O WHILE a seguir define, dentro da array $text_part_len, o tamanho
    das subdivisões criadas acima. While apenas para resumir o código
    */
    $count = 0;
    while( $count < 5 )
    {
        $text_part_len[$count] = strlen( $text_part[$count] );
        $count ++;
    }
    
    // texto sem as tags é igual a "Hello " + "World" + "!"
    $text_clean = $text_part[0] . $text_part[2] . $text_part[4];
    
    // Texto sem tags > tamanho final requerido
    if( strlen( $text_clean ) > $max )
    {
        // reduz o texto sem tag para ficar do tamanho requerido
        $text_reduced             = substr( $text_clean, 0, $max );
        // conta o tamanho do texto reduzido
        $text_reduced_len     = strlen( $text_reduced );
        
        // !!! NO MEU EXEMPLO ELE NÃO ENTRA NO "if", ENTRA NO "elseif" !!!
        
        // se tamanho reduzido for maior que local de fechamento da tag ( maior que "Hello World", ignorando o "!" )
        if( $text_reduced_len > ( $text_part_len[0] + $text_part_len[2] ) )
        {
            // define a posicao em que a tag termina, no texto reduzido
            $text_end_tag_pos     = $text_part_len[0] + $text_part_len[2];
            // define o texto que virá após o termino da tag
            $text_end                     = substr( $text_reduced, $text_end_tag_pos );
            // texto final = "Hello " + "<strong>" + "World" + "</strong>" + resto que couber
            $final_text                     = $text_part[0] . $text_part[1] . $text_part[2] . $text_part[3] . $text_end;
        }
        /*
        se tamanho reduzido for maior o que vem antes da tag porém menor do que o que vem
        antes somando do que vem durante ( maior que "Hello " porém menor que "Hello World" )
        */
        elseif( $text_reduced_len > $text_part_len[0] && $text_reduced_len <= ( $text_part_len[0] + $text_part_len[2] ) )
        {
            // define o texto que vem após abertura da tag
            $text_end     = substr( $text_reduced, $text_part_len[0] );
            // texto final = "Hello " + "<strong>" + "Wo" + "</strong>"
            $final_text     = $text_part[0] . $text_part[1] . $text_end . $text_part[3];
        }
        // se tamanho reduxido cortar o texto ANTES da tag
        elseif( $text_reduced_len <= $text_part_len[0] )
        {
            // texto final = texto reduzido
            $final_text = $text_reduced;
        }
    }
    // se texto sem tags for igual ou menor que tamanho final requerido, nada precisa ser feito
    else
    {
        $final_text = $text;
    }
    // retorna o texto final
    return $final_text;    
}
    // apenas ecoando pra exibir
    echo reduce_tagged_text( $texto, $tamanho_final, $tag_html );

?>

O código acima funciona, mas só se o texto contiver apenas UMA tag Html.

O que venho pedir a vocês é se alguém tem uma solução melhor para que eu possa aplicar esse procedimento à um texto que conténha diversas tags Html.

Ps.: Eu poderia criar no formulário de notícia uma textbox chamada "previa", mas isso não é possível no meu caso. De qualquer forma, será uma ferramente de grande utilidade se concluída!

Muito obrigado, Lucas

Link para o comentário
Compartilhar em outros sites

3 respostass a esta questão

Posts Recomendados

  • 0

Então, Marcos Nunes, você não entendeu o que eu estou querendo não. A idéia é diminuir uma string, porém MANTER as tags nos lugares equivalentes, entende, sem quebrar as tags ou deixar sem finalização. De qualquer forma, eu encontrei um script muito interessante na web e já estou utilizando-o.

Eis o script, caso alguém precise futuramente:

function substrHTML($txt, $max) {
        
          if( strlen( strip_tags( $txt ) ) > $max )
          {
          $stack = array();
          $offset = $count = 0;
        
          while ($count < $max) {
            preg_match('/([^&<]*)(.)?/', $txt, $rs, NULL, $offset);
            $len = strlen($rs[1]);
        
            if (($count + $len) > $max) {
              $len = $max - $count;
              $rs[2] = '';
            }
            $count += $len;
            $offset += $len;
        
            switch ($rs[2]) {
              case '&':
                $offset = strpos($txt, ';', $offset) + 1;
                $count++;
                break;
              case '<':
                if ($txt[$offset+1] == '/') {
                  array_pop($stack);
                } else {
                  preg_match('/<(.*?)[ >]/', $txt, $t, NULL, $offset);
                  $tag = $t[1];
                  array_push($stack, $tag);
                }
                $offset = strpos($txt, '>', $offset) + 1;
        
                break;
            }
          }
          $result = substr($txt, 0, $offset);
        
          if (count($stack)) $result .= "</".join($stack, '></').">";
        
          return $result;
          }
        else{ return $txt; }
    }

Não parei ainda pra ver como funciona, mas parece bem engenhoso... já utilizei e funciona. Caso alguém use e encontre alguma falha, por favor me avise!

Sirvam-se...

Ps.:

Mérito a alguém chamado Pedro Faria, que postou o código em http://www.phpavancado.net/node/309

Link para o comentário
Compartilhar em outros sites

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,1k
    • Posts
      651,8k
×
×
  • Criar Novo...