PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/libraries/joomla/html/string.php

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