[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/HTML/Helpers/ -> StringHelper.php (source)

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


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer