Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Symfony 2 Twig Extension that truncates html and preserves tags
Truncate Html string without stripping tags
register in Resources/config/services.yml with:
class: Radley\TwigExtensionBundle\Extension\TruncateHtmlExtension
- { name: twig.extension }
{{ htmlstring|truncatehtml(500)|raw }}
namespace Radley\TwigExtensionBundle\Extension;
class TruncateHtmlString {
function __construct($string, $limit) {
// create dom element using the html string
$this->tempDiv = new \DomDocument();
// keep the characters count till now
$this->charCount = 0;
$this->encoding = 'UTF-8';
// character limit need to check
$this->limit = $limit;
function cut() {
// create empty document to store new html
$this->newDiv = new \DomDocument();
// cut the string by parsing through each element
$this->searchEnd($this->tempDiv->documentElement, $this->newDiv);
$newhtml = $this->newDiv->saveHTML();
return $newhtml;
function deleteChildren($node) {
while (isset($node->firstChild)) {
function searchEnd($parseDiv, $newParent) {
foreach($parseDiv->childNodes as $ele) {
// not text node
if($ele->nodeType != 3) {
$newEle = $this->newDiv->importNode($ele, true);
if(count($ele->childNodes) === 0) {
$res = $this->searchEnd($ele, $newEle);
return $res;
// the limit of the char count reached
if(mb_strlen($ele->nodeValue, $this->encoding) + $this->charCount >= $this->limit) {
$newEle = $this->newDiv->importNode($ele);
$newEle->nodeValue = substr($newEle->nodeValue, 0, $this->limit - $this->charCount);
return true;
$newEle = $this->newDiv->importNode($ele);
$this->charCount += mb_strlen($newEle->nodeValue, $this->encoding);
return false;
class TruncateHtmlExtension extends \Twig_Extension {
public function getName() {
return 'truncatehtml';
public function getFilters() {
return array('truncatehtml' => new \Twig_Filter_Method($this, 'truncatehtml'));
public function truncatehtml($html, $limit, $endchar = '&hellip;') {
$output = new TruncateHtmlString($html, $limit);
return $output->cut() . $endchar;

This comment has been minimized.

Copy link

Lughino commented Dec 7, 2013

Really a great job!
There is only one problem, when he meets with no closing tag type
(slash at the end) an exception is thrown:

An exception has been thrown during the rendering of a template ("Warning: DOMDocument::loadXML(): Opening and ending tag mismatch: br line 1 and div in Entity, line: 1 in /var/www/Acme/src/Acme/CoreBundle/Form/Extension/TruncateHtmlString.php line 16")

Is there a way to solve?
Unfortunately, those tags will automatically create a WYSIWYG editor and I cannot change them.


This comment has been minimized.

Copy link

ale30p commented Mar 21, 2014



This comment has been minimized.

Copy link

ptsiampas commented Apr 25, 2017

Thanks for that Leon, just a slight addition, I had loadXML freak out when there was an invalid char within the HTML.. So I am adding my fix here for anyone else that wants to use it :)

function __construct( $string, $limit ) {
$this->tempDiv->loadXML( '<div>' . $this->utf8_for_xml($string) . '</div>' );

function utf8_for_xml($string){
return preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $string);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.