$n_passed codepoints passed, " . count ($failures) . " failed.

\n"; if (!empty($failures)) { echo "\n"; echo "\n"; echo ''; echo ''; echo ''; echo "\n"; echo "\n"; echo "\n"; foreach ($failures as $failure) { echo ''; if ($intl_loaded) { echo ''; } foreach ($failure as $value) { echo ""; } echo '' . "\n"; echo "\n"; } echo "\n"; echo "
CodepointmbstringmrbsSummary
' . IntlChar::charName($failure[0]) . '$valueFail
\n"; } } function test_ord() : void { global $color_fail, $intl_loaded, $max_codepoint; $n_passed = 0; $failures = []; for ($i =0; $i<=$max_codepoint; $i++) { $str = mb_chr($i, 'UTF-8'); if (($str !== false) && mb_check_encoding($str, 'UTF-8')) { $mrbs_ord = Mbstring::mb_ord($str); if ($mrbs_ord === mb_ord($str)) { $n_passed++; } else { $failures[] = [$str, $i, $mrbs_ord]; } } } echo "

$n_passed codepoints passed, " . count ($failures) . " failed.

\n"; if (!empty($failures)) { echo "\n"; echo "\n"; echo ''; echo ''; echo ''; echo "\n"; echo "\n"; echo "\n"; foreach ($failures as $failure) { echo ''; if ($intl_loaded) { echo ''; } foreach ($failure as $value) { echo ""; } echo '' . "\n"; echo "\n"; } echo "\n"; echo "
CharmbstringmrbsSummary
' . IntlChar::charName(mb_ord($failure[0])) . '$valueFail
\n"; } } function codepoint_notation(int $codepoint) : string { // OK to user strtoupper here instead of mb_ because we're only looking at the hex characters return 'U+' . str_pad(strtoupper(dechex($codepoint)), 4, '0', STR_PAD_LEFT); } function test(string $function, $args) : void { global $color_fail, $color_pass; echo ""; echo "$function"; foreach ($args as $arg) { echo "$arg"; } // Using the mbstring versions echo ""; try { $mbstring = call_user_func_array($function, $args); } catch (Throwable $t) { $mbstring = get_class($t); } echo var_export($mbstring, true); echo ""; // Using the MRBS emulations echo ""; try { $mrbs = call_user_func_array([__NAMESPACE__ . "\\Mbstring\\Mbstring", $function], $args); } catch (Throwable $t) { $mrbs = get_class($t); } echo var_export($mrbs, true); echo ""; // Compare the results $color = ($mbstring === $mrbs) ? $color_pass : $color_fail; echo ''; echo ($mbstring === $mrbs) ? 'Pass' : 'Fail'; echo ""; echo "\n"; } function test_strlen() : void { echo "\n"; echo thead_html(['string', 'encoding']); echo "\n"; // Simple case test('mb_strlen', ['abcd', 'UTF-8']); // Multibyte test('mb_strlen', ['會議室預約系統', 'UTF-8']); test('mb_strlen', ['emojis 😀😨🙁', 'UTF-8']); // Empty string test('mb_strlen', ['', 'UTF-8']); // 8bit testing test('mb_strlen', ['', '8bit']); test('mb_strlen', ['&', '8bit']); test('mb_strlen', ['å', '8bit']); test('mb_strlen', ['議', '8bit']); test('mb_strlen', ['👽', '8bit']); test('mb_strlen', ['z👽', '8bit']); test('mb_strlen', ['åäö', '8bit']); test('mb_strlen', ['👽統', '8bit']); test('mb_strlen', ['👿🤩', '8bit']); test('mb_strlen', ['系統åg', '8bit']); echo "\n"; echo "
\n"; } function test_all_codepoints(string $function) : void { global $color_fail, $intl_loaded, $max_codepoint; echo "

Testing all codepoints

\n"; $n_passed = 0; $failures = []; for ($i =0; $i<=$max_codepoint; $i++) { $str = mb_chr($i, 'UTF-8'); if (($str !== false) && mb_check_encoding($str, 'UTF-8')) { $mb = call_user_func($function, $str); $mrbs = call_user_func([__NAMESPACE__ . "\\Mbstring\\Mbstring", $function], $str); if ($mb === $mrbs) { $n_passed++; } else { $failures[] = [$str, $mb, $mrbs]; } } } echo "

$n_passed codepoints passed, " . count ($failures) . " failed.

