Truncar texto com PHP sem bagunçar tags HTML

Um dos grandes problemas de guardar HTML no banco de dados é na hora de mostrar. Pode ser que você
queira retornar apenas uma quantidade X de caracteres. Ai surge o grande problema. Pode ser que
algumas tags fiquem pela metade, atrapalhando bastante a visualização do código.
Tá, mas e, por quê eu não tiro as tags antes de cortar a string?
É uma solução, mas existem casos (como o desse site) que é necessário mostrar o HTML também.
Nesse caso podemos usar a função abaixo, que retorna a quantidade de caracteres que você quer,
cuidando para não cortar nenhuma tag HTML pela metade. Segue o código:

function truncar($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true) {
	if ($considerHtml) {
		if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
			return $text;
		}

		preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
		$total_length = strlen($ending);
		$open_tags = array();
		$truncate = '';
		foreach ($lines as $line_matchings) {
			if (!empty($line_matchings[1])) {
				if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
				} else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
					$pos = array_search($tag_matchings[1], $open_tags);
					if ($pos !== false) {
					unset($open_tags[$pos]);
					}
				} else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
					array_unshift($open_tags, strtolower($tag_matchings[1]));
				}
				$truncate .= $line_matchings[1];
			}
			$content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
			if ($total_length+$content_length > $length) {
				$left = $length - $total_length;
				$entities_length = 0;
				if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
					foreach ($entities[0] as $entity) {
						if ($entity[1]+1-$entities_length <= $left) {
							$left--;
							$entities_length += strlen($entity[0]);
						} else {
							break;
						}
					}
				}
				$truncate .= substr($line_matchings[2], 0, $left+$entities_length);
				break;
			} else {
				$truncate .= $line_matchings[2];
				$total_length += $content_length;
			}
			if($total_length <= $length) {
				break;
			}
		}
	} else {
		if (strlen($text) <= $length) {
			return $text;
		} else {
			$truncate = substr($text, 0, $length - strlen($ending));
		}
	}
	if (!$exact) {
		$spacepos = strrpos($truncate, ' ');
		if (isset($spacepos)) {
			$truncate = substr($truncate, 0, $spacepos);
		}
	}
	$truncate .= $ending;
	if($considerHtml) {
		foreach ($open_tags as $tag) {
			$truncate .= '</' . $tag . '>';
		}
	}
	return $truncate;
}

Como usar a função:

echo truncar('<span>jo<strong>nn</strong>as</span>', 6, '...', false, true); // imprime <span>jo<strong>nn</strong>as</span><br />...

2 comentários para “Truncar texto com PHP sem bagunçar tags HTML”

  1. Bill disse:

    Mto boa esta função retirada do helper do CakePHP, muito útil!

    Abs

  2. Uerlen disse:

    Muito boa sua função meu caro.

    [b]Parabens [/b]ae...

Comente você também

* Copie este código:

* Cole ou digite o código aqui:

Google