Uma tarefa importante e frequente na publicação na Internet é a conversão de textos para HTML.
Não esperamos que o usuário tenha algum conhecimento do HTML. Além disto, é um trabalho muito massante escrever os textos em meio à uma profusão de marcações – que atrapalham inclusive alguma alteração posterior.
Ao bom estilo “wiki”, vamos criar uma função que reconhece os parágrafos, inserindo automaticamente as marcações para os títulos, listas e alguma coisa mais.
Para “potencializar” ainda mais o nosso formatador, vamos usar a função para filtrar o HTML que criamos anteriormente – filter_html().
Pode acontecer que, ao submeter um texto em um formulário, as linhas sejam quebradas em lugares indesejados. Para evitar confusão, vamos considerar como separação de parágrafo, uma linha em branco.
Nossa função irá pegar o primeiro caractére do parágrafo e fazer uma análize. Porém, algumas marcações poderão abranger múltiplos parágrafos, por isto, nosso método de percorrer os parágrafos não é o mais simples: ao invés de usarmos um simples explode(), que poderia nos entregar os parágrafos em um array, utilizaremos um ponteiro para procurar os parágrafos e os recortaremos um a um.
Teremos um conjunto de marcações padrão para serem utilizadas durante a formatação, mas aceitaremos como parâmetro marcações alternativas que, se forem informadas, serão utilizadas no lugar das marcações padrão.
Vamos à função :
<?php
function text2html ($string, $filter_html_mode=1, $alternative_marks=false)
{ // text2html
// As marcações utilizadas por padrão
static $default_marks = array (
'p_open'=>'<p>',
'p_close'=>'</p>',
'h1_open'=>'<h1>',
'h1_close'=>'</h1>',
'h2_open'=>'<h2>',
'h2_close'=>'</h2>',
'h3_open'=>'<h3>',
'h3_close'=>'</h3>',
'h4_open'=>'<h4>',
'h4_close'=>'</h4>',
'h5_open'=>'<h5>',
'h5_close'=>'</h5>',
'h6_open'=>'<h6>',
'h6_close'=>'</h6>',
'ul_open'=>'<ul>',
'ul_close'=>'</ul>',
'ol_open'=>'<ol>',
'ol_close'=>'</ol>',
'al_open'=>'<ol style="list-style-type:lower-latin">',
'al_close'=>'</ol>',
'li_open'=>'<li>',
'li_close'=>'</li>',
'pre_open'=>'<pre>',
'pre_close'=>'</pre>',
'code_open'=>'<code><pre>',
'code_close'=>'</pre></code>',
'style_open'=>'<style type="text/css">',
'style_close'=>'</style>',
'script_open'=>'<script type="text/javascript">',
'script_close'=>'</script>',
'hr'=>'<hr>'
);
// Permite marcações alternativas
is_array ($alternative_marks)? $marks = array_merge ($default_marks, $alternative_marks) : $marks = $default_marks;
// Converte quebras de linhas Windows para Linux
$string = str_replace ("\r\n", "\n", $string);
// o cumprimento total da string
$strlen = strlen ($string);
// Os ponteiros indicando o início e o fim de um parágrafo
$paragraph_start = 0;
$paragraph_end = 0;
// O resultado final
$result = '';
// enquanto o ponteiro não alcançar o final da string
while ($paragraph_start < $strlen)
{ // move pointer
// um parágrafo é delimitado por duas quebras de linha consecutivas:
$paragraph_end = strpos ($string, "\n\n", $paragraph_start);
if ($paragraph_end === false)
$paragraph_end = $strlen;
else
$paragraph_end += 2;
// pega o primeiro caractére do parágrafo
$char = $string[$paragraph_start];
switch ($char)
{ // switch char
// Ignora os caractéres
case "\r":
case "\n":
case "\t":
$paragraph_end = $paragraph_start + 1;
break;
// Listas
case '*':
case '#':
case '@':
$paragraph = substr ($string, $paragraph_start, $paragraph_end - $paragraph_start);
// Separa as linhas do parágrafo
$lines = explode ("\n", $paragraph . "\n");
$last = '';
$last_level = 0;
// Percorre as linhas deste parágrafo
foreach ($lines as $line)
{ // each line
// Quantos símbolos existem no início desta linha
$current_level = strspn ($line, '*#@');
// Recorta os símbolos encontrados para análize posterior
$current = substr ($line, 0, $current_level);
// Retira os símbolos do início da linha
$line = substr ($line, $current_level);
$line = trim ($line);
$line = filter_html ($line, $filter_html_mode);
// Se preferir: $line = filter_html (trim (substr ($line, $current_level)), $filter_html_mode);
// Se não houverem símbolos no início desta linha, trata-se de uma
// quebra acidental, o que nos leva a emendar esta linha com a linha anterior
if (!$current_level and strlen ($line))
$result .= ' ' . $line;
else
{ // valid line
// Vamos comparar os símbolos da linha atual com a linha anterior
// para determinar até que ponto são iguais
for ($equal = 0; $equal < $last_level and $equal < $current_level and $current[$equal] == $last[$equal]; $equal ++)
{ } // compare levels
// Se o resultado indica que precisamos descer algum nível da lista
for ($level = $last_level; $level > $equal; $level --)
{ // loop level down
$char = $last[$level - 1];
if ($char == '*')
$result .= $marks['li_close'] . "\n" . $marks['ul_close'] . "\n";
elseif ($char == '#')
$result .= $marks['li_close'] . "\n" . $marks['ol_close'] . "\n";
else
$result .= $marks['li_close'] . "\n" . $marks['al_close'] . "\n";
} // loop level down
// Se fechamos uma lista, a linha atual deverá ser criada como um novo item
if ($last_level > $equal and $equal)
{ // new item after close a list
$result .= $marks['li_close'] . "\n" . $marks['li_open'];
} // new item after close a list
// Se o prefixo da lista anterior for idêntico ao desta,
// simplesmente criamos um novo item
if ($last_level == $equal and $current_level == $equal and $equal)
{ // item in same level
$result .= $marks['li_close'] . "\n" . $marks['li_open'];
} // item in same level
// Se for preciso criar sublistas
for ($level = $equal; $level < $current_level; $level ++)
{ // level up
if ($level)
$result .= "\n";
$char = $current[$level];
if ($char == '*')
$result .= $marks['ul_open'] . "\n" . $marks['li_open'];
elseif ($char == '#')
$result .= $marks['ol_open'] . "\n" . $marks['li_open'];
else
$result .= $marks['al_open'] . "\n" . $marks['li_open'];
} // level up
// Finalmente, colocamos o conteúdo desta linha
$result .= $line;
// Preparamos a próxima iteração anotando os itens desta linha como
// sendo os símbolos da linha anterior
$last = $current;
$last_level = $current_level;
} // valid line
} // each line
break;
// Cabeçálhos - ou Títulos
case '=':
$paragraph = substr ($string, $paragraph_start, $paragraph_end - $paragraph_start);
// Vamos ver quantos símbolos = tem no início do parágrafo
$level = strspn ($paragraph, '=');
// Se forem de 1 a 6
if ($level <= 6)
{ // valid header level
// Vamos filtrar o parágrafo, removendo os sinais de = no início e no final
$paragraph = trim ($paragraph, "= \r\n\t");
$paragraph = filter_html ($paragraph, $filter_html_mode);
$result .= $marks['h' . $level . '_open'] . $paragraph . $marks['h' . $level . '_close'] . "\n";
} // valid header level
// Se tiverem mais de 6 sinais de =, será tratado como um parágrafo normal
else
$result .= $marks['p_open'] . filter_html (trim ($paragraph), $filter_html_mode) . $marks['p_close'] . "\n";
break;
// Um separador horizontal
case '-':
$paragraph = substr ($string, $paragraph_start, $paragraph_end - $paragraph_start);
// Quantos hifens ("-") tem no parágrafo
$level = strspn ($paragraph, '-');
// Vamos limpar o parágrafo
$clean = trim ($paragraph, "- \r\n\t");
// Somente se houverem mais de 4 hifens e se não houver restado nada após a limpeza
if ($level > 4 and !strlen ($clean))
$result .= $marks['hr'] . "\n";
// Caso contrário, este será mais um parágrafo comum
else
$result .= $marks['p_open'] . filter_html (trim ($paragraph), $filter_html_mode) . $marks['p_close'] . "\n";
break;
// Linhas pré-formatadas terão o HTML escapado
case ' ':
$paragraph = substr ($string, $paragraph_start, $paragraph_end - $paragraph_start);
$result .= $marks['pre_open'] . "\n" . str_replace ('<', '<', trim ($paragraph, "\r\n")) . "\n" . $marks['pre_close'] . "\n";
break;
// procura por marcações que alteram o método de formatação
case '[':
$tag_start = $paragraph_start + 1;
// Procura por ]
$tag_end = strpos ($string, ']', $tag_start);
// Se for encontrado
if (is_int ($tag_end))
{ // end of tag found
// Recortamos a tag
$tag = substr ($string, $tag_start, $tag_end - $tag_start);
list ($tag_type) = explode (':', $tag);
$tag_original = $tag_type;
// Vamos analizar seu conteúdo
switch ($tag_type)
{ // switch tag
case 'style':
case 'script':
// Se não for permitido script ou style, as marcas serão convertidas
if ($filter_html_mode != 2)
$tag_type = 'pre';
case 'code':
case 'pre':
// Vamos procurar pela marca de fechamento
$close_tag = '[/' . $tag_original . ']';
$close_tag_length = strlen ($close_tag);
$close_tag_pos = strpos ($string, $close_tag, $tag_end);
// Se a encontrarmos
if ($close_tag_pos)
{ // close tag found
$paragraph_end = $close_tag_pos + $close_tag_length;
$tag_end ++;
$paragraph = substr ($string, $tag_end, $close_tag_pos - $tag_end);
// Se o conteudo for do tipo code ou pre, escapamos o HTML
if ($tag_type == 'code' or $tag_type == 'pre')
$paragraph = str_replace ('<', '<', $paragraph);
$result .= $marks[$tag_type . '_open'] . $paragraph . $marks[$tag_type . '_close'] . "\n";
break 2;
} // close tag found
// outras marcações serão consideradas como um parágrafo normal
// simplesmente deixando o fluxo do programa prosseguir
} // switch tag
} // end of tag found
// Um parágrafo normal
default:
$result .= $marks['p_open'] . filter_html (trim (substr ($string, $paragraph_start, $paragraph_end - $paragraph_start)), $filter_html_mode) . $marks['p_close'] . "\n";
} // switch char
// Movemos o ponteiro adiante
$paragraph_start = $paragraph_end;
} // move pointer
// converte quebras de linhas Linux para Windows
$result = str_replace ("\n", "\r\n", $result);
return $result;
} // text2html
?>
Esta postagem foi modificada pela última vez em 10/12/2013 13:36