\n"; if (!empty($failures)) { echo "\n"; echo "\n"; echo ''; echo ''; echo ''; echo "\n"; echo "\n"; echo "\n"; foreach ($failures as $failure) { echo ''; if ($intl_loaded) { echo ''; } foreach ($failure as $char) { echo "'; } echo '' . "\n"; echo "\n"; } echo "\n"; echo "
CodepointmbstringmrbsSummary
' . IntlChar::charName(mb_ord($failure[0])) . '$char" . codepoint_notation(mb_ord($char)) . 'Fail
\n"; } } function test_strtolower() : void { echo "\n"; echo thead_html(['string']); echo "\n"; // Empty string test('mb_strtolower', ['']); // Simple string test('mb_strtolower', ['ABcDeFgHI']); // More complex test('mb_strtolower', ['AÅÄÖ']); // Turkish characters test('mb_strtolower', ['CÇGĞIİSŞ']); test('mb_strtolower', ['İ']); // Other test('mb_strtolower', ['Τάχιστη αλώπηξ βαφής']); test('mb_strtolower', ['👽系😨z😎éÉ']); echo "\n"; echo "
\n"; test_all_codepoints('mb_strtolower'); } function test_strtoupper() : void { echo "\n"; echo thead_html(['string']); echo "\n"; // Empty string test('mb_strtoupper', ['']); // Simple string test('mb_strtoupper', ['ABcDeFgHI']); // More complex test('mb_strtoupper', ['aåäö']); // Turkish characters test('mb_strtoupper', ['cçgğiiı̇sş']); // Other test('mb_strtoupper', ['Τάχιστη αλώπηξ βαφής']); test('mb_strtoupper', ['👽系😨z😎éÉ']); // These fail with Transliterator test('mb_strtoupper', ['ƛɤ']); echo "\n"; echo "
\n"; test_all_codepoints('mb_strtoupper'); } function test_substr() : void { echo "\n"; echo thead_html(['string', 'start', 'length']); echo "\n"; // Empty string test('mb_substr', ['', 0, null]); test('mb_substr', ['', 1, null]); test('mb_substr', ['', 0, 2]); test('mb_substr', ['', 1, 2]); // Multibyte test('mb_substr', ['👽系😨z😎é', 0, null]); test('mb_substr', ['👽系😨z😎é', 2, null]); test('mb_substr', ['👽系😨z😎é', 2, 1]); test('mb_substr', ['👽系😨z😎é', 2, -1]); test('mb_substr', ['👽系😨z😎é', 2, -5]); test('mb_substr', ['👽系😨z😎é', -1, null]); test('mb_substr', ['👽系😨z😎é', -3, -1]); test('mb_substr', ['👽系😨z😎é', -9, -1]); test('mb_substr', ['👽系😨z😎é', -9, -12]); echo "\n"; echo "
\n"; } function test_pos() : void { echo "\n"; echo thead_html(['haystack', 'needle', 'offset']); echo "\n"; // mb_strpos() // ----------- test('mb_strpos', ['0123456789a0123456789b0123456789c', 'c', 0]); test('mb_strpos', ['0123456789a0123456789b0123456789c', 'd', 0]); test('mb_strpos', ['0123456789a0123456789b0123456789c', 'c', -1]); test('mb_strpos', ['0123456789a0123456789b0123456789c', 'c', -2]); test('mb_strpos', ['TRUE', 'E_', 0]); // Equivalence test('mb_strpos', ['Jour précédent', 'e', 0]); $old_locale = setlocale(LC_ALL, '0'); setlocale(LC_ALL, ['fr_FR', 'fr']); test('mb_strpos', ['Jour précédent', 'e', 0]); setlocale(LC_ALL, $old_locale); // mb_stripos() // ----------- test('mb_stripos', ['0123456789a0123456789b0123456789c', 'c', -1]); test('mb_stripos', ['0123456789a0123456789b0123456789c', 'C', -1]); // Multibyte test('mb_stripos', ['會C議室預約系統', 'C', 2]); test('mb_stripos', ['會議C室預約系統', 'C', 2]); test('mb_stripos', ['會議C室預約系統', '預約', 2]); // mb_strrpos() // ------------ // Positive offsets, needle at start test('mb_strrpos', ['0123456789a0123456789b0123456789c', '0123456789a', 0]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '0123456789a', 1]); // Positive offsets, needle partial match at the end test('mb_strrpos', ['0123456789a0123456789b0123456789c', '89cd', 10]); // Positive offsets, needle longer than search area test('mb_strrpos', ['abcde', 'cde', 2]); test('mb_strrpos', ['abcde', 'cde', 3]); // Negative offsets, needle longer than search area test('mb_strrpos', ['abcdefg', 'cdefghi', -2]); test('mb_strrpos', ['abcdefg', 'cdefghij', -2]); // Negative offsets, needle in the middle of haystack test('mb_strrpos', ['0123456789a0123456789b0123456789c', '234', 0]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '234', -1]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '234', -2]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '234', -3]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '234', -9]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '234', -10]); // Negative offsets, needle at the end of haystack test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789c', 0]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789c', -1]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789c', -2]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789c', -3]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789c', -4]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789c', -5]); // Negative offsets, needle partial match at the end test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789cd', 0]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789cd', -1]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789cd', -2]); test('mb_strrpos', ['0123456789a0123456789b0123456789c', '789cd', -3]); // Multibyte test('mb_strrpos', ['會🙂議C室🙂預約系統', '🙂', -1]); // Empty haystack test('mb_strrpos', ['', 'A', 0]); // Case sensitivity test('mb_strrpos', ['AaBb', 'a', 0]); test('mb_strrpos', ['AaBb', 'A', 0]); // Offset outside haystack test('mb_strrpos', ['', 'A', 1]); test('mb_strrpos', ['A', 'A', 2]); test('mb_strrpos', ['A', 'A', -2]); // mb_strripos() // ------------ // Case sensitivity test('mb_strripos', ['AaBb', 'a', 0]); test('mb_strripos', ['AaBb', 'A', 0]); echo "\n"; echo "
\n"; } echo "

mbstring emulation tests

\n"; $loaded_extensions = get_loaded_extensions(); echo "PHP version: " . PHP_VERSION; echo "
\n"; echo "mbstring enabled: " . var_export(in_array('mbstring', $loaded_extensions), true); echo "
\n"; echo "intl enabled: " . var_export(in_array('intl', $loaded_extensions), true); echo "
\n"; echo "iconv enabled: " . var_export(in_array('iconv', $loaded_extensions), true); echo "
\n"; echo "
\n"; if (!in_array('mbstring', $loaded_extensions)) { die("This test needs the 'mbstring' PHP extension to be loaded."); } echo "

mb_chr()

\n"; test_chr(); echo "

mb_ord()

\n"; test_ord(); echo "

mb_strlen()

\n"; test_strlen(); echo "

mb_strtolower()

\n"; test_strtolower(); echo "

mb_strtoupper()

\n"; test_strtoupper(); echo "

mb_substr()

\n"; test_substr(); echo "

mb_*pos()

\n"; test_pos();