<?php

// your code goes here
$text = '<strong>&amp;foo < FOO<em class="bar">bar</em></strong>';

    $document = <<<EOD
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://w...content-available-to-author-only...3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://w...content-available-to-author-only...3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head>
<body>!html</body>
</html>
EOD;
    // PHP's \DOMDocument serialization adds straw whitespace in case the markup
    // of the wrapping document contains newlines, so ensure to remove all
    // newlines before injecting the actual HTML body to process.
    $document = strtr($document, array("\n" => '', '!html' => $text));

    $dom = new \DOMDocument();
    // Ignore warnings during HTML soup loading.
    @$dom->loadHTML($document);
    
    $body_node = $dom->getElementsByTagName('body')->item(0);
    $html = '';

    foreach ($body_node->getElementsByTagName('script') as $node) {
      static::escapeCdataElement($node);
    }
    foreach ($body_node->getElementsByTagName('style') as $node) {
      static::escapeCdataElement($node, '/*', '*/');
    }
    foreach ($body_node->childNodes as $node) {
      $html .= $dom->saveXML($node);
    }
    
    $text = $html;

preg_match_all('/<[^>]++>|[^<>\s]++/', $text, $tokens);

print_r($tokens);

$counter = 0;
$maxlength = 5;
$newtext = array();
foreach ($tokens[0] as $token) {
	if (mb_substr($token, 0, 1, 'utf-8') === '<') {
		$newtext[] = $token;
		continue;
	}
	$counter += strlen(html_entity_decode($token));
	if ($counter > $maxlength) {
		break;
	}
	$newtext[] = $token;
}
$newtext = implode('', $newtext);
print_r($tokens);
print_r($newtext);