Passed
Push — master ( a352f1...d20992 )
by Spuds
11:34 queued 05:42
created

Util::gmstrftime()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 3
nc 2
nop 2
dl 0
loc 6
ccs 0
cts 0
cp 0
crap 12
rs 10
c 0
b 0
f 0
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.7
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($modSettings['disableEntityCheck']))
433
		{
434
			$ent_list = '&(#\d{1,7}|quot|amp|lt|gt|nbsp);';
435
			if (function_exists('mb_strlen'))
436
				return mb_strlen(preg_replace('~' . $ent_list . '|.~u', '_', $string), 'UTF-8');
437
			else
438
				return strlen(preg_replace('~' . $ent_list . '|.~u', '_', preg_replace_callback(self::$_entity_check_reg, 'entity_fix__callback', $string)));
439
		}
440
		else
441
		{
442
			$ent_list = '&(#021|quot|amp|lt|gt|nbsp);';
443
			return strlen(preg_replace('~' . $ent_list . '|.~u', '_', $string));
444
		}
445
	}
446
447
	/**
448
	 * Remove slashes recursively.
449
	 *
450
	 * What it does:
451
	 *
452
	 * - removes slashes, recursively, from the array or string var.
453
	 * - effects both keys and values of arrays.
454
	 * - calls itself recursively to handle arrays of arrays.
455
	 *
456
	 * @todo not used, consider removing
457
	 * @deprecated since 1.0
458
	 *
459
	 * @param mixed[]|string $var
460
	 * @param int $level = 0
461
	 * @return array|string
462
	 */
463
	public static function stripslashes_recursive($var, $level = 0)
464
	{
465
		if (!is_array($var))
466
			return stripslashes($var);
467
468
		// Reindex the array without slashes, this time.
469
		$new_var = array();
470
471
		// Strip the slashes from every element.
472
		foreach ($var as $k => $v)
473
			$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

473
			$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...
474
475
		return $new_var;
476
	}
477
478
	/**
479
	 * Removes url stuff from the array/variable.
480
	 *
481
	 * What it does:
482
	 *
483
	 * - takes off url encoding (%20, etc.) from the array or string var.
484
	 * - importantly, does it to keys too!
485
	 * - calls itself recursively if there are any sub arrays.
486
	 *
487
	 * @todo not used, consider removing
488
	 * @deprecated since 1.0
489
	 *
490
	 * @param mixed[]|string $var
491
	 * @param int $level = 0
492
	 * @return array|string
493
	 */
494
	public static function urldecode_recursive($var, $level = 0)
495
	{
496
		if (!is_array($var))
497
			return urldecode($var);
498
499
		// Reindex the array...
500
		$new_var = array();
501
502
		// Add the htmlspecialchars to every element.
503
		foreach ($var as $k => $v)
504
			$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

504
			$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...
505
506
		return $new_var;
507
	}
508
509
	/**
510
	 * Unescapes any array or variable.
511
	 *
512
	 * What it does:
513
	 *
514
	 * - unescapes, recursively, from the array or string var.
515
	 * - effects both keys and values of arrays.
516
	 * - calls itself recursively to handle arrays of arrays.
517
	 *
518
	 * @todo not used, consider removing
519
	 * @deprecated since 1.0
520
	 *
521
	 * @param mixed[]|string $var
522
	 * @return array|string
523
	 */
524
	public static function unescapestring_recursive($var)
525
	{
526
		$db = database();
527
528
		if (!is_array($var))
529
		return $db->unescape_string($var);
530
531
		// Reindex the array without slashes, this time.
532
		$new_var = array();
533
534
		// Strip the slashes from every element.
535
		foreach ($var as $k => $v)
536
			$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

536
			$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...
537
538
		return $new_var;
539
	}
540
541
	/**
542
	 * Wrappers for unserialize
543
	 * What it does:
544
	 *
545
	 * - if using PHP < 7 it will use ext/safe_unserialize
546
	 * - if using PHP > 7 will use the built in unserialize
547
	 *
548
	 * @param string $string The string to unserialize
549
	 * @param string[] $options Optional, mimic the PHP 7+ option,
550
	 *                          see PHP documentation for the details
551 4
	 *                          additionally, it doesn't allow to use the option:
552
	 *                            allowed_classes => true
553 4
	 *                          that is reverted to false.
554
	 * @return mixed
555 4
	 */
556 4
	public static function unserialize($string, $options = array())
557
	{
558
		static $function = null;
559
560
		if ($function === null)
561
		{
562
			if (version_compare(PHP_VERSION, '7', '>='))
563
			{
564
				$function = 'unserialize';
565
			}
566
			else
567
			{
568 4
				require_once(EXTDIR . '/serialize.php');
569 4
				$function = 'ElkArte\\ext\\upgradephp\\safe_unserialize';
570 4
			}
571 4
		}
572
573 4
		if (!isset($options['allowed_classes']) || $options['allowed_classes'] === true)
574
		{
575
			$options['allowed_classes'] = false;
576
		}
577
578
		return @$function($string, $options);
579
	}
580
}
581