PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 3.19.1
GiveWP – Donation Plugin and Fundraising Platform v3.19.1
4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / vendor / symfony / polyfill-mbstring / Mbstring.php
give / vendor / symfony / polyfill-mbstring Last commit date
Resources 2 years ago LICENSE 2 years ago Mbstring.php 1 year ago bootstrap.php 1 year ago bootstrap80.php 1 year ago
Mbstring.php
1046 lines
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Polyfill\Mbstring;
13
14 /**
15 * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
16 *
17 * Implemented:
18 * - mb_chr - Returns a specific character from its Unicode code point
19 * - mb_convert_encoding - Convert character encoding
20 * - mb_convert_variables - Convert character code in variable(s)
21 * - mb_decode_mimeheader - Decode string in MIME header field
22 * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
23 * - mb_decode_numericentity - Decode HTML numeric string reference to character
24 * - mb_encode_numericentity - Encode character to HTML numeric string reference
25 * - mb_convert_case - Perform case folding on a string
26 * - mb_detect_encoding - Detect character encoding
27 * - mb_get_info - Get internal settings of mbstring
28 * - mb_http_input - Detect HTTP input character encoding
29 * - mb_http_output - Set/Get HTTP output character encoding
30 * - mb_internal_encoding - Set/Get internal character encoding
31 * - mb_list_encodings - Returns an array of all supported encodings
32 * - mb_ord - Returns the Unicode code point of a character
33 * - mb_output_handler - Callback function converts character encoding in output buffer
34 * - mb_scrub - Replaces ill-formed byte sequences with substitute characters
35 * - mb_strlen - Get string length
36 * - mb_strpos - Find position of first occurrence of string in a string
37 * - mb_strrpos - Find position of last occurrence of a string in a string
38 * - mb_str_split - Convert a string to an array
39 * - mb_strtolower - Make a string lowercase
40 * - mb_strtoupper - Make a string uppercase
41 * - mb_substitute_character - Set/Get substitution character
42 * - mb_substr - Get part of string
43 * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive
44 * - mb_stristr - Finds first occurrence of a string within another, case insensitive
45 * - mb_strrchr - Finds the last occurrence of a character in a string within another
46 * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive
47 * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive
48 * - mb_strstr - Finds first occurrence of a string within another
49 * - mb_strwidth - Return width of string
50 * - mb_substr_count - Count the number of substring occurrences
51 * - mb_ucfirst - Make a string's first character uppercase
52 * - mb_lcfirst - Make a string's first character lowercase
53 * - mb_trim - Strip whitespace (or other characters) from the beginning and end of a string
54 * - mb_ltrim - Strip whitespace (or other characters) from the beginning of a string
55 * - mb_rtrim - Strip whitespace (or other characters) from the end of a string
56 *
57 * Not implemented:
58 * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
59 * - mb_ereg_* - Regular expression with multibyte support
60 * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable
61 * - mb_preferred_mime_name - Get MIME charset string
62 * - mb_regex_encoding - Returns current encoding for multibyte regex as string
63 * - mb_regex_set_options - Set/Get the default options for mbregex functions
64 * - mb_send_mail - Send encoded mail
65 * - mb_split - Split multibyte string using regular expression
66 * - mb_strcut - Get part of string
67 * - mb_strimwidth - Get truncated string with specified width
68 *
69 * @author Nicolas Grekas <p@tchwork.com>
70 *
71 * @internal
72 */
73 final class Mbstring
74 {
75 public const MB_CASE_FOLD = \PHP_INT_MAX;
76
77 private const SIMPLE_CASE_FOLD = [
78 ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
79 ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'],
80 ];
81
82 private static $encodingList = ['ASCII', 'UTF-8'];
83 private static $language = 'neutral';
84 private static $internalEncoding = 'UTF-8';
85
86 public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
87 {
88 if (\is_array($s)) {
89 $r = [];
90 foreach ($s as $str) {
91 $r[] = self::mb_convert_encoding($str, $toEncoding, $fromEncoding);
92 }
93
94 return $r;
95 }
96
97 if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) {
98 $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
99 } else {
100 $fromEncoding = self::getEncoding($fromEncoding);
101 }
102
103 $toEncoding = self::getEncoding($toEncoding);
104
105 if ('BASE64' === $fromEncoding) {
106 $s = base64_decode($s);
107 $fromEncoding = $toEncoding;
108 }
109
110 if ('BASE64' === $toEncoding) {
111 return base64_encode($s);
112 }
113
114 if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
115 if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
116 $fromEncoding = 'Windows-1252';
117 }
118 if ('UTF-8' !== $fromEncoding) {
119 $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
120 }
121
122 return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s);
123 }
124
125 if ('HTML-ENTITIES' === $fromEncoding) {
126 $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8');
127 $fromEncoding = 'UTF-8';
128 }
129
130 return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
131 }
132
133 public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
134 {
135 $ok = true;
136 array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
137 if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
138 $ok = false;
139 }
140 });
141
142 return $ok ? $fromEncoding : false;
143 }
144
145 public static function mb_decode_mimeheader($s)
146 {
147 return iconv_mime_decode($s, 2, self::$internalEncoding);
148 }
149
150 public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
151 {
152 trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING);
153 }
154
155 public static function mb_decode_numericentity($s, $convmap, $encoding = null)
156 {
157 if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
158 trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
159
160 return null;
161 }
162
163 if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
164 return false;
165 }
166
167 if (null !== $encoding && !\is_scalar($encoding)) {
168 trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
169
170 return ''; // Instead of null (cf. mb_encode_numericentity).
171 }
172
173 $s = (string) $s;
174 if ('' === $s) {
175 return '';
176 }
177
178 $encoding = self::getEncoding($encoding);
179
180 if ('UTF-8' === $encoding) {
181 $encoding = null;
182 if (!preg_match('//u', $s)) {
183 $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
184 }
185 } else {
186 $s = iconv($encoding, 'UTF-8//IGNORE', $s);
187 }
188
189 $cnt = floor(\count($convmap) / 4) * 4;
190
191 for ($i = 0; $i < $cnt; $i += 4) {
192 // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
193 $convmap[$i] += $convmap[$i + 2];
194 $convmap[$i + 1] += $convmap[$i + 2];
195 }
196
197 $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
198 $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
199 for ($i = 0; $i < $cnt; $i += 4) {
200 if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
201 return self::mb_chr($c - $convmap[$i + 2]);
202 }
203 }
204
205 return $m[0];
206 }, $s);
207
208 if (null === $encoding) {
209 return $s;
210 }
211
212 return iconv('UTF-8', $encoding.'//IGNORE', $s);
213 }
214
215 public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
216 {
217 if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
218 trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
219
220 return null;
221 }
222
223 if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
224 return false;
225 }
226
227 if (null !== $encoding && !\is_scalar($encoding)) {
228 trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
229
230 return null; // Instead of '' (cf. mb_decode_numericentity).
231 }
232
233 if (null !== $is_hex && !\is_scalar($is_hex)) {
234 trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING);
235
236 return null;
237 }
238
239 $s = (string) $s;
240 if ('' === $s) {
241 return '';
242 }
243
244 $encoding = self::getEncoding($encoding);
245
246 if ('UTF-8' === $encoding) {
247 $encoding = null;
248 if (!preg_match('//u', $s)) {
249 $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
250 }
251 } else {
252 $s = iconv($encoding, 'UTF-8//IGNORE', $s);
253 }
254
255 static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
256
257 $cnt = floor(\count($convmap) / 4) * 4;
258 $i = 0;
259 $len = \strlen($s);
260 $result = '';
261
262 while ($i < $len) {
263 $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
264 $uchr = substr($s, $i, $ulen);
265 $i += $ulen;
266 $c = self::mb_ord($uchr);
267
268 for ($j = 0; $j < $cnt; $j += 4) {
269 if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
270 $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
271 $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
272 continue 2;
273 }
274 }
275 $result .= $uchr;
276 }
277
278 if (null === $encoding) {
279 return $result;
280 }
281
282 return iconv('UTF-8', $encoding.'//IGNORE', $result);
283 }
284
285 public static function mb_convert_case($s, $mode, $encoding = null)
286 {
287 $s = (string) $s;
288 if ('' === $s) {
289 return '';
290 }
291
292 $encoding = self::getEncoding($encoding);
293
294 if ('UTF-8' === $encoding) {
295 $encoding = null;
296 if (!preg_match('//u', $s)) {
297 $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
298 }
299 } else {
300 $s = iconv($encoding, 'UTF-8//IGNORE', $s);
301 }
302
303 if (\MB_CASE_TITLE == $mode) {
304 static $titleRegexp = null;
305 if (null === $titleRegexp) {
306 $titleRegexp = self::getData('titleCaseRegexp');
307 }
308 $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s);
309 } else {
310 if (\MB_CASE_UPPER == $mode) {
311 static $upper = null;
312 if (null === $upper) {
313 $upper = self::getData('upperCase');
314 }
315 $map = $upper;
316 } else {
317 if (self::MB_CASE_FOLD === $mode) {
318 static $caseFolding = null;
319 if (null === $caseFolding) {
320 $caseFolding = self::getData('caseFolding');
321 }
322 $s = strtr($s, $caseFolding);
323 }
324
325 static $lower = null;
326 if (null === $lower) {
327 $lower = self::getData('lowerCase');
328 }
329 $map = $lower;
330 }
331
332 static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
333
334 $i = 0;
335 $len = \strlen($s);
336
337 while ($i < $len) {
338 $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
339 $uchr = substr($s, $i, $ulen);
340 $i += $ulen;
341
342 if (isset($map[$uchr])) {
343 $uchr = $map[$uchr];
344 $nlen = \strlen($uchr);
345
346 if ($nlen == $ulen) {
347 $nlen = $i;
348 do {
349 $s[--$nlen] = $uchr[--$ulen];
350 } while ($ulen);
351 } else {
352 $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
353 $len += $nlen - $ulen;
354 $i += $nlen - $ulen;
355 }
356 }
357 }
358 }
359
360 if (null === $encoding) {
361 return $s;
362 }
363
364 return iconv('UTF-8', $encoding.'//IGNORE', $s);
365 }
366
367 public static function mb_internal_encoding($encoding = null)
368 {
369 if (null === $encoding) {
370 return self::$internalEncoding;
371 }
372
373 $normalizedEncoding = self::getEncoding($encoding);
374
375 if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) {
376 self::$internalEncoding = $normalizedEncoding;
377
378 return true;
379 }
380
381 if (80000 > \PHP_VERSION_ID) {
382 return false;
383 }
384
385 throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding));
386 }
387
388 public static function mb_language($lang = null)
389 {
390 if (null === $lang) {
391 return self::$language;
392 }
393
394 switch ($normalizedLang = strtolower($lang)) {
395 case 'uni':
396 case 'neutral':
397 self::$language = $normalizedLang;
398
399 return true;
400 }
401
402 if (80000 > \PHP_VERSION_ID) {
403 return false;
404 }
405
406 throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang));
407 }
408
409 public static function mb_list_encodings()
410 {
411 return ['UTF-8'];
412 }
413
414 public static function mb_encoding_aliases($encoding)
415 {
416 switch (strtoupper($encoding)) {
417 case 'UTF8':
418 case 'UTF-8':
419 return ['utf8'];
420 }
421
422 return false;
423 }
424
425 public static function mb_check_encoding($var = null, $encoding = null)
426 {
427 if (null === $encoding) {
428 if (null === $var) {
429 return false;
430 }
431 $encoding = self::$internalEncoding;
432 }
433
434 if (!\is_array($var)) {
435 return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var);
436 }
437
438 foreach ($var as $key => $value) {
439 if (!self::mb_check_encoding($key, $encoding)) {
440 return false;
441 }
442 if (!self::mb_check_encoding($value, $encoding)) {
443 return false;
444 }
445 }
446
447 return true;
448 }
449
450 public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
451 {
452 if (null === $encodingList) {
453 $encodingList = self::$encodingList;
454 } else {
455 if (!\is_array($encodingList)) {
456 $encodingList = array_map('trim', explode(',', $encodingList));
457 }
458 $encodingList = array_map('strtoupper', $encodingList);
459 }
460
461 foreach ($encodingList as $enc) {
462 switch ($enc) {
463 case 'ASCII':
464 if (!preg_match('/[\x80-\xFF]/', $str)) {
465 return $enc;
466 }
467 break;
468
469 case 'UTF8':
470 case 'UTF-8':
471 if (preg_match('//u', $str)) {
472 return 'UTF-8';
473 }
474 break;
475
476 default:
477 if (0 === strncmp($enc, 'ISO-8859-', 9)) {
478 return $enc;
479 }
480 }
481 }
482
483 return false;
484 }
485
486 public static function mb_detect_order($encodingList = null)
487 {
488 if (null === $encodingList) {
489 return self::$encodingList;
490 }
491
492 if (!\is_array($encodingList)) {
493 $encodingList = array_map('trim', explode(',', $encodingList));
494 }
495 $encodingList = array_map('strtoupper', $encodingList);
496
497 foreach ($encodingList as $enc) {
498 switch ($enc) {
499 default:
500 if (strncmp($enc, 'ISO-8859-', 9)) {
501 return false;
502 }
503 // no break
504 case 'ASCII':
505 case 'UTF8':
506 case 'UTF-8':
507 }
508 }
509
510 self::$encodingList = $encodingList;
511
512 return true;
513 }
514
515 public static function mb_strlen($s, $encoding = null)
516 {
517 $encoding = self::getEncoding($encoding);
518 if ('CP850' === $encoding || 'ASCII' === $encoding) {
519 return \strlen($s);
520 }
521
522 return @iconv_strlen($s, $encoding);
523 }
524
525 public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
526 {
527 $encoding = self::getEncoding($encoding);
528 if ('CP850' === $encoding || 'ASCII' === $encoding) {
529 return strpos($haystack, $needle, $offset);
530 }
531
532 $needle = (string) $needle;
533 if ('' === $needle) {
534 if (80000 > \PHP_VERSION_ID) {
535 trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING);
536
537 return false;
538 }
539
540 return 0;
541 }
542
543 return iconv_strpos($haystack, $needle, $offset, $encoding);
544 }
545
546 public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
547 {
548 $encoding = self::getEncoding($encoding);
549 if ('CP850' === $encoding || 'ASCII' === $encoding) {
550 return strrpos($haystack, $needle, $offset);
551 }
552
553 if ($offset != (int) $offset) {
554 $offset = 0;
555 } elseif ($offset = (int) $offset) {
556 if ($offset < 0) {
557 if (0 > $offset += self::mb_strlen($needle)) {
558 $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
559 }
560 $offset = 0;
561 } else {
562 $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
563 }
564 }
565
566 $pos = '' !== $needle || 80000 > \PHP_VERSION_ID
567 ? iconv_strrpos($haystack, $needle, $encoding)
568 : self::mb_strlen($haystack, $encoding);
569
570 return false !== $pos ? $offset + $pos : false;
571 }
572
573 public static function mb_str_split($string, $split_length = 1, $encoding = null)
574 {
575 if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) {
576 trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING);
577
578 return null;
579 }
580
581 if (1 > $split_length = (int) $split_length) {
582 if (80000 > \PHP_VERSION_ID) {
583 trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING);
584
585 return false;
586 }
587
588 throw new \ValueError('Argument #2 ($length) must be greater than 0');
589 }
590
591 if (null === $encoding) {
592 $encoding = mb_internal_encoding();
593 }
594
595 if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
596 $rx = '/(';
597 while (65535 < $split_length) {
598 $rx .= '.{65535}';
599 $split_length -= 65535;
600 }
601 $rx .= '.{'.$split_length.'})/us';
602
603 return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY);
604 }
605
606 $result = [];
607 $length = mb_strlen($string, $encoding);
608
609 for ($i = 0; $i < $length; $i += $split_length) {
610 $result[] = mb_substr($string, $i, $split_length, $encoding);
611 }
612
613 return $result;
614 }
615
616 public static function mb_strtolower($s, $encoding = null)
617 {
618 return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding);
619 }
620
621 public static function mb_strtoupper($s, $encoding = null)
622 {
623 return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding);
624 }
625
626 public static function mb_substitute_character($c = null)
627 {
628 if (null === $c) {
629 return 'none';
630 }
631 if (0 === strcasecmp($c, 'none')) {
632 return true;
633 }
634 if (80000 > \PHP_VERSION_ID) {
635 return false;
636 }
637 if (\is_int($c) || 'long' === $c || 'entity' === $c) {
638 return false;
639 }
640
641 throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint');
642 }
643
644 public static function mb_substr($s, $start, $length = null, $encoding = null)
645 {
646 $encoding = self::getEncoding($encoding);
647 if ('CP850' === $encoding || 'ASCII' === $encoding) {
648 return (string) substr($s, $start, null === $length ? 2147483647 : $length);
649 }
650
651 if ($start < 0) {
652 $start = iconv_strlen($s, $encoding) + $start;
653 if ($start < 0) {
654 $start = 0;
655 }
656 }
657
658 if (null === $length) {
659 $length = 2147483647;
660 } elseif ($length < 0) {
661 $length = iconv_strlen($s, $encoding) + $length - $start;
662 if ($length < 0) {
663 return '';
664 }
665 }
666
667 return (string) iconv_substr($s, $start, $length, $encoding);
668 }
669
670 public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
671 {
672 [$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [
673 self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding),
674 self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding),
675 ]);
676
677 return self::mb_strpos($haystack, $needle, $offset, $encoding);
678 }
679
680 public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
681 {
682 $pos = self::mb_stripos($haystack, $needle, 0, $encoding);
683
684 return self::getSubpart($pos, $part, $haystack, $encoding);
685 }
686
687 public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
688 {
689 $encoding = self::getEncoding($encoding);
690 if ('CP850' === $encoding || 'ASCII' === $encoding) {
691 $pos = strrpos($haystack, $needle);
692 } else {
693 $needle = self::mb_substr($needle, 0, 1, $encoding);
694 $pos = iconv_strrpos($haystack, $needle, $encoding);
695 }
696
697 return self::getSubpart($pos, $part, $haystack, $encoding);
698 }
699
700 public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
701 {
702 $needle = self::mb_substr($needle, 0, 1, $encoding);
703 $pos = self::mb_strripos($haystack, $needle, $encoding);
704
705 return self::getSubpart($pos, $part, $haystack, $encoding);
706 }
707
708 public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
709 {
710 $haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding);
711 $needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding);
712
713 $haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack);
714 $needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle);
715
716 return self::mb_strrpos($haystack, $needle, $offset, $encoding);
717 }
718
719 public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
720 {
721 $pos = strpos($haystack, $needle);
722 if (false === $pos) {
723 return false;
724 }
725 if ($part) {
726 return substr($haystack, 0, $pos);
727 }
728
729 return substr($haystack, $pos);
730 }
731
732 public static function mb_get_info($type = 'all')
733 {
734 $info = [
735 'internal_encoding' => self::$internalEncoding,
736 'http_output' => 'pass',
737 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
738 'func_overload' => 0,
739 'func_overload_list' => 'no overload',
740 'mail_charset' => 'UTF-8',
741 'mail_header_encoding' => 'BASE64',
742 'mail_body_encoding' => 'BASE64',
743 'illegal_chars' => 0,
744 'encoding_translation' => 'Off',
745 'language' => self::$language,
746 'detect_order' => self::$encodingList,
747 'substitute_character' => 'none',
748 'strict_detection' => 'Off',
749 ];
750
751 if ('all' === $type) {
752 return $info;
753 }
754 if (isset($info[$type])) {
755 return $info[$type];
756 }
757
758 return false;
759 }
760
761 public static function mb_http_input($type = '')
762 {
763 return false;
764 }
765
766 public static function mb_http_output($encoding = null)
767 {
768 return null !== $encoding ? 'pass' === $encoding : 'pass';
769 }
770
771 public static function mb_strwidth($s, $encoding = null)
772 {
773 $encoding = self::getEncoding($encoding);
774
775 if ('UTF-8' !== $encoding) {
776 $s = iconv($encoding, 'UTF-8//IGNORE', $s);
777 }
778
779 $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
780
781 return ($wide << 1) + iconv_strlen($s, 'UTF-8');
782 }
783
784 public static function mb_substr_count($haystack, $needle, $encoding = null)
785 {
786 return substr_count($haystack, $needle);
787 }
788
789 public static function mb_output_handler($contents, $status)
790 {
791 return $contents;
792 }
793
794 public static function mb_chr($code, $encoding = null)
795 {
796 if (0x80 > $code %= 0x200000) {
797 $s = \chr($code);
798 } elseif (0x800 > $code) {
799 $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
800 } elseif (0x10000 > $code) {
801 $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
802 } else {
803 $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
804 }
805
806 if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
807 $s = mb_convert_encoding($s, $encoding, 'UTF-8');
808 }
809
810 return $s;
811 }
812
813 public static function mb_ord($s, $encoding = null)
814 {
815 if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
816 $s = mb_convert_encoding($s, 'UTF-8', $encoding);
817 }
818
819 if (1 === \strlen($s)) {
820 return \ord($s);
821 }
822
823 $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
824 if (0xF0 <= $code) {
825 return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
826 }
827 if (0xE0 <= $code) {
828 return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
829 }
830 if (0xC0 <= $code) {
831 return (($code - 0xC0) << 6) + $s[2] - 0x80;
832 }
833
834 return $code;
835 }
836
837 public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string
838 {
839 if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
840 throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
841 }
842
843 if (null === $encoding) {
844 $encoding = self::mb_internal_encoding();
845 } else {
846 self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given');
847 }
848
849 if (self::mb_strlen($pad_string, $encoding) <= 0) {
850 throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
851 }
852
853 $paddingRequired = $length - self::mb_strlen($string, $encoding);
854
855 if ($paddingRequired < 1) {
856 return $string;
857 }
858
859 switch ($pad_type) {
860 case \STR_PAD_LEFT:
861 return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string;
862 case \STR_PAD_RIGHT:
863 return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding);
864 default:
865 $leftPaddingLength = floor($paddingRequired / 2);
866 $rightPaddingLength = $paddingRequired - $leftPaddingLength;
867
868 return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding);
869 }
870 }
871
872 public static function mb_ucfirst(string $string, ?string $encoding = null): string
873 {
874 if (null === $encoding) {
875 $encoding = self::mb_internal_encoding();
876 } else {
877 self::assertEncoding($encoding, 'mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given');
878 }
879
880 $firstChar = mb_substr($string, 0, 1, $encoding);
881 $firstChar = mb_convert_case($firstChar, \MB_CASE_TITLE, $encoding);
882
883 return $firstChar.mb_substr($string, 1, null, $encoding);
884 }
885
886 public static function mb_lcfirst(string $string, ?string $encoding = null): string
887 {
888 if (null === $encoding) {
889 $encoding = self::mb_internal_encoding();
890 } else {
891 self::assertEncoding($encoding, 'mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given');
892 }
893
894 $firstChar = mb_substr($string, 0, 1, $encoding);
895 $firstChar = mb_convert_case($firstChar, \MB_CASE_LOWER, $encoding);
896
897 return $firstChar.mb_substr($string, 1, null, $encoding);
898 }
899
900 private static function getSubpart($pos, $part, $haystack, $encoding)
901 {
902 if (false === $pos) {
903 return false;
904 }
905 if ($part) {
906 return self::mb_substr($haystack, 0, $pos, $encoding);
907 }
908
909 return self::mb_substr($haystack, $pos, null, $encoding);
910 }
911
912 private static function html_encoding_callback(array $m)
913 {
914 $i = 1;
915 $entities = '';
916 $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8'));
917
918 while (isset($m[$i])) {
919 if (0x80 > $m[$i]) {
920 $entities .= \chr($m[$i++]);
921 continue;
922 }
923 if (0xF0 <= $m[$i]) {
924 $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
925 } elseif (0xE0 <= $m[$i]) {
926 $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
927 } else {
928 $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
929 }
930
931 $entities .= '&#'.$c.';';
932 }
933
934 return $entities;
935 }
936
937 private static function title_case(array $s)
938 {
939 return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8');
940 }
941
942 private static function getData($file)
943 {
944 if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
945 return require $file;
946 }
947
948 return false;
949 }
950
951 private static function getEncoding($encoding)
952 {
953 if (null === $encoding) {
954 return self::$internalEncoding;
955 }
956
957 if ('UTF-8' === $encoding) {
958 return 'UTF-8';
959 }
960
961 $encoding = strtoupper($encoding);
962
963 if ('8BIT' === $encoding || 'BINARY' === $encoding) {
964 return 'CP850';
965 }
966
967 if ('UTF8' === $encoding) {
968 return 'UTF-8';
969 }
970
971 return $encoding;
972 }
973
974 public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string
975 {
976 return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
977 }
978
979 public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string
980 {
981 return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__);
982 }
983
984 public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string
985 {
986 return self::mb_internal_trim('{[%s]+$}D', $string, $characters, $encoding, __FUNCTION__);
987 }
988
989 private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function): string
990 {
991 if (null === $encoding) {
992 $encoding = self::mb_internal_encoding();
993 } else {
994 self::assertEncoding($encoding, $function.'(): Argument #3 ($encoding) must be a valid encoding, "%s" given');
995 }
996
997 if ('' === $characters) {
998 return null === $encoding ? $string : self::mb_convert_encoding($string, $encoding);
999 }
1000
1001 if ('UTF-8' === $encoding) {
1002 $encoding = null;
1003 if (!preg_match('//u', $string)) {
1004 $string = @iconv('UTF-8', 'UTF-8//IGNORE', $string);
1005 }
1006 if (null !== $characters && !preg_match('//u', $characters)) {
1007 $characters = @iconv('UTF-8', 'UTF-8//IGNORE', $characters);
1008 }
1009 } else {
1010 $string = iconv($encoding, 'UTF-8//IGNORE', $string);
1011
1012 if (null !== $characters) {
1013 $characters = iconv($encoding, 'UTF-8//IGNORE', $characters);
1014 }
1015 }
1016
1017 if (null === $characters) {
1018 $characters = "\\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}";
1019 } else {
1020 $characters = preg_quote($characters);
1021 }
1022
1023 $string = preg_replace(sprintf($regex, $characters), '', $string);
1024
1025 if (null === $encoding) {
1026 return $string;
1027 }
1028
1029 return iconv('UTF-8', $encoding.'//IGNORE', $string);
1030 }
1031
1032 private static function assertEncoding(string $encoding, string $errorFormat): void
1033 {
1034 try {
1035 $validEncoding = @self::mb_check_encoding('', $encoding);
1036 } catch (\ValueError $e) {
1037 throw new \ValueError(sprintf($errorFormat, $encoding));
1038 }
1039
1040 // BC for PHP 7.3 and lower
1041 if (!$validEncoding) {
1042 throw new \ValueError(sprintf($errorFormat, $encoding));
1043 }
1044 }
1045 }
1046