Source for file string.php

Documentation is available at string.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  HTML
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM') or die;
  11.  
  12. /**
  13.  * HTML helper class for rendering manipulated strings.
  14.  *
  15.  * @package     Joomla.Platform
  16.  * @subpackage  HTML
  17.  * @since       1.6
  18.  */
  19. abstract class JHtmlString
  20. {
  21.     /**
  22.      * Truncates text blocks over the specified character limit and closes
  23.      * all open HTML tags. The method will optionally not truncate an individual
  24.      * word, it will find the first space that is within the limit and
  25.      * truncate at that point. This method is UTF-8 safe.
  26.      *
  27.      * @param   string   $text       The text to truncate.
  28.      * @param   integer  $length     The maximum length of the text.
  29.      * @param   boolean  $noSplit    Don't split a word if that is where the cutoff occurs (default: true).
  30.      * @param   boolean  $allowHtml  Allow HTML tags in the output, and close any open tags (default: true).
  31.      *
  32.      * @return  string   The truncated text.
  33.      *
  34.      * @since   1.6
  35.      */
  36.     public static function truncate($text, $length = 0, $noSplit = true, $allowHtml = true)
  37.     {
  38.         // Assume a lone open tag is invalid HTML.
  39.         if ($length == 1 && substr($text, 0, 1) == '<')
  40.         {
  41.             return '...';
  42.         }
  43.  
  44.         // Check if HTML tags are allowed.
  45.         if (!$allowHtml)
  46.         {
  47.             // Deal with spacing issues in the input.
  48.             $text = str_replace('>', '> ', $text);
  49.             $text = str_replace(array('&nbsp;', '&#160;'), ' ', $text);
  50.             $text = JString::trim(preg_replace('#\s+#mui', ' ', $text));
  51.  
  52.             // Strip the tags from the input and decode entities.
  53.             $text = strip_tags($text);
  54.             $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
  55.  
  56.             // Remove remaining extra spaces.
  57.             $text = str_replace('&nbsp;', ' ', $text);
  58.             $text = JString::trim(preg_replace('#\s+#mui', ' ', $text));
  59.         }
  60.  
  61.         // Whether or not allowing HTML, truncate the item text if it is too long.
  62.         if ($length > 0 && JString::strlen($text) > $length)
  63.         {
  64.             $tmp = trim(JString::substr($text, 0, $length));
  65.  
  66.             if (substr($tmp, 0, 1) == '<' && strpos($tmp, '>') === false)
  67.             {
  68.                 return '...';
  69.             }
  70.  
  71.             // $noSplit true means that we do not allow splitting of words.
  72.             if ($noSplit)
  73.             {
  74.                 // Find the position of the last space within the allowed length.
  75.                 $offset = JString::strrpos($tmp, ' ');
  76.                 $tmp = JString::substr($tmp, 0, $offset + 1);
  77.  
  78.                 // If there are no spaces and the string is longer than the maximum
  79.                 // we need to just use the ellipsis. In that case we are done.
  80.                 if ($offset === false && strlen($text) > $length)
  81.                 {
  82.                     return '...';
  83.                 }
  84.  
  85.                 if (JString::strlen($tmp) > $length - 3)
  86.                 {
  87.                     $tmp = trim(JString::substr($tmp, 0, JString::strrpos($tmp, ' ')));
  88.                 }
  89.             }
  90.  
  91.             if ($allowHtml)
  92.             {
  93.                 // Put all opened tags into an array
  94.                 preg_match_all("#<([a-z][a-z0-9]*)\b.*?(?!/)>#i", $tmp, $result);
  95.                 $openedTags = $result[1];
  96.  
  97.                 // Some tags self close so they do not need a separate close tag.
  98.                 $openedTags = array_diff($openedTags, array("img", "hr", "br"));
  99.                 $openedTags = array_values($openedTags);
  100.  
  101.                 // Put all closed tags into an array
  102.                 preg_match_all("#</([a-z]+)>#iU", $tmp, $result);
  103.                 $closedTags = $result[1];
  104.  
  105.                 $numOpened = count($openedTags);
  106.  
  107.                 // All tags are closed so trim the text and finish.
  108.                 if (count($closedTags) == $numOpened)
  109.                 {
  110.                     return trim($tmp) . '...';
  111.                 }
  112.  
  113.                 // Closing tags need to be in the reverse order of opening tags.
  114.                 $openedTags = array_reverse($openedTags);
  115.  
  116.                 // Close tags
  117.                 for ($i = 0; $i < $numOpened; $i++)
  118.                 {
  119.                     if (!in_array($openedTags[$i], $closedTags))
  120.                     {
  121.                         $tmp .= "</" . $openedTags[$i] . ">";
  122.                     }
  123.                     else
  124.                     {
  125.                         unset($closedTags[array_search($openedTags[$i], $closedTags)]);
  126.                     }
  127.                 }
  128.             }
  129.  
  130.             if ($tmp === false || strlen($text) > strlen($tmp))
  131.             {
  132.                 $text = trim($tmp) . '...';
  133.             }
  134.         }
  135.  
  136.         // Clean up any internal spaces created by the processing.
  137.         $text = str_replace(' </', '</', $text);
  138.         $text = str_replace(' ...', '...', $text);
  139.  
  140.         return $text;
  141.     }
  142.  
  143.     /**
  144.     * Method to extend the truncate method to more complex situations
  145.     *
  146.     * The goal is to get the proper length plain text string with as much of
  147.     * the html intact as possible with all tags properly closed.
  148.     *
  149.     * @param   string   $html       The content of the introtext to be truncated
  150.     * @param   integer  $maxLength  The maximum number of characters to render
  151.     * @param   boolean  $noSplit    Don't split a word if that is where the cutoff occurs (default: true).
  152.     *
  153.     * @return  string  The truncated string. If the string is truncated an ellipsis
  154.     *                   (...) will be appended.
  155.     *
  156.     * @note    If a maximum length of 3 or less is selected and the text has more than
  157.     *           that number of characters an ellipsis will be displayed.
  158.     *           This method will not create valid HTML from malformed HTML.
  159.     *
  160.     * @since   3.1
  161.     */
  162.     public static function truncateComplex($html, $maxLength = 0, $noSplit = true)
  163.     {
  164.         // Start with some basic rules.
  165.         $baseLength = strlen($html);
  166.  
  167.         // If the original HTML string is shorter than the $maxLength do nothing and return that.
  168.         if ($baseLength <= $maxLength || $maxLength == 0)
  169.         {
  170.             return $html;
  171.         }
  172.  
  173.         // Take care of short simple cases.
  174.         if ($maxLength <= 3 && substr($html, 0, 1) != '<' && strpos(substr($html, 0, $maxLength - 1), '<') === false && $baseLength > $maxLength)
  175.         {
  176.             return '...';
  177.         }
  178.  
  179.         // Deal with maximum length of 1 where the string starts with a tag.
  180.         if ($maxLength == 1 && substr($html, 0, 1) == '<')
  181.         {
  182.             $endTagPos = strlen(strstr($html, '>', true));
  183.             $tag = substr($html, 1, $endTagPos);
  184.  
  185.             $l = $endTagPos + 1;
  186.  
  187.             if ($noSplit)
  188.             {
  189.                 return substr($html, 0, $l) . '</' . $tag . '...';
  190.             }
  191.  
  192.             // TODO: $character doesn't seem to be used...
  193.             $character = substr(strip_tags($html), 0, 1);
  194.  
  195.             return substr($html, 0, $l) . '</' . $tag . '...';
  196.         }
  197.  
  198.         // First get the truncated plain text string. This is the rendered text we want to end up with.
  199.         $ptString = JHtml::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = false);
  200.  
  201.         // It's all HTML, just return it.
  202.         if (strlen($ptString) == 0)
  203.         {
  204.                 return $html;
  205.         }
  206.  
  207.         // If the plain text is shorter than the max length the variable will not end in ...
  208.         // In that case we use the whole string.
  209.         if (substr($ptString, -3) != '...')
  210.         {
  211.                 return $html;
  212.         }
  213.  
  214.         // Regular truncate gives us the ellipsis but we want to go back for text and tags.
  215.         if ($ptString == '...')
  216.         {
  217.             $stripped = substr(strip_tags($html), 0, $maxLength);
  218.             $ptString = JHtml::_('string.truncate', $stripped, $maxLength, $noSplit, $allowHtml = false);
  219.         }
  220.  
  221.         // We need to trim the ellipsis that truncate adds.
  222.         $ptString = rtrim($ptString, '.');
  223.  
  224.         // Now deal with more complex truncation.
  225.         while ($maxLength <= $baseLength)
  226.         {
  227.             // Get the truncated string assuming HTML is allowed.
  228.             $htmlString = JHtml::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = true);
  229.  
  230.             if ($htmlString == '...' && strlen($ptString) + 3 > $maxLength)
  231.             {
  232.                 return $htmlString;
  233.             }
  234.  
  235.             $htmlString = rtrim($htmlString, '.');
  236.  
  237.             // Now get the plain text from the HTML string and trim it.
  238.             $htmlStringToPtString = JHtml::_('string.truncate', $htmlString, $maxLength, $noSplit, $allowHtml = false);
  239.             $htmlStringToPtString = rtrim($htmlStringToPtString, '.');
  240.  
  241.             // If the new plain text string matches the original plain text string we are done.
  242.             if ($ptString == $htmlStringToPtString)
  243.             {
  244.                 return $htmlString . '...';
  245.             }
  246.  
  247.             // Get the number of HTML tag characters in the first $maxLength characters
  248.             $diffLength = strlen($ptString) - strlen($htmlStringToPtString);
  249.  
  250.             if ($diffLength <= 0)
  251.             {
  252.                 return $htmlString . '...';
  253.             }
  254.  
  255.             // Set new $maxlength that adjusts for the HTML tags
  256.             $maxLength += $diffLength;
  257.         }
  258.     }
  259.  
  260.     /**
  261.      * Abridges text strings over the specified character limit. The
  262.      * behavior will insert an ellipsis into the text replacing a section
  263.      * of variable size to ensure the string does not exceed the defined
  264.      * maximum length. This method is UTF-8 safe.
  265.      *
  266.      * For example, it transforms "Really long title" to "Really...title".
  267.      *
  268.      * Note that this method does not scan for HTML tags so will potentially break them.
  269.      *
  270.      * @param   string   $text    The text to abridge.
  271.      * @param   integer  $length  The maximum length of the text (default is 50).
  272.      * @param   integer  $intro   The maximum length of the intro text (default is 30).
  273.      *
  274.      * @return  string   The abridged text.
  275.      *
  276.      * @since   1.6
  277.      */
  278.     public static function abridge($text, $length = 50, $intro = 30)
  279.     {
  280.         // Abridge the item text if it is too long.
  281.         if (JString::strlen($text) > $length)
  282.         {
  283.             // Determine the remaining text length.
  284.             $remainder = $length - ($intro + 3);
  285.  
  286.             // Extract the beginning and ending text sections.
  287.             $beg = JString::substr($text, 0, $intro);
  288.             $end = JString::substr($text, JString::strlen($text) - $remainder);
  289.  
  290.             // Build the resulting string.
  291.             $text = $beg . '...' . $end;
  292.         }
  293.  
  294.         return $text;
  295.     }
  296. }

Documentation generated on Tue, 19 Nov 2013 15:14:32 +0100 by phpDocumentor 1.4.3