1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208:
<?php
/**
* MvcCore
*
* This source file is subject to the BSD 3 License
* For the full copyright and license information, please view
* the LICENSE.md file that are distributed with this source code.
*
* @copyright Copyright (c) 2016 Tom Flidr (https://github.com/mvccore)
* @license https://mvccore.github.io/docs/mvccore/5.0.0/LICENCE.md
*/
namespace MvcCore\Ext\Views\Helpers;
/**
* Responsibility - truncate plain text or text with html tags to max. chars.
* - No html tags are truncated, only text content in html code is truncated.
* - Possibility to setup custom chars for three dots,
* html entity `…` by default, for plain text `...` by default.
* - Possibility to set default truncating method, if third param to define is not set.
* @method \MvcCore\Ext\Views\Helpers\TruncateHelper GetInstance()
*/
class TruncateHelper extends \MvcCore\Ext\Views\Helpers\AbstractHelper {
/**
* MvcCore Extension - View Helper - Assets - version:
* Comparison by PHP function version_compare();
* @see http://php.net/manual/en/function.version-compare.php
*/
const VERSION = '5.0.0';
/**
* If this static property is set - helper is possible
* to configure as singleton before it's used for first time.
* Example:
* `\MvcCore\Ext\View\Helpers\Truncate::GetInstance()
* ->SetThreeDotsText('…', TRUE)
* ->SetThreeDotsText('...', FALSE)
* ->SetAlwaysHtmlMode();`
* @var \MvcCore\Ext\Views\Helpers\TruncateHelper
*/
protected static $instance;
/**
* Custom substrings to set three dost after truncated text.
* If not set by `SetThreeDotsText()` method, there is used
* for truncated html `…` by default and for text `...` by default.
* @var string[]|NULL[]
*/
protected $threeDotsTexts = [NULL, NULL];
/**
* If `TRUE`, texts will be truncated in html mode,
* if there is not third force param `$isHtml` in `Truncate()` method,
* if `FALSE` text will be truncated in text mode with the same option.
* @var bool|NULL
*/
protected $alwaysHtmlMode = NULL;
/**
* Set three dots custom chars - `...` or `…`,
* for html mode or different for text mode.
* There is used by default for html mode `…` and
* for text mode `...`.
* @param string $threeDotsText
* @param bool $forHtmlText
* @return \MvcCore\Ext\Views\Helpers\TruncateHelper
*/
public function SetThreeDotsText ($threeDotsText = '…', $forHtmlText = TRUE) {
$this->threeDotsTexts[$forHtmlText ? 1 : 0] = $threeDotsText;
return $this;
}
/**
* Setup helper to use always html mode truncating
* if there is not set third param to use force html or text mode.
* @param bool $alwaysHtmlMode
* @return \MvcCore\Ext\Views\Helpers\TruncateHelper
*/
public function SetAlwaysHtmlMode ($alwaysHtmlMode = TRUE) {
$this->alwaysHtmlMode = $alwaysHtmlMode;
return $this;
}
/**
* Truncate text with any HTML tags inside, keep all tags and truncate text content only
* or truncate simple text without any HTML tags.
* Clean all possible punctuation and brackets before three dots: `',.:;?!+"\'-–()[]{}<>=$§ '`.
* @param string $text Text content or html content to truncate.
* @param int $maxChars Max text chars or max. html content chars.
* @param bool|NULL $isHtml If `TRUE`, first param will be force truncated in html mode,
* If `FALSE`, first param will be force truncated in text mode,
* If `NULL`, there is used possibly configured property
* `\MvcCore\Ext\View\Helpers\::$alwaysHtmlMode` and if not configured,
* there is automatically detected if first param contains any html tag(s).
* @return string
*/
public function Truncate ($text, $maxChars = 200, $isHtml = NULL) {
if ($isHtml === NULL) {
$isHtml = $this->alwaysHtmlMode;
if ($isHtml === NULL) {
preg_match("#\<(.+)\>#", $text, $m);
$isHtml = count($m) > 0;
}
}
if ($isHtml) {
return $this->truncateHtml($text, $maxChars);
} else {
return $this->truncateText($text, $maxChars);
}
}
/**
* Truncate text with any HTML tags inside, keep all tags and truncate text content only.
* Clean all possible punctuation and brackets before three dots: `',.:;?!+"\'-–()[]{}<>=$§ '`.
* @param string $text
* @param int $maxChars
* @return string
*/
protected function truncateHtml (& $text, $maxChars) {
$texts = [];
$index = 0;
$charsCount = 0;
// explode all html content to array with text contents and html tags
while (TRUE) {
$openTagPos = mb_strpos($text, '<', $index);
if ($openTagPos === FALSE) {
$subText = mb_substr($text, $index);
$subText = preg_replace("#\s+#", ' ', str_replace(' ', ' ', $subText));
$texts[] = [TRUE, $subText, $charsCount, mb_strlen($subText)];
break;
}
$closeTagPos = mb_strpos($text, '>', $openTagPos + 1);
if ($closeTagPos === FALSE) {
$subText = mb_substr($text, $index);
$subText = preg_replace("#\s+#", ' ', str_replace(' ', ' ', $subText));
$texts[] = [TRUE, $subText, $charsCount, mb_strlen($subText)];
break;
}
$subText = mb_substr($text, $index, $openTagPos - $index);
$subText = preg_replace("#\s+#", ' ', str_replace(' ', ' ', $subText));
$subTag = mb_substr($text, $openTagPos, $closeTagPos + 1 - $openTagPos);
$subTextLength = mb_strlen($subText);
$subTagLength = mb_strlen($subTag);
if ($subTextLength > 0)
$texts[] = [TRUE, $subText, $charsCount, $subTextLength];
if ($subTagLength > 0)
$texts[] = [FALSE, $subTag, 0, $subTagLength];
$charsCount += $subTextLength;
if ($charsCount >= $maxChars) break;
$index = $closeTagPos + 1;
}
// if there are more chars in text content:
if ($charsCount > $maxChars) {
$threeDotsText = $this->threeDotsTexts[1];
if ($threeDotsText === NULL) $threeDotsText = '…';
// try to put three dots from the end into text content where necessary
for ($i = count($texts) - 1; $i > -1; $i -= 1) {
list($type, $subText, $charsCount, $subTextLength) = $texts[$i];
if (!$type) continue;
$maxCharsLocal = $maxChars - $charsCount;
$subText = mb_substr($subText, 0, $maxCharsLocal);
$lastSpacePos = mb_strrpos($subText, ' ');
if ($lastSpacePos !== FALSE) {
$subText = rtrim($subText, ',.:;?!+"\'-–()[]{}<>=$§ ');
if (mb_strlen($subText) === 0) {
unset($texts[$i]);
} else {
$texts[$i][1] = mb_substr($subText, 0, $lastSpacePos) . $threeDotsText;
break;
}
} else {
unset($texts[$i]);
}
}
// implode truncated text content and all tags (not truncated) back together
$result = '';
foreach ($texts as $textRecord) $result .= $textRecord[1];
return $result;
} else {
return $text;
}
}
/**
* Truncate simple text without any HTML tags.
* Clean all possible punctuation and brackets before three dots: `',.:;?!+"\'-–()[]{}<>=$§ '`.
* @param string $text
* @param int $maxChars
* @return void
*/
protected function truncateText (& $text, $maxChars) {
if (mb_strlen($text) > $maxChars) {
$text = preg_replace("#\s+#", ' ', $text);
$text = mb_substr($text, 0, $maxChars);
$lastSpacePos = mb_strrpos($text, ' ');
if ($lastSpacePos !== FALSE) {
$threeDotsText = $this->threeDotsTexts[0];
if ($threeDotsText === NULL) $threeDotsText = '...';
$text = rtrim($text, ',.:;?!+"\'-–()[]{}<>=$§ ');
$text = mb_substr($text, 0, $lastSpacePos) . $threeDotsText;
}
}
return $text;
}
}