Back to StringHelper class

Method truncate

public static string
truncate
(mixed $text, mixed $length = 0, mixed $noSplit = true, mixed $allowHtml = true)
Truncates text blocks over the specified character limit and closes all open HTML tags. The method will optionally not truncate an individual word, it will find the first space that is within the limit and truncate at that point. This method is UTF-8 safe.
Parameters
  • string $text The text to truncate.
  • int $length The maximum length of the text.
  • bool $noSplit Don't split a word if that is where the cutoff occurs (default: true).
  • bool $allowHtml Allow HTML tags in the output, and close any open tags (default: true).
Returns
  • string The truncated text.
Since
  • 1.6
Class: StringHelper
Project: Joomla

Method truncate - Source code

/**
 * Truncates text blocks over the specified character limit and closes
 * all open HTML tags. The method will optionally not truncate an individual
 * word, it will find the first space that is within the limit and
 * truncate at that point. This method is UTF-8 safe.
 *
 * @param   string   $text       The text to truncate.
 * @param   integer  $length     The maximum length of the text.
 * @param   boolean  $noSplit    Don't split a word if that is where the cutoff occurs (default: true).
 * @param   boolean  $allowHtml  Allow HTML tags in the output, and close any open tags (default: true).
 *
 * @return  string   The truncated text.
 *
 * @since   1.6
 */
public static function truncate($text, $length = 0, $noSplit = true, $allowHtml = true)
{
    // Assume a lone open tag is invalid HTML.
    if ($length === 1 && $text[0] === '<') {
        return '...';
    }
    // Check if HTML tags are allowed.
    if (!$allowHtml) {
        // Deal with spacing issues in the input.
        $text = str_replace('>', '> ', $text);
        $text = str_replace(array(' ', ' '), ' ', $text);
        $text = FrameworkStringHelper::trim(preg_replace('#\\s+#mui', ' ', $text));
        // Strip the tags from the input and decode entities.
        $text = strip_tags($text);
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
        // Remove remaining extra spaces.
        $text = str_replace(' ', ' ', $text);
        $text = FrameworkStringHelper::trim(preg_replace('#\\s+#mui', ' ', $text));
    }
    // Whether or not allowing HTML, truncate the item text if it is too long.
    if ($length > 0 && FrameworkStringHelper::strlen($text) > $length) {
        $tmp = trim(FrameworkStringHelper::substr($text, 0, $length));
        if ($tmp[0] === '<' && strpos($tmp, '>') === false) {
            return '...';
        }
        // $noSplit true means that we do not allow splitting of words.
        if ($noSplit) {
            // Find the position of the last space within the allowed length.
            $offset = FrameworkStringHelper::strrpos($tmp, ' ');
            $tmp = FrameworkStringHelper::substr($tmp, 0, $offset + 1);
            // If there are no spaces and the string is longer than the maximum
            // we need to just use the ellipsis. In that case we are done.
            if ($offset === false && strlen($text) > $length) {
                return '...';
            }
            if (FrameworkStringHelper::strlen($tmp) > $length - 3) {
                $tmp = trim(FrameworkStringHelper::substr($tmp, 0, FrameworkStringHelper::strrpos($tmp, ' ')));
            }
        }
        if ($allowHtml) {
            // Put all opened tags into an array
            preg_match_all("#<([a-z][a-z0-9]*)\\b.*?(?!/)>#i", $tmp, $result);
            $openedTags = $result[1];
            // Some tags self close so they do not need a separate close tag.
            $openedTags = array_diff($openedTags, array('img', 'hr', 'br'));
            $openedTags = array_values($openedTags);
            // Put all closed tags into an array
            preg_match_all("#</([a-z][a-z0-9]*)\\b(?:[^>]*?)>#iU", $tmp, $result);
            $closedTags = $result[1];
            $numOpened = count($openedTags);
            // Not all tags are closed so trim the text and finish.
            if (count($closedTags) !== $numOpened) {
                // Closing tags need to be in the reverse order of opening tags.
                $openedTags = array_reverse($openedTags);
                // Close tags
                for ($i = 0; $i < $numOpened; $i++) {
                    if (!in_array($openedTags[$i], $closedTags)) {
                        $tmp .= '</' . $openedTags[$i] . '>';
                    } else {
                        unset($closedTags[array_search($openedTags[$i], $closedTags)]);
                    }
                }
            }
            // Check if we are within a tag
            if (FrameworkStringHelper::strrpos($tmp, '<') > FrameworkStringHelper::strrpos($tmp, '>')) {
                $offset = FrameworkStringHelper::strrpos($tmp, '<');
                $tmp = FrameworkStringHelper::trim(FrameworkStringHelper::substr($tmp, 0, $offset));
            }
        }
        if ($tmp === false || strlen($text) > strlen($tmp)) {
            $text = trim($tmp) . '...';
        }
    }
    // Clean up any internal spaces created by the processing.
    $text = str_replace(' </', '</', $text);
    $text = str_replace(' ...', '...', $text);
    return $text;
}