TextUtils::textTruncate()   B
last analyzed

Complexity

Conditions 8
Paths 12

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 16
rs 8.4444
cc 8
nc 12
nop 3
1
<?php
2
/**
3
 * Text utils file.
4
 *
5
 * @package App
6
 *
7
 * @copyright YetiForce S.A.
8
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 */
11
12
namespace App;
13
14
/**
15
 * Text utils class.
16
 */
17
class TextUtils
18
{
19
	/**
20
	 * Get text length.
21
	 *
22
	 * @param string $text
23
	 *
24
	 * @return int
25
	 */
26
	public static function getTextLength($text)
27
	{
28
		return null !== $text ? mb_strlen($text) : 0;
0 ignored issues
show
introduced by
The condition null !== $text is always true.
Loading history...
29
	}
30
31
	/**
32
	 * Truncating text.
33
	 *
34
	 * @param string   $text
35
	 * @param bool|int $length
36
	 * @param bool     $addDots
37
	 *
38
	 * @return string
39
	 */
40
	public static function textTruncate($text, $length = false, $addDots = true)
41
	{
42
		if (!$length) {
43
			$length = Config::main('listview_max_textlength');
44
		}
45
		$textLength = 0;
46
		if (null !== $text) {
0 ignored issues
show
introduced by
The condition null !== $text is always true.
Loading history...
47
			$textLength = mb_strlen($text);
48
		}
49
		if ((!$addDots && $textLength > $length) || ($addDots && $textLength > $length + 2)) {
50
			$text = mb_substr($text, 0, $length, Config::main('default_charset'));
51
			if ($addDots) {
52
				$text .= '...';
53
			}
54
		}
55
		return $text;
56
	}
57
58
	/**
59
	 * Truncating HTML by words.
60
	 *
61
	 * @param string $html
62
	 * @param int    $length
63
	 * @param string $ending
64
	 *
65
	 * @return string
66
	 */
67
	public static function htmlTruncateByWords(string $html, int $length = 0, string $ending = '...'): string
68
	{
69
		if (!$length) {
70
			$length = Config::main('listview_max_textlength');
71
		}
72
		if (\strlen(strip_tags($html)) <= $length) {
73
			return $html;
74
		}
75
		$totalLength = \mb_strlen($ending);
76
		$openTagsLength = 0;
77
		$openTags = [];
78
		preg_match_all('/(<.+?>)?([^<>]*)/s', $html, $tags, PREG_SET_ORDER);
79
		$html = '';
80
		foreach ($tags as $tag) {
81
			$tagLength = \mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $tag[2]));
82
			if (($totalLength + $tagLength + $openTagsLength) >= $length) {
83
				if (empty($html)) {
84
					preg_match('/^<\s*([^\s>!]+).*?>$/s', $tag[1], $tagName);
85
					$openTags[] = $tagName[1];
86
					$html = $tag[1] . self::textTruncate($tag[2], $length - 3, false);
87
				}
88
				break;
89
			}
90
			if (!empty($tag[1])) {
91
				if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $tag[1])) {
92
					// if tag is a closing tag
93
				} elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $tag[1], $tagName)) {
94
					$pos = array_search(strtolower($tagName[1]), $openTags);
95
					if (false !== $pos) {
96
						unset($openTags[$pos]);
97
						$openTagsLength -= \mb_strlen("</{$tagName[1]}>");
98
					}
99
				} elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $tag[1], $tagName)) {
100
					array_unshift($openTags, strtolower($tagName[1]));
101
					$openTagsLength += \mb_strlen("</{$tagName[1]}>");
102
				}
103
			}
104
			$html .= $tag[0];
105
			$totalLength += $tagLength;
106
		}
107
		$html .= $ending;
108
		if ($openTags) {
109
			$html .= '</' . implode('></', $openTags) . '>';
110
		}
111
		return $html;
112
	}
113
114
	/**
115
	 * Truncating HTML.
116
	 *
117
	 * @param string $html
118
	 * @param int    $length
119
	 * @param string $ending
120
	 *
121
	 * @return string
122
	 */
123
	public static function htmlTruncate(string $html, int $length = 255, string $ending = '...'): string
124
	{
125
		if (\strlen($html) <= $length) {
126
			return $html;
127
		}
128
		$totalLength = \mb_strlen($ending);
129
		$openTagsLength = 0;
130
		$openTags = [];
131
		preg_match_all('/(<.+?>)?([^<>]*)/s', $html, $tags, PREG_SET_ORDER);
132
		$html = '';
133
		foreach ($tags as $tag) {
134
			$tagLength = \mb_strlen($tag[0]);
135
			if (($totalLength + $tagLength + $openTagsLength) >= $length) {
136
				if (empty($html)) {
137
					preg_match('/^<\s*([^\s>!]+).*?>$/s', $tag[1], $tagName);
138
					$openTags[] = $tagName[1];
139
					$html = $tag[1] . self::textTruncate($tag[2], $length - 3, false);
140
				}
141
				break;
142
			}
143
			if (!empty($tag[1])) {
144
				if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $tag[1])) {
145
					// if tag is a closing tag
146
				} elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $tag[1], $tagName)) {
147
					$pos = array_search(strtolower($tagName[1]), $openTags);
148
					if (false !== $pos) {
149
						unset($openTags[$pos]);
150
						$openTagsLength -= \mb_strlen("</{$tagName[1]}>");
151
					}
152
				} elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $tag[1], $tagName)) {
153
					array_unshift($openTags, strtolower($tagName[1]));
154
					$openTagsLength += \mb_strlen("</{$tagName[1]}>");
155
				}
156
			}
157
			$html .= $tag[0];
158
			$totalLength += $tagLength;
159
		}
160
		$html .= $ending;
161
		if ($openTags) {
162
			$html .= '</' . implode('></', $openTags) . '>';
163
		}
164
		return $html;
165
	}
166
167
	/**
168
	 * Get all attributes of a tag.
169
	 *
170
	 * @param string $tag
171
	 *
172
	 * @return string[]
173
	 */
174
	public static function getTagAttributes(string $tag): array
175
	{
176
		$dom = new \DOMDocument('1.0', 'UTF-8');
177
		$previousValue = libxml_use_internal_errors(true);
178
		$dom->loadHTML('<?xml encoding="utf-8"?>' . $tag);
179
		libxml_clear_errors();
180
		libxml_use_internal_errors($previousValue);
181
		$tag = $dom->getElementsByTagName('*')->item(2);
182
		$attributes = [];
183
		if ($tag->hasAttributes()) {
184
			foreach ($tag->attributes as $attr) {
185
				$attributes[$attr->name] = $attr->value;
186
			}
187
		}
188
		return $attributes;
189
	}
190
}
191