Passed
Push — master ( d9e5dd...36764d )
by Spuds
01:07 queued 26s
created

Util::shorten_html()   F

Complexity

Conditions 19
Paths 1153

Size

Total Lines 111
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 33.7982

Importance

Changes 0
Metric Value
cc 19
eloc 50
nc 1153
nop 4
dl 0
loc 111
ccs 38
cts 58
cp 0.6552
crap 33.7982
rs 0.3499
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Utility functions, such as to handle multi byte strings
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * @version 1.1.9
11
 *
12
 */
13
14
/**
15
 * Utility functions, such as to handle multi byte strings
16
 * Note: some of these might be deprecated or removed in the future.
17
 */
18
class Util
19
{
20
	static protected $_entity_check_reg = '~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~';
21
22
	/**
23
	 * Converts invalid / disallowed / out of range entities to nulls
24
	 *
25
	 * @param string $string
26
	 */
27 1
	public static function entity_fix($string)
28
	{
29 1
		$num = $string[0] === 'x' ? hexdec(substr($string, 1)) : (int) $string;
30
31
		// We don't allow control characters, characters out of range, byte markers, etc
32 1
		if ($num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num == 0x202D || $num == 0x202E)
33 1
			return '';
34
		else
35
			return '&#' . $num . ';';
36
	}
37
38
	/**
39
	 * Performs an htmlspecialchars on a string, using UTF-8 character set
40
	 * Optionally performs an entity_fix to null any invalid character entities from the string
41
	 *
42
	 * @param string $string
43
	 * @param int $quote_style integer or constant representation of one
44
	 * @param string $charset only UTF-8 allowed
45
	 * @param bool $double true will allow double encoding, false will not encode existing html entities,
46
	 */
47 4
	public static function htmlspecialchars($string, $quote_style = ENT_COMPAT, $charset = 'UTF-8', $double = false)
48
	{
49 4
		global $modSettings;
50
51 4
		if (empty($string))
52 4
			return $string;
53
54 4
		if (empty($modSettings['disableEntityCheck']))
55 4
			$check = preg_replace_callback('~(&amp;#(\d{1,7}|x[0-9a-fA-F]{1,6});)~', 'entity_fix__callback', htmlspecialchars($string, $quote_style, $charset, $double));
56
		else
57 1
			$check = htmlspecialchars($string, $quote_style, $charset, $double);
58
59 4
		return $check;
60
	}
61
62
	/**
63
	 * Trims tabs, newlines, carriage returns, spaces, vertical tabs and null bytes
64
	 * and any number of space characters from the start and end of a string
65
	 *
66
	 * - Optionally performs an entity_fix to null any invalid character entities from the string
67
	 *
68
	 * @param string $string
69
	 */
70 4
	public static function htmltrim($string)
71
	{
72 4
		global $modSettings;
73
74
		// Preg_replace for any kind of whitespace or invisible separator
75 4
		// and invisible control characters and unused code points
76
		$space_chars = '\p{Z}\p{C}';
77 4
78 4
		if (empty($modSettings['disableEntityCheck']))
79
		{
80
			$check = preg_replace('~^(?:[' . $space_chars . ']|&nbsp;)+|(?:[' . $space_chars . ']|&nbsp;)+$~u', '', preg_replace_callback(self::$_entity_check_reg, 'entity_fix__callback', $string));
81
		}
82 4
		else
83
		{
84
			$check = preg_replace('~^(?:[' . $space_chars . ']|&nbsp;)+|(?:[' . $space_chars . ']|&nbsp;)+$~u', '', $string);
85
		}
86
87
		return $check;
88
	}
89
90
	/**
91
	 * Perform a strpos search on a multi-byte string
92
	 *
93
	 * - Optionally performs an entity_fix to null any invalid character entities from the string before the search
94
	 *
95 4
	 * @param string $haystack what to search in
96
	 * @param string $needle what is being looked for
97 4
	 * @param int $offset where to start, assumed 0
98
	 * @param bool $right set to true to mimic strrpos functions
99 4
	 */
100 4
	public static function strpos($haystack, $needle, $offset = 0, $right = false)
101 4
	{
102
		global $modSettings;
103
104
		$haystack_check = empty($modSettings['disableEntityCheck']) ? preg_replace_callback(self::$_entity_check_reg, 'entity_fix__callback', $haystack) : $haystack;
105 4
		$haystack_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~u', $haystack_check, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
106 2
		$count = 0;
107 2
108 2
		// From the right side, like mb_strrpos instead
109
		if ($right)
110
		{
111 4
			$haystack_arr = array_reverse($haystack_arr);
112 4
			$count = count($haystack_arr) - 1;
113 2
		}
114 2
115
		// Single character search, lets go
116
		if (strlen($needle) === 1)
117
		{
118 2
			$result = array_search($needle, array_slice($haystack_arr, $offset));
119 2
			return is_int($result) ? ($right ? $count - ($result + $offset) : $result + $offset) : false;
120 2
		}
121 2
		else
122
		{
123 2
			$needle_check = empty($modSettings['disableEntityCheck']) ? preg_replace_callback(self::$_entity_check_reg, 'entity_fix__callback', $needle) : $needle;
124 2
			$needle_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~u', $needle_check, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
125
			$needle_arr = $right ? array_reverse($needle_arr) : $needle_arr;
126 1
			$needle_size = count($needle_arr);
127 1
128 1
			$result = array_search($needle_arr[0], array_slice($haystack_arr, $offset));
129
			while ((int) $result === $result)
130 1
			{
131 1
				$offset += $result;
132
				if (array_slice($haystack_arr, $offset, $needle_size) === $needle_arr)
133 1
					return $right ? ($count - $offset - $needle_size + 1) : $offset;
134
135
				$result = array_search($needle_arr[0], array_slice($haystack_arr, ++$offset));
136
			}
137
138
			return false;
139
		}
140
	}
141
142
	/**
143
	 * Perform a substr operation on multi-byte strings
144
	 *
145
	 * - Optionally performs an entity_fix to null any invalid character entities from the string before the operation
146 7
	 *
147
	 * @param string $string
148 7
	 * @param string $start
149
	 * @param int|null $length
150 7
	 */
151 7
	public static function substr($string, $start, $length = null)
152
	{
153
		global $modSettings;
154
155 7
		if (empty($modSettings['disableEntityCheck']))
156
			$ent_arr = preg_split('~(&#\d{1,7};|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~u', preg_replace_callback(self::$_entity_check_reg, 'entity_fix__callback', $string), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
157
		else
158
			$ent_arr = preg_split('~(&#021;|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~u', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
159
160
		return $length === null ? implode('', array_slice($ent_arr, $start)) : implode('', array_slice($ent_arr, $start, $length));
0 ignored issues
show
Bug introduced by
$start of type string is incompatible with the type integer expected by parameter $offset of array_slice(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

160
		return $length === null ? implode('', array_slice($ent_arr, /** @scrutinizer ignore-type */ $start)) : implode('', array_slice($ent_arr, $start, $length));
Loading history...
161
	}
162
163
	/**
164
	 * Converts a multi-byte string to lowercase
165 14
	 *
166
	 * - Prefers to use mb_ functions if available, otherwise will use charset substitution tables
167 14
	 *
168 14
	 * @param string $string
169
	 */
170
	public static function strtolower($string)
171
	{
172
		if (function_exists('mb_strtolower'))
173
			return mb_strtolower($string, 'UTF-8');
174
		else
175
		{
176
			require_once(SUBSDIR . '/Charset.subs.php');
177
			return utf8_strtolower($string);
178
		}
179
	}
180
181
	/**
182
	 * Converts a multi-byte string to uppercase
183 3
	 *
184
	 * Prefers to use mb_ functions if available, otherwise will use charset substitution tables
185 3
	 *
186 3
	 * @param string $string
187
	 */
188
	public static function strtoupper($string)
189
	{
190
		if (function_exists('mb_strtoupper'))
191
			return mb_strtoupper($string, 'UTF-8');
192
		else
193
		{
194
			require_once(SUBSDIR . '/Charset.subs.php');
195
			return utf8_strtoupper($string);
196
		}
197
	}
198
199
	/**
200
	 * Cuts off a multi-byte string at a certain length
201
	 *
202
	 * - Optionally performs an entity_fix to null any invalid character entities from the string prior to the length check
203
	 * - Use this when the number of actual characters (&nbsp; = 6 not 1) must be <= length not the displayable,
204
	 * for example db field compliance to avoid overflow
205
	 *
206
	 * @param string $string
207
	 * @param int $length
208
	 */
209
	public static function truncate($string, $length)
210
	{
211
		global $modSettings;
212
213
		// Set a list of common functions.
214
		$ent_list = empty($modSettings['disableEntityCheck']) ? '&(#\d{1,7}|quot|amp|lt|gt|nbsp);' : '&(#021|quot|amp|lt|gt|nbsp);';
215
216
		if (empty($modSettings['disableEntityCheck']))
217
			$string = preg_replace_callback(self::$_entity_check_reg, 'entity_fix__callback', $string);
218
219
		preg_match('~^(' . $ent_list . '|.){' . self::strlen(substr($string, 0, $length)) . '}~u', $string, $matches);
220
		$string = $matches[0];
221
		while (strlen($string) > $length)
222
			$string = preg_replace('~(?:' . $ent_list . '|.)$~u', '', $string);
223
224
		return $string;
225
	}
226
227
	/**
228
	 * Shorten a string of text
229
	 *
230
	 * What it does:
231
	 *
232
	 * - Shortens a text string to a given visual length
233
	 * - Considers certain html entities as 1 in length, &amp; &nbsp; etc
234
	 * - Optionally adds ending ellipsis that honor length or are appended
235
	 * - Optionally attempts to break the string on a word boundary approximately at the allowed length
236
	 * - If using cutword and the resulting length is < len minus buffer then it is truncated to length plus an ellipsis.
237
	 * - Respects internationalization characters, html spacing and entities as one character.
238
	 * - Returns the shortened string.
239
	 * - Does not account for html tags, ie <b>test</b> is 11 characters not 4
240
	 *
241
	 * @param string $string The string to shorten
242
	 * @param int $length The length to cut the string to
243 2
	 * @param bool $cutword try to cut at a word boundary
244
	 * @param string $ellipsis characters to add at the end of a cut string
245
	 * @param bool $exact set true to include ellipsis in the allowed length, false will append instead
246 2
	 * @param int $buffer maximum length underflow to allow when cutting on a word boundary
247
	 */
248
	public static function shorten_text($string, $length = 384, $cutword = false, $ellipsis = '...', $exact = true, $buffer = 12)
249 2
	{
250 2
		// Does len include the ellipsis or are the ellipsis appended
251
		$ending = !empty($ellipsis) && $exact ? self::strlen($ellipsis) : 0;
252
253 1
		// If its to long, cut it down to size
254 1
		if (self::strlen($string) > $length)
255 1
		{
256
			// Try to cut on a word boundary
257
			if ($cutword)
258 1
			{
259 1
				$string = self::substr($string, 0, $length - $ending);
260
				$space_pos = self::strpos($string, ' ', 0, true);
261 1
262 1
				// Always one clown in the audience who likes long words or not using the spacebar
263
				if (!empty($space_pos) && ($length - $space_pos <= $buffer))
264
					$string = self::substr($string, 0, $space_pos);
265 1
266
				$string = rtrim($string) . ($ellipsis ? $ellipsis : '');
267 2
			}
268
			else
269
				$string = self::substr($string, 0, $length - $ending) . ($ellipsis ? $ellipsis : '');
270
		}
271
272
		return $string;
273
	}
274
275
	/**
276
	 * Truncate a string up to a number of characters while preserving whole words and HTML tags
277
	 *
278
	 * This function is an adaption of the cake php function truncate in utility string.php (MIT)
279
	 *
280
	 * @param string $string text to truncate.
281
	 * @param integer $length length of returned string
282 1
	 * @param string $ellipsis characters to add at the end of cut string, like ...
283
	 * @param boolean $exact If to account for the $ellipsis length in returned string length
284
	 *
285 1
	 * @return string Trimmed string.
286 1
	 */
287
	public static function shorten_html($string, $length = 384, $ellipsis = '...', $exact = true)
288
	{
289 1
		// If its shorter than the maximum length, while accounting for html tags, simply return
290 1
		if (self::strlen(preg_replace('~<.*?>~', '', $string)) <= $length)
291 1
			return $string;
292
293
		// Start off empty
294 1
		$total_length = $exact ? self::strlen($ellipsis) : 0;
295
		$open_tags = array();
296
		$truncate = '';
297 1
298
		// Group all html open and closing tags, [1] full tag with <> [2] basic tag name [3] tag content
299
		preg_match_all('~(<\/?([\w+]+)[^>]*>)?([^<>]*)~', $string, $tags, PREG_SET_ORDER);
300 1
301 1
		// Walk down the stack of tags
302
		foreach ($tags as $tag)
303 1
		{
304 1
			// If this tag has content
305
			if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2]))
306
			{
307
				// Opening tag add the closing tag to the top of the stack
308
				if (preg_match('~<[\w]+[^>]*>~s', $tag[0]))
309
					array_unshift($open_tags, $tag[2]);
310
				// Closing tag
311
				elseif (preg_match('~<\/([\w]+)[^>]*>~s', $tag[0], $close_tag))
312
				{
313 1
					// Remove its starting tag
314
					$pos = array_search($close_tag[1], $open_tags);
315
					if ($pos !== false)
316 1
						array_splice($open_tags, $pos, 1);
0 ignored issues
show
Bug introduced by
It seems like $pos can also be of type string; however, parameter $offset of array_splice() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

316
						array_splice($open_tags, /** @scrutinizer ignore-type */ $pos, 1);
Loading history...
317
				}
318
			}
319 1
320
			// Add this (opening or closing) tag to $truncate
321
			$truncate .= $tag[1];
322 1
323 1
			// Calculate the length of the actual tag content, accounts for html entities as a single characters
324
			$content_length = self::strlen($tag[3]);
325 1
326 1
			// Have we exceeded the allowed length limit, only add in what we are allowed
327 1
			if ($content_length + $total_length > $length)
328
			{
329
				// The number of characters which we can still return
330
				$remaining = $length - $total_length;
331
				$truncate .= self::substr($tag[3], 0, $remaining);
332 1
				break;
333 1
			}
334
			// Still room to go so add the tag content and continue
335
			else
336
			{
337 1
				$truncate .= $tag[3];
338 1
				$total_length += $content_length;
339 1
			}
340
341
			// Are we there yet?
342 1
			if ($total_length >= $length)
343 1
				break;
344 1
		}
345
346
		// Our truncated string up to the last space
347 1
		$space_pos = self::strpos($truncate, ' ', 0, true);
348 1
		$space_pos = empty($space_pos) ? $length : $space_pos;
349 1
		$truncate_check = self::substr($truncate, 0, $space_pos);
350 1
351
		// Make sure this would not cause a cut in the middle of a tag
352
		$lastOpenTag = (int) self::strpos($truncate_check, '<', 0, true);
353
		$lastCloseTag = (int) self::strpos($truncate_check, '>', 0, true);
354
		if ($lastOpenTag > $lastCloseTag)
355
		{
356
			// Find the last full open tag in our truncated string, its what was being cut
357
			preg_match_all('~<[\w]+[^>]*>~s', $truncate, $lastTagMatches);
358
			$last_tag = array_pop($lastTagMatches[0]);
359
360
			// Set the space to just after the last tag
361 1
			$space_pos = self::strpos($truncate, $last_tag, 0, true) + strlen($last_tag);
362
			$space_pos = empty($space_pos) ? $length : $space_pos;
363
		}
364 1
365 1
		// Look at what we are going to cut off the end of our truncated string
366 1
		$bits = self::substr($truncate, $space_pos);
367
368
		// Does it cut a tag off, if so we need to know so it can be added back at the cut point
369
		preg_match_all('~<\/([a-z]+)>~', $bits, $dropped_tags, PREG_SET_ORDER);
370
		if (!empty($dropped_tags))
371
		{
372
			if (!empty($open_tags))
373
			{
374
				foreach ($dropped_tags as $closing_tag)
375
				{
376
					if (!in_array($closing_tag[1], $open_tags))
377
						array_unshift($open_tags, $closing_tag[1]);
378
				}
379
			}
380
			else
381
			{
382
				foreach ($dropped_tags as $closing_tag)
383 1
					$open_tags[] = $closing_tag[1];
384
			}
385
		}
386 1
387
		// Cut it
388
		$truncate = self::substr($truncate, 0, $space_pos);
389 1
390 1
		// Dot dot dot
391
		$truncate .= $ellipsis;
392 1
393
		// Finally close any html tags that were left open
394
		foreach ($open_tags as $tag)
395
			$truncate .= '</' . $tag . '>';
396
397
		return $truncate;
398
	}
399
400 2
	/**
401
	 * Converts the first character of a multi-byte string to uppercase
402 2
	 *
403
	 * @param string $string
404
	 */
405
	public static function ucfirst($string)
406
	{
407
		return self::strtoupper(self::substr($string, 0, 1)) . self::substr($string, 1);
408
	}
409
410 2
	/**
411
	 * Converts the first character of each work in a multi-byte string to uppercase
412 2
	 *
413 2
	 * @param string $string
414 2
	 */
415 2
	public static function ucwords($string)
416
	{
417
		$words = preg_split('~([\s\r\n\t]+)~', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
418
		for ($i = 0, $n = count($words); $i < $n; $i += 2)
419
			$words[$i] = self::ucfirst($words[$i]);
420
		return implode('', $words);
421
	}
422
423 10
	/**
424
	 * Returns the length of multi-byte string
425 10
	 *
426
	 * @param string $string
427 10
	 */
428 10
	public static function strlen($string)
429 10
	{
430 10
		global $modSettings;
431 10
432
		if (empty($string))
433
		{
434
			return 0;
435
		}
436
437
		if (empty($modSettings['disableEntityCheck']))
438
		{
439
			$ent_list = '&(#\d{1,7}|quot|amp|lt|gt|nbsp);';
440
			if (function_exists('mb_strlen'))
441
				return mb_strlen(preg_replace('~' . $ent_list . '|.~u', '_', $string), 'UTF-8');
442
			else
443
				return strlen(preg_replace('~' . $ent_list . '|.~u', '_', preg_replace_callback(self::$_entity_check_reg, 'entity_fix__callback', $string)));
444
		}
445
		else
446
		{
447
			$ent_list = '&(#021|quot|amp|lt|gt|nbsp);';
448
			return strlen(preg_replace('~' . $ent_list . '|.~u', '_', $string));
449
		}
450
	}
451
452
	/**
453
	 * Remove slashes recursively.
454
	 *
455
	 * What it does:
456
	 *
457
	 * - removes slashes, recursively, from the array or string var.
458
	 * - effects both keys and values of arrays.
459
	 * - calls itself recursively to handle arrays of arrays.
460
	 *
461
	 * @todo not used, consider removing
462
	 * @deprecated since 1.0
463
	 *
464
	 * @param mixed[]|string $var
465
	 * @param int $level = 0
466
	 * @return array|string
467
	 */
468
	public static function stripslashes_recursive($var, $level = 0)
469
	{
470
		if (!is_array($var))
471
			return stripslashes($var);
472
473
		// Reindex the array without slashes, this time.
474
		$new_var = array();
475
476
		// Strip the slashes from every element.
477
		foreach ($var as $k => $v)
478
			$new_var[stripslashes($k)] = $level > 25 ? null : self::stripslashes_recursive($v, $level + 1);
0 ignored issues
show
Deprecated Code introduced by
The function Util::stripslashes_recursive() has been deprecated: since 1.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

478
			$new_var[stripslashes($k)] = $level > 25 ? null : /** @scrutinizer ignore-deprecated */ self::stripslashes_recursive($v, $level + 1);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
479
480
		return $new_var;
481
	}
482
483
	/**
484
	 * Removes url stuff from the array/variable.
485
	 *
486
	 * What it does:
487
	 *
488
	 * - takes off url encoding (%20, etc.) from the array or string var.
489
	 * - importantly, does it to keys too!
490
	 * - calls itself recursively if there are any sub arrays.
491
	 *
492
	 * @todo not used, consider removing
493
	 * @deprecated since 1.0
494
	 *
495
	 * @param mixed[]|string $var
496
	 * @param int $level = 0
497
	 * @return array|string
498
	 */
499
	public static function urldecode_recursive($var, $level = 0)
500
	{
501
		if (!is_array($var))
502
			return urldecode($var);
503
504
		// Reindex the array...
505
		$new_var = array();
506
507
		// Add the htmlspecialchars to every element.
508
		foreach ($var as $k => $v)
509
			$new_var[urldecode($k)] = $level > 25 ? null : self::urldecode_recursive($v, $level + 1);
0 ignored issues
show
Deprecated Code introduced by
The function Util::urldecode_recursive() has been deprecated: since 1.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

509
			$new_var[urldecode($k)] = $level > 25 ? null : /** @scrutinizer ignore-deprecated */ self::urldecode_recursive($v, $level + 1);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
510
511
		return $new_var;
512
	}
513
514
	/**
515
	 * Unescapes any array or variable.
516
	 *
517
	 * What it does:
518
	 *
519
	 * - unescapes, recursively, from the array or string var.
520
	 * - effects both keys and values of arrays.
521
	 * - calls itself recursively to handle arrays of arrays.
522
	 *
523
	 * @todo not used, consider removing
524
	 * @deprecated since 1.0
525
	 *
526
	 * @param mixed[]|string $var
527
	 * @return array|string
528
	 */
529
	public static function unescapestring_recursive($var)
530
	{
531
		$db = database();
532
533
		if (!is_array($var))
534
		return $db->unescape_string($var);
535
536
		// Reindex the array without slashes, this time.
537
		$new_var = array();
538
539
		// Strip the slashes from every element.
540
		foreach ($var as $k => $v)
541
			$new_var[$db->unescape_string($k)] = self::unescapestring_recursive($v);
0 ignored issues
show
Deprecated Code introduced by
The function Util::unescapestring_recursive() has been deprecated: since 1.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

541
			$new_var[$db->unescape_string($k)] = /** @scrutinizer ignore-deprecated */ self::unescapestring_recursive($v);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
542
543
		return $new_var;
544
	}
545
546
	/**
547
	 * Wrappers for unserialize
548
	 * What it does:
549
	 *
550
	 * - if using PHP < 7 it will use ext/safe_unserialize
551 4
	 * - if using PHP > 7 will use the built in unserialize
552
	 *
553 4
	 * @param string $string The string to unserialize
554
	 * @param string[] $options Optional, mimic the PHP 7+ option,
555 4
	 *                          see PHP documentation for the details
556 4
	 *                          additionally, it doesn't allow to use the option:
557
	 *                            allowed_classes => true
558
	 *                          that is reverted to false.
559
	 * @return mixed
560
	 */
561
	public static function unserialize($string, $options = array())
562
	{
563
		static $function = null;
564
565
		if ($function === null)
566
		{
567
			if (version_compare(PHP_VERSION, '7', '>='))
568 4
			{
569 4
				$function = 'unserialize';
570 4
			}
571 4
			else
572
			{
573 4
				require_once(EXTDIR . '/serialize.php');
574
				$function = 'ElkArte\\ext\\upgradephp\\safe_unserialize';
575
			}
576
		}
577
578
		if (!isset($options['allowed_classes']) || $options['allowed_classes'] === true)
579
		{
580
			$options['allowed_classes'] = false;
581
		}
582
583
		return @$function($string, $options);
584
	}
585
586
	/*
587
	* Provide a PHP 8.1 version of strftime
588
	*
589
	* @param string $format of the date/time to return
590
	* @param int|null $timestamp to convert
591
	* @return string|false
592
	*/
593
	public static function strftime(string $format, int $timestamp = null)
594
	{
595
		if (function_exists('strftime') && (PHP_VERSION_ID < 80100))
596
			return \strftime($format, $timestamp);
597
598
		if (is_null($timestamp))
599
			$timestamp = time();
600
601
		$date_equivalents = array (
602
			'%a' => 'D',
603
			'%A' => 'l',
604
			'%d' => 'd',
605
			'%e' => 'j',
606
			'%j' => 'z',
607
			'%u' => 'N',
608
			'%w' => 'w',
609
			// Week
610
			'%U' => 'W', // Week Number of the given year
611
			'%V' => 'W',
612
			'%W' => 'W',
613
			// Month
614
			'%b' => 'M',
615
			'%B' => 'F',
616
			'%h' => 'M',
617
			'%m' => 'm',
618
			// Year
619
			'%C' => 'y', // Two digit representation of the century
620
			'%g' => 'y',
621
			'%G' => 'y',
622
			'%y' => 'y',
623
			'%Y' => 'Y',
624
			// Time
625
			'%H' => 'H',
626
			'%k' => 'G',
627
			'%I' => 'h',
628
			'%l' => 'g',
629
			'%M' => 'i',
630
			'%p' => 'A',
631
			'%P' => 'a',
632
			'%r' => 'H:i:s a',
633
			'%R' => 'H:i',
634
			'%S' => 's',
635
			'%T' => 'H:i:s',
636
			'%X' => 'h:i:s', // Preferred time representation based upon locale
637
			'%z' => 'O',
638
			'%Z' => 'T',
639
			// Time and Date Stamps
640
			'%c' => 'c',
641
			'%D' => 'm/d/y',
642
			'%F' => 'y/m/d',
643
			'%s' => 'U',
644
			'%x' => '', // Locale based date representation
645
			// Misc
646
			'%n' => "\n",
647
			'%t' => "\t",
648
			'%%' => '%',
649
		);
650
651
		$format = preg_replace_callback(
652
			'/%[A-Za-z]{1}/',
653
			function($matches) use ($timestamp, $date_equivalents)
654
			{
655
				$new_format = str_replace(array_keys($date_equivalents), array_values($date_equivalents), $matches[0]);
656
				return date($new_format, $timestamp);
657
			},
658
			$format
659
		);
660
661
		return $format;
662
	}
663
664
	/*
665
	* Provide a PHP 8.1 version of gmstrftime
666
	*
667
	* @param string $format of the date/time to return
668
	* @param int|null $timestamp to convert
669
	* @return string|false
670
	*/
671
	public static function gmstrftime(string $format, int $timestamp = null)
672
	{
673
		if (function_exists('gmstrftime') && (PHP_VERSION_ID < 80100))
674
			return \gmstrftime($format, $timestamp);
675
676
		return self::strftime($format, $timestamp);
677
	}
678
679
	/**
680
	 * Checks if the string contains any 4byte chars (emoji) and if so,
681
	 * converts them into &#x...; HTML entities.
682
	 *
683
	 * @param string $string
684
	 * @return string
685
	 */
686
	static function clean_4byte_chars($string)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
687
	{
688
		global $modSettings;
689
690
		if (!empty($modSettings['using_utf8mb4']))
691
			return $string;
692
693
		$result = $string;
694
695
		//  If we are in the 4-byte range
696
		if (preg_match('~[\x{10000}-\x{10FFFF}]~u', $string))
697
		{
698
			$ord = array_map('ord', str_split($string));
0 ignored issues
show
Bug introduced by
It seems like str_split($string) can also be of type true; however, parameter $array of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

698
			$ord = array_map('ord', /** @scrutinizer ignore-type */ str_split($string));
Loading history...
699
700
			// Byte length
701
			$length = strlen($string);
702
			$result = '';
703
704
			// Look for a 4byte marker
705
			for ($i = 0; $i < $length; $i++)
706
			{
707
				// The first byte of a 4-byte character encoding starts with the bytes 0xF0-0xF4 (240 <-> 244)
708
				// but look all the way to 247 for safe measure
709
				$ord1 = $ord[$i];
710
				if ($ord1 >= 240 && $ord1 <= 247)
711
				{
712
					// Replace it with the corresponding html entity
713
					$entity = self::uniord(chr($ord[$i]) . chr($ord[$i + 1]) . chr($ord[$i + 2]) . chr($ord[$i + 3]));
714
715
					if ($entity === false)
716
						$result .= "\xEF\xBF\xBD";
717
					else
718
						$result .= '&#x' . dechex($entity) . ';';
719
720
					$i += 3;
721
				}
722
				else
723
					$result .= $string[$i];
724
			}
725
		}
726
727
		return $result;
728
	}
729
730
	/**
731
	 * Converts a 4byte char into the corresponding HTML entity code.
732
	 *
733
	 * This function is derived from:
734
	 * http://www.greywyvern.com/code/php/utf8_html
735
	 *
736
	 * @param string $c
737
	 * @return integer|false
738
	 */
739
	static function uniord($c)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
740
	{
741
		if (ord($c[0]) >= 0 && ord($c[0]) <= 127)
742
			return ord($c[0]);
743
744
		if (ord($c[0]) >= 192 && ord($c[0]) <= 223)
745
			return (ord($c[0]) - 192) * 64 + (ord($c[1]) - 128);
746
747
		if (ord($c[0]) >= 224 && ord($c[0]) <= 239)
748
			return (ord($c[0]) - 224) * 4096 + (ord($c[1]) - 128) * 64 + (ord($c[2]) - 128);
749
750
		if (ord($c[0]) >= 240 && ord($c[0]) <= 247)
751
			return (ord($c[0]) - 240) * 262144 + (ord($c[1]) - 128) * 4096 + (ord($c[2]) - 128) * 64 + (ord($c[3]) - 128);
752
753
		if (ord($c[0]) >= 248 && ord($c[0]) <= 251)
754
			return (ord($c[0]) - 248) * 16777216 + (ord($c[1]) - 128) * 262144 + (ord($c[2]) - 128) * 4096 + (ord($c[3]) - 128) * 64 + (ord($c[4]) - 128);
755
756
		if (ord($c[0]) >= 252 && ord($c[0]) <= 253)
757
			return (ord($c[0]) - 252) * 1073741824 + (ord($c[1]) - 128) * 16777216 + (ord($c[2]) - 128) * 262144 + (ord($c[3]) - 128) * 4096 + (ord($c[4]) - 128) * 64 + (ord($c[5]) - 128);
758
759
		if (ord($c[0]) >= 254 && ord($c[0]) <= 255)
760
			return false;
761
762
		return 0;
763
	}
764
}
765